diff --git a/.cursor/worktrees.json b/.cursor/worktrees.json new file mode 100644 index 0000000000..50c437360c --- /dev/null +++ b/.cursor/worktrees.json @@ -0,0 +1,3 @@ +{ + "setup-worktree": ["yarn install"] +} diff --git a/.eslintignore b/.eslintignore index 4c230c7b30..2a8650f397 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,7 @@ /node_modules /app /workspaces/* +# Standalone tooling scripts (not part of main app, run with ts-node) +/scripts +# Patch source files (copied to node_modules, not compiled directly) +/patches-src diff --git a/.eslintrc.json b/.eslintrc.json index 11bbeaface..44310f5127 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,5 +1,6 @@ { "extends": ["@rocket.chat/eslint-config"], + "ignorePatterns": ["patches-src/**/*"], "plugins": ["react", "react-hooks"], "env": { "browser": true, @@ -14,7 +15,14 @@ "react/display-name": "error", "react/jsx-curly-brace-presence": "error", "react/jsx-fragments": ["error", "syntax"], - "react/jsx-key": ["error", { "checkFragmentShorthand": true, "checkKeyMustBeforeSpread": true, "warnOnDuplicates": true }], + "react/jsx-key": [ + "error", + { + "checkFragmentShorthand": true, + "checkKeyMustBeforeSpread": true, + "warnOnDuplicates": true + } + ], "react/jsx-no-undef": "error", "react/jsx-uses-react": "error", "react/jsx-uses-vars": "error", @@ -43,7 +51,13 @@ } }, { - "files": ["**/*.stories.js", "**/*.stories.jsx", "**/*.stories.ts", "**/*.stories.tsx", "**/*.spec.tsx"], + "files": [ + "**/*.stories.js", + "**/*.stories.jsx", + "**/*.stories.ts", + "**/*.stories.tsx", + "**/*.spec.tsx" + ], "rules": { "react/display-name": "off", "react/no-multi-comp": "off" diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 71100be550..5fe7cb7eec 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -7,7 +7,7 @@ packages, which are hosted in the [Rocket.Chat Organization] on GitHub. [Rocket.Chat Organization]: https://github.com/RocketChat -__Note:__ If there's a feature you'd like, there's a bug you'd like to fix, or +**Note:** If there's a feature you'd like, there's a bug you'd like to fix, or you'd just like to get involved please raise an issue and start a conversation. We'll help as much as we can so you can get contributing - although we may not always be able to respond right away :) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index d01ec5de65..736faae3a6 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -16,14 +16,18 @@ Thanks for opening an issue! A few things to keep in mind: - Installation type: + - [ ] I have tested with the latest version - [ ] I can simulate the issue easily ## Description + ### Current Behavior + ### Expected Behavior + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d61..2f28cead03 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -4,7 +4,6 @@ about: Suggest an idea for this project title: '' labels: '' assignees: '' - --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1518f72892..9af4dbe59f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,6 +7,7 @@ prefixes: --> + Closes #ISSUE_NUMBER diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 2eaa7850e5..64ed5e1bdb 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -1,15 +1,16 @@ +permissions: + contents: read name: Build release on: push: branches: - master - - develop + - dev tags: - '*' - concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -31,10 +32,10 @@ jobs: with: fetch-depth: 0 - - name: Setup Node 22.13.1 + - name: Setup Node 22.17.1 uses: actions/setup-node@v4 with: - node-version: '22.13.1' + node-version: '22.17.1' - name: Setup node_modules cache uses: actions/cache@v4 @@ -63,6 +64,19 @@ jobs: NODE_ENV: production BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }} SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} + + - name: Setup gcloud (Windows) + if: ${{ matrix.os == 'windows-latest' }} + uses: google-github-actions/setup-gcloud@v2 + with: + version: '>=536.0.0' + + - name: Authenticate to Google Cloud (Windows) + if: ${{ matrix.os == 'windows-latest' }} + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GCP_SA_JSON }} + - uses: ./workspaces/desktop-release-action with: mac_csc_link: ${{ secrets.MAC_CSC_LINK }} @@ -70,8 +84,12 @@ jobs: mac_apple_id: ${{ secrets.APPLEID }} mac_apple_id_password: ${{ secrets.APPLEIDPASS }} mac_asc_provider: 'S6UPZG7ZR3' - win_csc_link: ${{ secrets.WIN_CSC_LINK }} - win_csc_key_password: ${{ secrets.WIN_CSC_KEY_PASSWORD }} + gcp_sa_json: ${{ secrets.GCP_SA_JSON }} + win_kms_key_resource: ${{ secrets.WIN_KMS_KEY_RESOURCE }} + win_user_crt: ${{ secrets.WIN_USER_CRT }} + win_intermediate_crt: ${{ secrets.WIN_INTERMEDIATE_CRT }} + win_root_crt: ${{ secrets.WIN_ROOT_CRT }} github_token: ${{ secrets.GH_TOKEN }} + snapcraft_token: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} diff --git a/.github/workflows/powershell-lint.yml b/.github/workflows/powershell-lint.yml new file mode 100644 index 0000000000..626aa33153 --- /dev/null +++ b/.github/workflows/powershell-lint.yml @@ -0,0 +1,48 @@ +name: PowerShell Lint + +permissions: + contents: read + +on: + pull_request: + paths: + - '**.ps1' + - '**.psm1' + - '**.psd1' + - '.github/workflows/powershell-lint.yml' + push: + branches: + - master + - dev + paths: + - '**.ps1' + - '**.psm1' + - '**.psd1' + - '.github/workflows/powershell-lint.yml' + +jobs: + lint: + runs-on: windows-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install PSScriptAnalyzer + shell: pwsh + run: | + Set-PSRepository PSGallery -InstallationPolicy Trusted + Install-Module -Name PSScriptAnalyzer -Force -Scope CurrentUser + + - name: Run PSScriptAnalyzer + shell: pwsh + run: | + # Exclude Write-Host warnings as they're useful for CI output + $results = Invoke-ScriptAnalyzer -Path . -Recurse -ReportSummary -ExcludeRule PSAvoidUsingWriteHost + if ($results) { + $results | Format-Table -AutoSize + Write-Error "PSScriptAnalyzer found issues" + exit 1 + } else { + Write-Host "✅ No PowerShell issues found" + } diff --git a/.github/workflows/pull-request-build.yml b/.github/workflows/pull-request-build.yml index b945550e0c..a9074fc9b2 100644 --- a/.github/workflows/pull-request-build.yml +++ b/.github/workflows/pull-request-build.yml @@ -1,10 +1,14 @@ name: Build pull request artifacts +permissions: + contents: read + pull-requests: write + on: pull_request: branches: - master - - develop + - dev concurrency: group: ${{ github.workflow }}-${{ github.head_ref }} @@ -16,10 +20,16 @@ jobs: strategy: fail-fast: false matrix: - os: - - ubuntu-latest - - macos-latest - - windows-latest + include: + # Regular Linux build for Linux packages + - os: ubuntu-latest + build-target: linux + # macOS build + - os: macos-latest + build-target: mac + # Windows native build + - os: windows-latest + build-target: windows runs-on: ${{ matrix.os }} steps: - name: Disable git core.autocrlf @@ -30,10 +40,10 @@ jobs: with: fetch-depth: 0 - - name: Setup Node 22.13.1 + - name: Setup Node 22.17.1 uses: actions/setup-node@v4 with: - node-version: '22.13.1' + node-version: '22.17.1' - name: Setup node_modules cache uses: actions/cache@v4 @@ -57,15 +67,464 @@ jobs: BUGSNAG_API_KEY: ${{ secrets.BUGSNAG_API_KEY }} SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} - - name: Build Windows Package + - name: Setup Google Cloud authentication (Windows) + if: ${{ matrix.os == 'windows-latest' }} + shell: pwsh + run: | + $tempDir = $env:RUNNER_TEMP + '${{ secrets.GCP_SA_JSON }}' | Out-File -FilePath "$tempDir\gcp-sa.json" -NoNewline + echo "GOOGLE_APPLICATION_CREDENTIALS=$tempDir\gcp-sa.json" >> $env:GITHUB_ENV + + - name: Cache Google Cloud KMS CNG provider (Windows) + if: ${{ matrix.os == 'windows-latest' }} + uses: actions/cache@v4 + id: cache-kms-cng + with: + path: build/installers/google-cloud-kms-cng-provider.msi + key: kms-cng-provider-${{ hashFiles('build/installers/.gitkeep') }}-v1.2 + restore-keys: | + kms-cng-provider- + + - name: Install Google Cloud KMS CNG provider (Windows) if: ${{ matrix.os == 'windows-latest' }} - run: yarn electron-builder --publish never --x64 --win nsis + shell: pwsh + run: | + $ForceDownload = if ('${{ steps.cache-kms-cng.outputs.cache-hit }}' -eq 'true') { $false } else { $true } + & "${{ github.workspace }}\build\install-kms-cng-provider.ps1" -Force:$ForceDownload + + - name: Setup Windows certificates (Windows) + if: ${{ matrix.os == 'windows-latest' }} + shell: pwsh + run: | + # Find and add Windows SDK signtool to PATH + Write-Host "Searching for signtool.exe in Windows SDK..." -ForegroundColor Cyan + $sdkPath = "${env:ProgramFiles(x86)}\Windows Kits\10\bin" + $signtoolFound = $false + + if (Test-Path $sdkPath) { + Write-Host "Windows SDK path exists: $sdkPath" + $signtoolPath = Get-ChildItem -Path $sdkPath -Include "signtool.exe" -Recurse -ErrorAction SilentlyContinue | + Sort-Object { $_.Directory.Name } -Descending | + Select-Object -First 1 + + if ($signtoolPath) { + $binPath = Split-Path $signtoolPath.FullName -Parent + Write-Host "✅ Found signtool.exe at: $($signtoolPath.FullName)" -ForegroundColor Green + Write-Host "Adding to PATH: $binPath" + + # Add to GITHUB_PATH for future steps + echo "$binPath" >> $env:GITHUB_PATH + + # Also add to current PATH for immediate use + $env:PATH = "$binPath;$env:PATH" + + # Store the full path for electron-builder + echo "SIGNTOOL_PATH=$($signtoolPath.FullName)" >> $env:GITHUB_ENV + + # Verify it's accessible + Write-Host "Verifying signtool accessibility..." + & $signtoolPath.FullName /? 2>&1 | Select-Object -First 1 + $signtoolFound = $true + } + } + + if (-not $signtoolFound) { + Write-Error "❌ signtool.exe not found in Windows SDK" + Write-Host "Attempting to install Windows SDK..." + # This would normally require admin rights and might not work in CI + # but let's log the issue clearly + exit 1 + } + + # Setup certificates + $certDir = "$env:RUNNER_TEMP\codesigning" + New-Item -ItemType Directory -Path $certDir -Force | Out-Null + '${{ secrets.WIN_USER_CRT }}' | Out-File -FilePath "$certDir\user.crt" -NoNewline + if ('${{ secrets.WIN_INTERMEDIATE_CRT }}' -ne '') { '${{ secrets.WIN_INTERMEDIATE_CRT }}' | Out-File -FilePath "$certDir\intermediate.crt" -NoNewline } + if ('${{ secrets.WIN_ROOT_CRT }}' -ne '') { '${{ secrets.WIN_ROOT_CRT }}' | Out-File -FilePath "$certDir\root.crt" -NoNewline } + + # Install certificates to stores + Write-Host "Installing certificates to Windows certificate stores..." + if (Test-Path "$certDir\root.crt") { + Write-Host "Installing root certificate..." + certutil -user -addstore "ROOT" "$certDir\root.crt" + } + if (Test-Path "$certDir\intermediate.crt") { + Write-Host "Installing intermediate certificate..." + certutil -user -addstore "CA" "$certDir\intermediate.crt" + } + Write-Host "Installing user certificate..." + certutil -user -addstore "MY" "$certDir\user.crt" + + # For KMS signing, we need to associate the certificate with the KMS key + # This is done through the CSP (Cryptographic Service Provider) + Write-Host "Associating certificate with KMS provider..." + + # Get the KMS key resource from environment + $kmsKeyResource = "${{ secrets.WIN_KMS_KEY_RESOURCE }}" + if ($kmsKeyResource) { + Write-Host "KMS Key Resource: $kmsKeyResource (masked)" + + # The certificate needs to be linked to the KMS key through the CSP + # This is typically done when the certificate is initially created with the KMS key + # Since we're importing an existing cert, we need to verify it has the proper association + + # Try to repair the certificate association + $thumb = (Get-PfxCertificate "$certDir\user.crt").Thumbprint + Write-Host "Certificate thumbprint: $thumb" + + # Use certutil to check the certificate's key provider info + Write-Host "Checking certificate key provider information..." + certutil -user -store MY $thumb | Select-String "Provider\|Key Container" + } + + # Compute and set certificate thumbprint + $thumb = (Get-PfxCertificate "$certDir\user.crt").Thumbprint + Write-Host "Certificate thumbprint: $thumb" + echo "WIN_KMS_CERT_SHA1=$thumb" >> $env:GITHUB_ENV + + # Verify the certificate is properly installed + Write-Host "Verifying certificate installation..." + $cert = Get-ChildItem -Path "Cert:\CurrentUser\My" | Where-Object { $_.Thumbprint -eq $thumb } + if ($cert) { + Write-Host "✅ Certificate found in store: $($cert.Subject)" + Write-Host " Issuer: $($cert.Issuer)" + Write-Host " Thumbprint: $($cert.Thumbprint)" + + # Check if the certificate has a private key reference + if ($cert.HasPrivateKey) { + Write-Host "✅ Certificate reports having a private key" + } else { + Write-Host "⚠️ Certificate does NOT have a private key - this is expected for KMS" + # For KMS signing, the private key is in the cloud, so this is actually OK + } + } else { + Write-Error "❌ Certificate not found in store!" + exit 1 + } + + # Verify Google Cloud authentication + if ($env:GOOGLE_APPLICATION_CREDENTIALS) { + Write-Host "✅ Google Cloud credentials configured: $env:GOOGLE_APPLICATION_CREDENTIALS" + if (Test-Path $env:GOOGLE_APPLICATION_CREDENTIALS) { + Write-Host "✅ Credentials file exists" + } else { + Write-Error "❌ Credentials file not found!" + exit 1 + } + } else { + Write-Error "❌ GOOGLE_APPLICATION_CREDENTIALS not set!" + exit 1 + } + + # Set up jsign and Google Cloud CLI for Windows builds + - name: Setup jsign (Windows) + if: ${{ matrix.os == 'windows-latest' }} + shell: pwsh + run: | + Write-Host "Installing OpenJDK 11..." -ForegroundColor Cyan + choco install openjdk11 -y + refreshenv + $javaHome = [System.Environment]::GetEnvironmentVariable("JAVA_HOME", "Machine") + if ($javaHome -and (Test-Path "$javaHome\bin\java.exe")) { + Write-Host "Java found at: $javaHome" -ForegroundColor Green + $env:JAVA_HOME = $javaHome + $javaBinPath = "$javaHome\bin" + $env:PATH = "$javaBinPath;$env:PATH" + echo "$javaBinPath" >> $env:GITHUB_PATH + } else { Write-Error "Java installation not found or JAVA_HOME not set"; exit 1 } + Write-Host "Installing jsign..." -ForegroundColor Cyan + choco install jsign -y + refreshenv + # Add jsign to GITHUB_PATH for subsequent steps + $jsignPath = "C:\ProgramData\chocolatey\lib\jsign\tools" + $jsignCmd = if (Test-Path "$jsignPath\jsign.cmd") { + "$jsignPath\jsign.cmd" + } else { + (Get-Command jsign.cmd -ErrorAction SilentlyContinue)?.Source + } + + if ($jsignCmd) { + $resolvedDir = Split-Path $jsignCmd -Parent + Write-Host "jsign found at $jsignCmd" -ForegroundColor Green + echo "$resolvedDir" >> $env:GITHUB_PATH + } else { + Write-Error "jsign not found at expected path or on PATH" + exit 1 + } + + - name: Setup gcloud (Windows) + if: ${{ matrix.os == 'windows-latest' }} + uses: google-github-actions/setup-gcloud@v2 + with: + version: '>=536.0.0' + + - name: Authenticate to Google Cloud (Windows) + if: ${{ matrix.os == 'windows-latest' }} + uses: google-github-actions/auth@v2 + with: + credentials_json: ${{ secrets.GCP_SA_JSON }} + + - name: Verify tools (Windows) + if: ${{ matrix.os == 'windows-latest' }} + shell: pwsh + run: | + java -version + jsign --help | Select-Object -First 2 + gcloud --version + + # Set up Google Cloud authentication for Linux builds + - name: Setup Google Cloud authentication (Linux) + if: ${{ matrix.os == 'ubuntu-latest' }} + run: | + echo "Setting up Google Cloud authentication..." + echo '${{ secrets.GCP_SA_JSON }}' > $RUNNER_TEMP/gcp-sa.json + echo "GOOGLE_APPLICATION_CREDENTIALS=$RUNNER_TEMP/gcp-sa.json" >> $GITHUB_ENV + + - name: Build Windows Package (Windows native - old signtool method) + if: ${{ matrix.os == 'windows-latest' && false }} + shell: pwsh + run: | + # Verify environment before build + Write-Host "=== Pre-build Environment Check ===" -ForegroundColor Cyan + + # Check signtool availability + Write-Host "Checking signtool availability..." + $signtoolCmd = Get-Command signtool -ErrorAction SilentlyContinue + if ($signtoolCmd) { + Write-Host "✅ signtool found in PATH at: $($signtoolCmd.Source)" -ForegroundColor Green + } else { + # Try to use the stored SIGNTOOL_PATH if available + if ($env:SIGNTOOL_PATH) { + Write-Host "Using SIGNTOOL_PATH: $env:SIGNTOOL_PATH" + # Create a shim/wrapper for signtool + $shimDir = "$env:RUNNER_TEMP\shims" + New-Item -ItemType Directory -Path $shimDir -Force | Out-Null + + # Create a batch file wrapper + $batchContent = "@echo off`n`"$env:SIGNTOOL_PATH`" %*" + $batchContent | Out-File -FilePath "$shimDir\signtool.bat" -Encoding ASCII + + # Also create a PowerShell wrapper + $psContent = "& `"$env:SIGNTOOL_PATH`" @args" + $psContent | Out-File -FilePath "$shimDir\signtool.ps1" -Encoding UTF8 + + # Add shim directory to PATH + $env:PATH = "$shimDir;$env:PATH" + Write-Host "Added signtool shim to PATH at: $shimDir" + } else { + Write-Error "❌ signtool not found in PATH and SIGNTOOL_PATH not set" + exit 1 + } + } + + # Verify KMS configuration + Write-Host "`n=== KMS Configuration ===" -ForegroundColor Cyan + Write-Host "WIN_KMS_KEY_RESOURCE: $(if ($env:WIN_KMS_KEY_RESOURCE) { '✅ Set' } else { '❌ Not set' })" + Write-Host "WIN_KMS_CERT_SHA1: $(if ($env:WIN_KMS_CERT_SHA1) { '✅ ' + $env:WIN_KMS_CERT_SHA1 } else { '❌ Not set' })" + Write-Host "WIN_KMS_CSP: $env:WIN_KMS_CSP" + Write-Host "WIN_KMS_CERT_STORE: $env:WIN_KMS_CERT_STORE" + Write-Host "GOOGLE_APPLICATION_CREDENTIALS: $(if ($env:GOOGLE_APPLICATION_CREDENTIALS) { '✅ ' + $env:GOOGLE_APPLICATION_CREDENTIALS } else { '❌ Not set' })" + + # Verify certificate in store + Write-Host "`n=== Certificate Verification ===" -ForegroundColor Cyan + if ($env:WIN_KMS_CERT_SHA1) { + $cert = Get-ChildItem -Path "Cert:\CurrentUser\My" | Where-Object { $_.Thumbprint -eq $env:WIN_KMS_CERT_SHA1 } + if ($cert) { + Write-Host "✅ Certificate found in store" + Write-Host " Subject: $($cert.Subject)" + Write-Host " HasPrivateKey: $($cert.HasPrivateKey)" + } else { + Write-Error "❌ Certificate with thumbprint $env:WIN_KMS_CERT_SHA1 not found in CurrentUser\My store!" + # Try to list all certs in the store for debugging + Write-Host "Certificates in CurrentUser\My store:" + Get-ChildItem -Path "Cert:\CurrentUser\My" | ForEach-Object { + Write-Host " - $($_.Thumbprint): $($_.Subject)" + } + } + } + + # Test KMS provider + Write-Host "`n=== Testing KMS Provider ===" -ForegroundColor Cyan + try { + # List available CSPs to verify KMS provider is installed + $cspList = @(certutil -csplist | Select-String "Provider Name") + $kmsProviderFound = $cspList | Where-Object { $_ -match "Google Cloud KMS Provider" } + if ($kmsProviderFound) { + Write-Host "✅ Google Cloud KMS Provider is installed" + } else { + Write-Host "⚠️ Google Cloud KMS Provider not found in CSP list" + Write-Host "Available CSPs:" + $cspList | ForEach-Object { Write-Host " $_" } + } + } catch { + Write-Host "⚠️ Could not verify CSP list: $_" + } + + # Test signing before the actual build + Write-Host "`n=== Testing Signing Configuration ===" -ForegroundColor Cyan + $testFile = "$env:RUNNER_TEMP\test-sign.exe" + + # Create a small test executable (copy powershell.exe as a test) + Copy-Item "$env:WINDIR\System32\WindowsPowerShell\v1.0\powershell.exe" -Destination $testFile -Force + + Write-Host "Testing signing with the following parameters:" + Write-Host " CSP: $env:WIN_KMS_CSP" + Write-Host " Key Resource: $(if ($env:WIN_KMS_KEY_RESOURCE) { 'Set (masked)' } else { 'Not set' })" + Write-Host " Certificate SHA1: $env:WIN_KMS_CERT_SHA1" + Write-Host " Certificate Store: $env:WIN_KMS_CERT_STORE" + + # Try to sign the test file + try { + $signtoolPath = if ($env:SIGNTOOL_PATH) { $env:SIGNTOOL_PATH } else { "signtool" } + Write-Host "Using signtool at: $signtoolPath" + + $signArgs = @( + "sign", + "/fd", "SHA256", + "/tr", "http://timestamp.digicert.com", + "/td", "SHA256", + "/csp", "$env:WIN_KMS_CSP", + "/kc", "$env:WIN_KMS_KEY_RESOURCE", + "/sha1", "$env:WIN_KMS_CERT_SHA1", + "/s", "$env:WIN_KMS_CERT_STORE", + "/v", # Verbose output + "/debug", # Debug output + "$testFile" + ) + + Write-Host "Running test signing command..." + Write-Host "$signtoolPath $($signArgs -join ' ')" -ForegroundColor Gray + + $result = & $signtoolPath @signArgs 2>&1 + $result | ForEach-Object { Write-Host $_ } + + if ($LASTEXITCODE -eq 0) { + Write-Host "✅ Test signing successful!" -ForegroundColor Green + } else { + Write-Host "❌ Test signing failed with exit code: $LASTEXITCODE" -ForegroundColor Red + Write-Host "This indicates a configuration issue with KMS signing." + + # Additional debugging + Write-Host "`nAttempting to get more information about the certificate..." + certutil -user -store MY $env:WIN_KMS_CERT_SHA1 -v | Select-String "Provider\|Container\|Key\|Algorithm" + } + } catch { + Write-Host "❌ Exception during test signing: $_" -ForegroundColor Red + } finally { + # Clean up test file + if (Test-Path $testFile) { + Remove-Item $testFile -Force -ErrorAction SilentlyContinue + } + } + + Write-Host "`n=== Starting Build ===" -ForegroundColor Cyan + # Run electron-builder + yarn electron-builder --publish never --x64 --ia32 --arm64 --win nsis env: - CSC_LINK: ${{ secrets.WIN_CSC_LINK }} - CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }} + WIN_KMS_KEY_RESOURCE: ${{ secrets.WIN_KMS_KEY_RESOURCE }} + WIN_CERT_FILE: ${{ runner.temp }}\codesigning\user.crt + WIN_KMS_CERT_SHA1: ${{ env.WIN_KMS_CERT_SHA1 }} + WIN_KMS_CSP: 'Google Cloud KMS Provider' + WIN_TIMESTAMP_URL: 'http://timestamp.digicert.com' + WIN_KMS_CERT_STORE: 'MY' + WIN_KMS_USE_LOCAL_MACHINE: 'false' + SIGNTOOL_PATH: ${{ env.SIGNTOOL_PATH }} + GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }} + + - name: Build Windows Package (Windows native) + if: ${{ matrix.build-target == 'windows' }} + shell: pwsh + run: | + Write-Host "=== Building Windows Package Natively ===" -ForegroundColor Cyan + + # Ensure jsign is accessible + $jsignPath = "C:\ProgramData\chocolatey\lib\jsign\tools" + if (Test-Path "$jsignPath\jsign.cmd") { + Write-Host "✅ jsign.cmd found at $jsignPath\jsign.cmd" -ForegroundColor Green + $env:PATH = "$jsignPath;$env:PATH" + echo "$jsignPath" >> $env:GITHUB_PATH + } else { + Write-Error "❌ jsign.cmd not found at $jsignPath\jsign.cmd" + exit 1 + } + + # Resolve gcloud from PATH (setup-gcloud added it) + $gcloudCmd = (Get-Command gcloud -ErrorAction SilentlyContinue)?.Source + if ($gcloudCmd) { + Write-Host "✅ gcloud resolved to: $gcloudCmd" -ForegroundColor Green + } else { + Write-Error "❌ gcloud not found on PATH. Ensure setup-gcloud@v2 ran successfully." + exit 1 + } + + Write-Host "PATH includes: $env:PATH" -ForegroundColor Cyan + + # Find Python installation for gcloud + Write-Host "Locating Python installation..." -ForegroundColor Yellow + $pythonPaths = @( + "C:\hostedtoolcache\windows\Python\*\x64\python.exe", + "C:\Python*\python.exe", + "C:\Program Files\Python*\python.exe", + "$env:LOCALAPPDATA\Programs\Python\Python*\python.exe" + ) + + $pythonExe = $null + foreach ($path in $pythonPaths) { + $found = Get-ChildItem $path -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($found) { + $pythonExe = $found.FullName + break + } + } + + if (-not $pythonExe) { + Write-Error "Python not found in expected locations" + exit 1 + } + + Write-Host "Found Python at: $pythonExe" -ForegroundColor Green + $env:CLOUDSDK_PYTHON = $pythonExe + + # Authenticate gcloud with service account + Write-Host "Authenticating gcloud with service account..." -ForegroundColor Yellow + $gcpCredentials = "$env:RUNNER_TEMP\gcp-sa.json" + Write-Host "Service account credentials at: $gcpCredentials" -ForegroundColor Cyan + + & $gcloudCmd auth activate-service-account --key-file="$gcpCredentials" + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to activate service account" + exit 1 + } + + # Set project from service account file + $projectData = Get-Content $gcpCredentials | ConvertFrom-Json + $projectId = $projectData.project_id + Write-Host "Setting project to: $projectId" -ForegroundColor Cyan + & $gcloudCmd config set project $projectId + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to set project" + exit 1 + } + + # Verify authentication is working + Write-Host "Verifying gcloud authentication..." -ForegroundColor Yellow + $tokenOutput = & $gcloudCmd auth print-access-token 2>&1 + if ($LASTEXITCODE -eq 0) { + Write-Host "✅ Google Cloud authentication successful" -ForegroundColor Green + } else { + Write-Host "❌ Google Cloud authentication failed: $tokenOutput" -ForegroundColor Red + exit 1 + } + + yarn electron-builder --publish never --x64 --ia32 --arm64 --win nsis + env: + WIN_KMS_KEY_RESOURCE: ${{ secrets.WIN_KMS_KEY_RESOURCE }} + WIN_CERT_FILE: ${{ runner.temp }}\codesigning\user.crt + GOOGLE_APPLICATION_CREDENTIALS: ${{ env.GOOGLE_APPLICATION_CREDENTIALS }} - name: Build MacOS Package - if: ${{ matrix.os == 'macos-latest' }} + if: ${{ matrix.build-target == 'mac' }} run: | sudo mdutil -a -i off yarn electron-builder --publish never --mac --universal @@ -79,12 +538,12 @@ jobs: ASC_PROVIDER: 'S6UPZG7ZR3' - name: Build Ubuntu Package - if: ${{ matrix.os == 'ubuntu-latest' }} - run: yarn electron-builder --publish never --linux snap deb + if: ${{ matrix.build-target == 'linux' }} + run: yarn electron-builder --publish never --linux snap deb appimage - name: Find Snap File id: find_snap - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.build-target == 'linux' }} run: | SNAP_FILE=$(find dist/ -maxdepth 1 -name 'rocketchat-*.snap' -print -quit) if [ -z "$SNAP_FILE" ]; then @@ -95,28 +554,7 @@ jobs: echo "SNAP_FILE_PATH=$SNAP_FILE" >> $GITHUB_OUTPUT - name: Publish to Snap Store (Edge) - if: ${{ matrix.os == 'ubuntu-latest' }} - uses: snapcore/action-publish@v1 - env: - SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} - with: - snap: ${{ steps.find_snap.outputs.SNAP_FILE_PATH }} - release: edge - - - name: Find Snap File - id: find_snap - if: ${{ matrix.os == 'ubuntu-latest' }} - run: | - SNAP_FILE=$(find dist/ -maxdepth 1 -name 'rocketchat-*.snap' -print -quit) - if [ -z "$SNAP_FILE" ]; then - echo "::error::Snap file not found in dist/" - exit 1 - fi - echo "Found snap file: $SNAP_FILE" - echo "SNAP_FILE_PATH=$SNAP_FILE" >> $GITHUB_OUTPUT - - - name: Publish to Snap Store (Edge) - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.build-target == 'linux' }} uses: snapcore/action-publish@v1 env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} @@ -130,7 +568,7 @@ jobs: # Upload artifacts to Wasabi (Windows) - name: Upload Artifacts to Wasabi (Windows) - if: ${{ matrix.os == 'windows-latest' }} + if: ${{ matrix.build-target == 'windows' }} run: | aws s3 cp dist/ s3://${{ secrets.WASABI_BUCKET_NAME }}/pr-${{ github.event.pull_request.number }}/${{ matrix.os }}/ --recursive ` --acl public-read ` @@ -143,7 +581,7 @@ jobs: # Upload artifacts to Wasabi (macOS) - name: Upload Artifacts to Wasabi (macOS) - if: ${{ matrix.os == 'macos-latest' }} + if: ${{ matrix.build-target == 'mac' }} run: | aws s3 cp dist/ s3://${{ secrets.WASABI_BUCKET_NAME }}/pr-${{ github.event.pull_request.number }}/${{ matrix.os }}/ --recursive \ --acl public-read \ @@ -158,13 +596,14 @@ jobs: # Upload artifacts to Wasabi (Ubuntu) - name: Upload Artifacts to Wasabi (Ubuntu) - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.build-target == 'linux' }} run: | aws s3 cp dist/ s3://${{ secrets.WASABI_BUCKET_NAME }}/pr-${{ github.event.pull_request.number }}/${{ matrix.os }}/ --recursive \ --acl public-read \ --endpoint-url=https://s3.us-east-1.wasabisys.com \ --exclude "*" \ --include "rocketchat-*.snap" \ + --include "rocketchat-*.AppImage" \ --include "rocketchat-*.deb" env: AWS_ACCESS_KEY_ID: ${{ secrets.WASABI_ACCESS_KEY_ID }} @@ -180,7 +619,7 @@ jobs: const path = require('path'); const distDir = path.join(process.cwd(), 'dist'); const files = fs.readdirSync(distDir); - const patterns = [/rocketchat-.*\.dmg$/, /rocketchat-.*\.pkg$/, /rocketchat-.*\.exe$/, /rocketchat-.*\.snap$/, /rocketchat-.*\.deb$/]; + const patterns = [/rocketchat-.*\.dmg$/, /rocketchat-.*\.pkg$/, /rocketchat-.*\.exe$/, /rocketchat-.*\.snap$/, /rocketchat-.*\.deb$/, /rocketchat-.*\.appimage$/i]; let artifactUrls = ''; for (const file of files) { if (patterns.some(pattern => pattern.test(file))) { @@ -195,9 +634,7 @@ jobs: uses: marocchino/sticky-pull-request-comment@v2 with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + header: ${{ runner.os }}-installer message: | ### ${{ runner.os }} installer download ${{ steps.get-artifact-urls.outputs.artifact_urls }} - header: '### ${{ runner.os }} installer download' - recreate: true - append: false diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index 356955881e..774ca18f1b 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -4,11 +4,14 @@ on: pull_request: branches: - master + - dev concurrency: group: ${{ github.workflow }}-${{ github.head_ref }} cancel-in-progress: true +permissions: + contents: read jobs: check: strategy: @@ -24,10 +27,10 @@ jobs: run: git config --global core.autocrlf false - name: Checkout code uses: actions/checkout@v3 - - name: Setup Node 22.13.1 + - name: Setup Node 22.17.1 uses: actions/setup-node@v4 with: - node-version: '22.13.1' + node-version: '22.17.1' - name: Setup node_modules cache uses: actions/cache@v4 with: diff --git a/.gitignore b/.gitignore index 2246ee7f6a..7b68f1ae12 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ Thumbs.db /dist /Development.provisionprofile +# npm (this project uses yarn) +/package-lock.json + # yarn .pnp.* .yarn/* @@ -17,4 +20,6 @@ Thumbs.db !.yarn/releases !.yarn/sdks !.yarn/versions -supportedVersions.jwt \ No newline at end of file +supportedVersions.jwt +# local research (git-ignored) +localdev/ diff --git a/.prettierrc.mjs b/.prettierrc.mjs index 40d8c98ceb..8e6f105aa7 100644 --- a/.prettierrc.mjs +++ b/.prettierrc.mjs @@ -2,5 +2,5 @@ import config from '@rocket.chat/prettier-config/fuselage/index.js'; export default { ...config, - "trailingComma": "es5" -} + trailingComma: 'es5', +}; diff --git a/.yarn/patches/@ewsjs-xhr-npm-2.0.2-77506b0a6c.patch b/.yarn/patches/@ewsjs-xhr-npm-2.0.2-77506b0a6c.patch new file mode 100644 index 0000000000..5f0abfd512 --- /dev/null +++ b/.yarn/patches/@ewsjs-xhr-npm-2.0.2-77506b0a6c.patch @@ -0,0 +1,34 @@ +diff --git a/dist/ntlmProvider.js b/dist/ntlmProvider.js +index df9a914110df069ad0a7b3cd4d07461a23aeee2a..e2dc5e7d8cd2e64823fea9fc4ec32fc258a20063 100644 +--- a/dist/ntlmProvider.js ++++ b/dist/ntlmProvider.js +@@ -40,7 +40,11 @@ class NtlmProvider { + delete opt['data']; + delete opt['responseType']; + try { +- const response = await (0, axios_1.default)(opt).catch(err => err.response); ++ const response = await (0, axios_1.default)(opt).catch(err => { ++ if (err.response) ++ return err.response; ++ throw err; ++ }); + if (!response.headers['www-authenticate']) + throw new Error('www-authenticate not found on response of second request'); + let type2msg = (0, ntlm_client_1.decodeType2Message)(response.headers['www-authenticate']); +diff --git a/src/ntlmProvider.ts b/src/ntlmProvider.ts +index e6689cbef424452426ae46ca00cf34abcbb77b88..fb0a47127155229ab886adc39f1059e4ed2a2454 100644 +--- a/src/ntlmProvider.ts ++++ b/src/ntlmProvider.ts +@@ -51,7 +51,11 @@ export class NtlmProvider implements IProvider { + delete opt['responseType']; + + try { +- const response = await axios(opt).catch(err => err.response); ++ const response = await axios(opt).catch(err => { ++ if (err.response) ++ return err.response; ++ throw err; ++ }); + + if (!response.headers['www-authenticate']) + throw new Error('www-authenticate not found on response of second request'); diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..df04b1ae50 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,347 @@ +# Development Guidelines for Rocket.Chat.Electron + +## Quick Reference + +```bash +# Development +yarn install && yarn start + +# Testing +yarn lint && yarn test + +# Build workspaces (ALWAYS use root commands) +yarn workspaces:build + +# Windows build (all architectures) +yarn electron-builder --x64 --ia32 --arm64 --win nsis +``` + +--- + +## Building the Project + +### Workspace Commands (IMPORTANT) + +**ALWAYS** use root package.json commands for workspaces: + +```bash +yarn workspaces:build # Builds all workspaces including desktop-release-action +``` + +**DO NOT** run `yarn build` directly in workspace directories - creates incorrect output structures. + +### After Building desktop-release-action + +Remove the nested dist folder created by ncc bundler: + +```bash +rm -rf workspaces/desktop-release-action/dist/dist +``` + +The action only needs `workspaces/desktop-release-action/dist/index.js`. + +### Windows Build Architectures + +Always include all architectures: + +- x64 (64-bit) +- ia32 (32-bit) +- arm64 (ARM) + +### Code Signing + +Windows packages use Google Cloud KMS. Build happens in two phases: + +1. Build packages without signing (empty environment variables) +2. Sign built packages using jsign with Google Cloud KMS + +This prevents MSI build failures from KMS CNG provider conflicts. + +### Patching npm Packages (CRITICAL) + +This project uses **TWO different patching mechanisms** - do NOT confuse them: + +| Mechanism | Location | Used For | +| ----------------------- | ---------------- | ----------------------------------------- | +| **Yarn patch protocol** | `.yarn/patches/` | `@ewsjs/xhr` (configured in package.json) | +| **patch-package** | `patches/` | `@kayahr/jest-electron-runner` | + +**NEVER add `@ewsjs/xhr` patches to `patches/`** - it's already patched by Yarn. Adding a patch-package patch causes CI failures due to conflicts. + +--- + +## Architecture Overview + +Electron desktop application built with TypeScript and React. + +### Entry Points (compiled by Rollup) + +| File | Process | Purpose | +| ------------------- | -------- | ----------------------------------------------- | +| `src/main.ts` | Main | Orchestrates the application | +| `src/rootWindow.ts` | Renderer | Main window UI | +| `src/preload.ts` | Preload | Privileged API bridge between main and renderer | + +### Core Technologies + +- **Electron 40.0.0** - Desktop framework +- **TypeScript 5.7.3** - Type-safe JavaScript +- **React 18.3.1** - UI components +- **Redux 5.0.1** - State management +- **Rollup 4.32.0** - Build bundler +- **Jest** - Testing (with Electron runner) + +### Key Directories + +| Directory | Purpose | +| ---------------------- | ------------------------------ | +| `src/main/` | Main process code | +| `src/ui/` | React components and UI logic | +| `src/preload/` | Preload scripts for secure IPC | +| `src/servers/` | Server connection management | +| `src/downloads/` | Download handling | +| `src/notifications/` | Notification system | +| `src/videoCallWindow/` | Video call window management | +| `src/store/` | Redux store configuration | +| `src/i18n/` | Internationalization resources | + +### State Management + +Redux with modular reducers. State syncs between main and renderer via IPC channels in `src/ipc/`. + +### Configuration Files + +| File | Purpose | +| ----------------------- | ---------------------------------------------------- | +| `rollup.config.mjs` | Build configuration | +| `electron-builder.json` | Packager configuration | +| `tsconfig.json` | TypeScript options | +| `.eslintrc.json` | ESLint rules (extends `@rocket.chat/eslint-config`) | +| `jest.config.js` | Test configuration (separate main/renderer projects) | +| `servers.json` | Default server connections | + +--- + +## Code Style and Conventions + +- TypeScript strict mode enabled +- React functional components with hooks +- Redux actions follow FSA (Flux Standard Action) pattern +- File naming: camelCase for files, PascalCase for components +- All code must pass ESLint and TypeScript checks +- Prefer editing existing files over creating new ones +- **No unnecessary comments** - self-documenting code through clear naming +- Only comment complex business logic or non-obvious decisions + +--- + +## UI Development - Fuselage Design System + +**MANDATORY: Use Fuselage components** for all UI work. + +- Storybook: [Rocket.Chat Fuselage Storybook](https://rocketchat.github.io/fuselage) +- Repository: [Rocket.Chat Fuselage](https://github.com/RocketChat/fuselage) +- Only create custom components when Fuselage doesn't provide what's needed +- Check `Theme.d.ts` for valid color tokens +- Import from `@rocket.chat/fuselage` +- Reference: [Rocket.Chat main repo](https://github.com/RocketChat/Rocket.Chat) for usage patterns + +--- + +## Testing + +### Test Files + +- `*.spec.ts` - Renderer process tests +- `*.main.spec.ts` - Main process tests + +Uses `@kayahr/jest-electron-runner` for Electron environment simulation. + +### Cross-Platform Compatibility (CRITICAL) + +Tests run on Windows, macOS, AND Linux CI. + +#### Primary Pattern: Defensive Coding + +Use optional chaining with fallbacks for Linux-only APIs: + +```typescript +// PREFERRED - works on all platforms without mocks +const uid = process.getuid?.() ?? 1000; +const isRoot = process.getuid?.() === 0; +const runtimeDir = + process.env.XDG_RUNTIME_DIR || `/run/user/${process.getuid?.() ?? 1000}`; +``` + +**Fallback Pattern: Mocking** (only when defensive coding isn't possible) + +```typescript +const originalPlatform = process.platform; + +beforeEach(() => { + Object.defineProperty(process, 'platform', { + value: 'linux', + configurable: true, + }); + process.env.XDG_RUNTIME_DIR = '/run/user/1000'; +}); + +afterEach(() => { + Object.defineProperty(process, 'platform', { + value: originalPlatform, + configurable: true, + }); +}); +``` + +**Linux-only APIs requiring defensive coding:** + +- `process.getuid()` / `process.getgid()` / `process.geteuid()` / `process.getegid()` + +--- + +## Git Guidelines + +### Branch Workflow + +**NEVER** commit directly to master/main. Always: + +1. Create a new branch +2. Test thoroughly +3. Create a Pull Request +4. Wait for review and approval + +### Git Operations (CRITICAL) + +- **NEVER commit without explicit user permission** - even if work is complete, wait for user to say "commit" +- **NEVER** merge, rebase, or push unless explicitly requested +- Read operations (status, diff, log, show) are allowed +- Show what will be committed before committing +- Wait for user confirmation before any write operation + +### Git Worktrees for Agents + +Agents should use worktrees to avoid disrupting user's work: + +```bash +# Create worktree (branch from master) +mkdir -p ../Rocket.Chat.Electron-worktrees +git worktree add ../Rocket.Chat.Electron-worktrees/feature-name -b new-branch master +cd ../Rocket.Chat.Electron-worktrees/feature-name +yarn + +# List worktrees +git worktree list + +# Clean up when done +git worktree remove ../Rocket.Chat.Electron-worktrees/feature-name +``` + +Each worktree has its own working directory, branch, build outputs, and node_modules. + +--- + +## Library and Framework Usage + +- **Always verify before using** - check official docs and type definitions +- For TypeScript: check `.d.ts` files in `node_modules/@package-name/dist/` +- Never assume prop values, tokens, or API endpoints work without verification + +--- + +## Documentation Guidelines + +- Check `docs/` folder before working on features +- Update existing docs when changing documented features +- Create docs for new features (flow diagrams, architecture, examples) +- Use simple language - explain like talking to a colleague +- Avoid complex words: "advanced" not "sophisticated", "use" not "utilize" + +### Knowledge Management (IMPORTANT) + +**When new knowledge is gained during development**, update documentation: + +1. **Root `AGENTS.md`** - Project-wide patterns, conventions, gotchas +2. **Folder-level `AGENTS.md`** - Module-specific knowledge (e.g., `src/outlookCalendar/AGENTS.md`) +3. **`docs/` folder** - Feature documentation, architecture diagrams, flow explanations + +**What to document:** + +- Bug patterns and their solutions +- Non-obvious architectural decisions +- Integration gotchas (e.g., "preload.ts runs in renderer, can't share globals with main") +- Critical warnings (e.g., "verboseLog() must call console.log(), not itself") +- Patterns that took time to figure out + +**Hierarchy:** + +```text +AGENTS.md (root) → Project-wide guidelines +├── src/module/AGENTS.md → Module-specific knowledge +└── docs/ → Feature documentation +``` + +This ensures future agents don't repeat the same investigations. + +--- + +## Communication Guidelines + +- Avoid subjective descriptors: "smart", "excellent", "dumb", "poor" +- Don't call existing code "slow" - only say "faster" when measurably true +- Use measurable improvements: "reduced memory usage", "improved by X%" +- Stay neutral - implement and describe, don't evaluate +- PR descriptions: straightforward language, no fancy words + +--- + +## Writing Technical Documentation + +### Never Invent Metrics + +**NEVER:** + +- Estimate time spent ("3 days debugging") +- Speculate user counts ("500+ users affected") +- Create arbitrary metrics without evidence +- Include time tracking unless explicitly requested + +**ONLY include:** + +- Numbers from actual logs or error messages +- Metrics documented in issues/PRs +- Verifiable repository data + +**Examples:** + +- Wrong: "Spent 3 days debugging" → Correct: "Multiple debugging approaches attempted" +- Wrong: "Affected 500+ users" → Correct: "Multiple community issues reported" + +### Post-Mortem Documents + +Start with "The Solution That Actually Worked": + +- **Problem**: Core issue in one sentence +- **Solution**: Code/approach that fixed it +- **Result**: Measurable improvement +- **PR**: Link + +Structure: + +1. The Solution That Actually Worked +2. Executive Summary +3. The Problem +4. Root Cause Analysis +5. Investigation Timeline +6. The Solution (detailed) +7. Critical Discoveries +8. Metrics & Impact +9. Lessons Learned +10. Future Recommendations + +Focus on: + +- Qualitative descriptions over fake quantitative data +- Actual configuration values from code +- Observable behaviors rather than percentages +- Root causes, not symptoms diff --git a/CHANGELOG.md b/CHANGELOG.md index 391f73644a..f9ba64edb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,498 +1,370 @@ ## [3.7.3](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.7.2...3.7.3) (2021-12-27) - ### Bug Fixes -* Object has destroyed message ([#2273](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2273)) ([3560b93](https://github.com/RocketChat/Rocket.Chat.Electron/commit/3560b93a541d5ae0b00cb886228942e4525f7246)) - - +- Object has destroyed message ([#2273](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2273)) ([3560b93](https://github.com/RocketChat/Rocket.Chat.Electron/commit/3560b93a541d5ae0b00cb886228942e4525f7246)) ## [3.7.1](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.7.0...3.7.1) (2021-12-20) - ### Bug Fixes -* add Windows zip to releases and fix Windows architecture switching on release ([#2258](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2258)) ([a67818e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a67818e7fea9f93c6b2e45873981547babca4ede)) -* Run menu bar event listeners asynchronously ([#2254](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2254)) ([d99352a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d99352afde37494910a9509e026c9fdf86aef18d)) - - +- add Windows zip to releases and fix Windows architecture switching on release ([#2258](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2258)) ([a67818e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a67818e7fea9f93c6b2e45873981547babca4ede)) +- Run menu bar event listeners asynchronously ([#2254](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2254)) ([d99352a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d99352afde37494910a9509e026c9fdf86aef18d)) # [3.7.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.6.0...3.7.0) (2021-11-30) - ### Bug Fixes -* Empty screen from a fresh start + servers.json ([#2243](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2243)) ([407c562](https://github.com/RocketChat/Rocket.Chat.Electron/commit/407c562531b5af841b40006f25aa657db56979f4)) - - +- Empty screen from a fresh start + servers.json ([#2243](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2243)) ([407c562](https://github.com/RocketChat/Rocket.Chat.Electron/commit/407c562531b5af841b40006f25aa657db56979f4)) # [3.6.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.5.7...3.6.0) (2021-11-16) - ### Bug Fixes -* Add back desktop version to userAgent string ([#2217](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2217)) ([07c6079](https://github.com/RocketChat/Rocket.Chat.Electron/commit/07c6079060a40d2549bc1502b55fc5d53219e018)) -* Missing entitlements on mac app ([#2191](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2191)) ([ce52cbc](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ce52cbcf749a3e2147723ffa8f2d42a3b20f8047)) - +- Add back desktop version to userAgent string ([#2217](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2217)) ([07c6079](https://github.com/RocketChat/Rocket.Chat.Electron/commit/07c6079060a40d2549bc1502b55fc5d53219e018)) +- Missing entitlements on mac app ([#2191](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2191)) ([ce52cbc](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ce52cbcf749a3e2147723ffa8f2d42a3b20f8047)) ### Features -* Add clear cache and clear storage data options to server ([#2229](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2229)) ([d65e73a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d65e73a6e87d10785d861ccf67a434d301698223)) -* Setting to open video chat on application window or external browser ([#2227](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2227)) ([d3364fd](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d3364fdcf73b440d758af6a3ad59f63d972986e8)) - - +- Add clear cache and clear storage data options to server ([#2229](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2229)) ([d65e73a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d65e73a6e87d10785d861ccf67a434d301698223)) +- Setting to open video chat on application window or external browser ([#2227](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2227)) ([d3364fd](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d3364fdcf73b440d758af6a3ad59f63d972986e8)) ## [3.5.7](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.5.6...3.5.7) (2021-10-06) - ### Bug Fixes -* Prevent error if can reach the icon ([#2197](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2197)) ([8c5217f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/8c5217f96160fea0f24fa29e8b9f162ff9c906b6)) -* Prevent invalid range for unit format ([#2195](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2195)) ([cc4037b](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cc4037b1fc0acd50f3cf8434434ccdc534568228)) -* Prevent store send messages before the screen is ready ([#2198](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2198)) ([34d185d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/34d185d6b8781e60f446d298c487e5f418b44fe5)) -* Redact info ([#2196](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2196)) ([7a62f33](https://github.com/RocketChat/Rocket.Chat.Electron/commit/7a62f33f0a24bcbf6ceb928b87147490d03cc1f2)) - +- Prevent error if can reach the icon ([#2197](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2197)) ([8c5217f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/8c5217f96160fea0f24fa29e8b9f162ff9c906b6)) +- Prevent invalid range for unit format ([#2195](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2195)) ([cc4037b](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cc4037b1fc0acd50f3cf8434434ccdc534568228)) +- Prevent store send messages before the screen is ready ([#2198](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2198)) ([34d185d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/34d185d6b8781e60f446d298c487e5f418b44fe5)) +- Redact info ([#2196](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2196)) ([7a62f33](https://github.com/RocketChat/Rocket.Chat.Electron/commit/7a62f33f0a24bcbf6ceb928b87147490d03cc1f2)) ### Features -* Electron dl for improve download experience ([#2200](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2200)) ([a132009](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a1320093880fe98f59be4f7cac0f3093e9195d33)) - - +- Electron dl for improve download experience ([#2200](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2200)) ([a132009](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a1320093880fe98f59be4f7cac0f3093e9195d33)) ## [3.5.6](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.5.5...3.5.6) (2021-09-23) - ### Bug Fixes -* Jitisi opening on browser ([#2180](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2180)) ([1d34690](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1d3469025e0f1b7988e542d02a8890308f136f6f)) - +- Jitisi opening on browser ([#2180](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2180)) ([1d34690](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1d3469025e0f1b7988e542d02a8890308f136f6f)) ### Features -* apple silicon universal support ([#2170](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2170)) ([6f180c6](https://github.com/RocketChat/Rocket.Chat.Electron/commit/6f180c693fb9a4d22b981a1e891499bc547a1b61)) - - +- apple silicon universal support ([#2170](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2170)) ([6f180c6](https://github.com/RocketChat/Rocket.Chat.Electron/commit/6f180c693fb9a4d22b981a1e891499bc547a1b61)) ## [3.5.5](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.5.4...3.5.5) (2021-09-21) - ### Bug Fixes -* Prevent errors on startup : Fix icon and window positioning errors ([#2173](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2173)) ([d42ce30](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d42ce30794d62ef494dbafd739e6eb0a5ce65b21)) - - +- Prevent errors on startup : Fix icon and window positioning errors ([#2173](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2173)) ([d42ce30](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d42ce30794d62ef494dbafd739e6eb0a5ce65b21)) ## [3.5.4](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.5.3...3.5.4) (2021-09-16) - ### Bug Fixes -* After zoom/reset navigating displays "white screen" ([#1947](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1947)) ([a1323df](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a1323df9ddb4dff8af6811e994d8e9b12f6fb24d)) -* Fix memory leak after navigation ([#2168](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2168)) ([8ef2d67](https://github.com/RocketChat/Rocket.Chat.Electron/commit/8ef2d67b361ce72d40b8b1d54d858730b2c9021f)) - - +- After zoom/reset navigating displays "white screen" ([#1947](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1947)) ([a1323df](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a1323df9ddb4dff8af6811e994d8e9b12f6fb24d)) +- Fix memory leak after navigation ([#2168](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2168)) ([8ef2d67](https://github.com/RocketChat/Rocket.Chat.Electron/commit/8ef2d67b361ce72d40b8b1d54d858730b2c9021f)) ## [3.5.3](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.5.2...3.5.3) (2021-09-09) - ### Bug Fixes -* Fix OAuth ([#2158](https://github.com/RocketChat/Rocket.Chat.Electron/pull/2158))([76bbba4](https://github.com/RocketChat/Rocket.Chat.Electron/commit/76bbba4d57bed9834c4fee9193b0d310ebccb907)) -* Remove preventDefault from download ([#2159](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2159)) ([4030e34](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4030e34561c6c88d4825106869cea649cfe42c44)) -* Removes rid param/conditional to fix deeplink ([#2160](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2160)) ([2a72853](https://github.com/RocketChat/Rocket.Chat.Electron/commit/2a7285356c26af884496ad0763aa8591f43211c2)) - - +- Fix OAuth ([#2158](https://github.com/RocketChat/Rocket.Chat.Electron/pull/2158))([76bbba4](https://github.com/RocketChat/Rocket.Chat.Electron/commit/76bbba4d57bed9834c4fee9193b0d310ebccb907)) +- Remove preventDefault from download ([#2159](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2159)) ([4030e34](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4030e34561c6c88d4825106869cea649cfe42c44)) +- Removes rid param/conditional to fix deeplink ([#2160](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2160)) ([2a72853](https://github.com/RocketChat/Rocket.Chat.Electron/commit/2a7285356c26af884496ad0763aa8591f43211c2)) ## [3.5.2](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.5.1...3.5.2) (2021-09-09) - ### Bug Fixes -* Bugsnag ([#2153](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2153)) ([e3b1be7](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e3b1be72db0facaa9890a07c5d8b38b7bf685fe9)) -* Click Notification ([#2154](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2154)) ([fb56545](https://github.com/RocketChat/Rocket.Chat.Electron/commit/fb565450ff0314e2cbc35140c287d6b7651f84bf)) -* oauth ([#2152](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2152)) ([898f3b4](https://github.com/RocketChat/Rocket.Chat.Electron/commit/898f3b49d2c58fe0c677025db4e2b5e89fe0aa59)) -* remove typo text ([#2150](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2150)) ([143fd48](https://github.com/RocketChat/Rocket.Chat.Electron/commit/143fd4810fc25538324db3db65e158b79601880d)) - - +- Bugsnag ([#2153](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2153)) ([e3b1be7](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e3b1be72db0facaa9890a07c5d8b38b7bf685fe9)) +- Click Notification ([#2154](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2154)) ([fb56545](https://github.com/RocketChat/Rocket.Chat.Electron/commit/fb565450ff0314e2cbc35140c287d6b7651f84bf)) +- oauth ([#2152](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2152)) ([898f3b4](https://github.com/RocketChat/Rocket.Chat.Electron/commit/898f3b49d2c58fe0c677025db4e2b5e89fe0aa59)) +- remove typo text ([#2150](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2150)) ([143fd48](https://github.com/RocketChat/Rocket.Chat.Electron/commit/143fd4810fc25538324db3db65e158b79601880d)) ## [3.5.1](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.5.0...3.5.1) (2021-09-08) - ### Bug Fixes -* Prevent Webcontent to navigate (outside) ([abd7a08](https://github.com/RocketChat/Rocket.Chat.Electron/commit/abd7a080c3cbdd99d9b398a2f724313b7a038648)) -* Restore window on focus request ([ca0b8c6](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ca0b8c6b8d9e7f0099655f2dc9bad7b5b08f71b1)) -* Settings and Download view ([fecdd5a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/fecdd5ad90edfe578474e9df6cf0fb20b36ab925)) - - +- Prevent Webcontent to navigate (outside) ([abd7a08](https://github.com/RocketChat/Rocket.Chat.Electron/commit/abd7a080c3cbdd99d9b398a2f724313b7a038648)) +- Restore window on focus request ([ca0b8c6](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ca0b8c6b8d9e7f0099655f2dc9bad7b5b08f71b1)) +- Settings and Download view ([fecdd5a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/fecdd5ad90edfe578474e9df6cf0fb20b36ab925)) # [3.5.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.4.0...3.5.0) (2021-09-06) - ### Bug Fixes -* Set default server servers.json ([#2064](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2064)) ([3965307](https://github.com/RocketChat/Rocket.Chat.Electron/commit/3965307ed4605af7880b95ebb1d195e247734ea1)) -* Set default server servers.json and Open server on click notification ([#2144](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2144)) ([b748b10](https://github.com/RocketChat/Rocket.Chat.Electron/commit/b748b10017c9937388c995278aa5ee1f82dcd469)) - +- Set default server servers.json ([#2064](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2064)) ([3965307](https://github.com/RocketChat/Rocket.Chat.Electron/commit/3965307ed4605af7880b95ebb1d195e247734ea1)) +- Set default server servers.json and Open server on click notification ([#2144](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2144)) ([b748b10](https://github.com/RocketChat/Rocket.Chat.Electron/commit/b748b10017c9937388c995278aa5ee1f82dcd469)) ### Features -* Settings allow enable/disable flashframe ([#2142](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2142)) ([1a48b69](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1a48b6940a0aac2fbb2254817926cb5f6c4aee3e)) -* Settings page and report opt-out ([#2138](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2138)) ([12ca958](https://github.com/RocketChat/Rocket.Chat.Electron/commit/12ca9589f45298fa430bd9fd30e7053637ecc84f)) - - +- Settings allow enable/disable flashframe ([#2142](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2142)) ([1a48b69](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1a48b6940a0aac2fbb2254817926cb5f6c4aee3e)) +- Settings page and report opt-out ([#2138](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2138)) ([12ca958](https://github.com/RocketChat/Rocket.Chat.Electron/commit/12ca9589f45298fa430bd9fd30e7053637ecc84f)) # [3.4.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.2.3...3.4.0) (2021-08-25) - ### Bug Fixes -* Prevents Notifications on Linux to show the message "Rocket.Chat is ready" ([#2122](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2122)) ([5ac093e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/5ac093ec33c6b5dccc603b1fa4e91d0eaeb1a7d2)) -* solve prettier errors ([bf16f55](https://github.com/RocketChat/Rocket.Chat.Electron/commit/bf16f550260e137883446229fd42cc126ab8e36c)) - - +- Prevents Notifications on Linux to show the message "Rocket.Chat is ready" ([#2122](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2122)) ([5ac093e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/5ac093ec33c6b5dccc603b1fa4e91d0eaeb1a7d2)) +- solve prettier errors ([bf16f55](https://github.com/RocketChat/Rocket.Chat.Electron/commit/bf16f550260e137883446229fd42cc126ab8e36c)) # [3.3.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.2.2...3.3.0) (2021-06-17) - ### Bug Fixes -* **i18n:** Language update from LingoHub 🤖 ([#2042](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2042)) ([cb07526](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cb07526ed5f4831fd05458208561be8e81f5d218)) -* **i18n:** Language update from LingoHub 🤖 ([#2047](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2047)) ([891b7b7](https://github.com/RocketChat/Rocket.Chat.Electron/commit/891b7b70d9fb6382ca2070c1644669b3f38b17ef)) - - +- **i18n:** Language update from LingoHub 🤖 ([#2042](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2042)) ([cb07526](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cb07526ed5f4831fd05458208561be8e81f5d218)) +- **i18n:** Language update from LingoHub 🤖 ([#2047](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2047)) ([891b7b7](https://github.com/RocketChat/Rocket.Chat.Electron/commit/891b7b70d9fb6382ca2070c1644669b3f38b17ef)) ## [3.2.4](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.2.3...3.2.4) (2021-08-16) - ### Bug Fixes -* Prevents Notifications on Linux to show the message "Rocket.Chat is ready" ([#2122](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2122)) ([5ac093e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/5ac093ec33c6b5dccc603b1fa4e91d0eaeb1a7d2)) - - +- Prevents Notifications on Linux to show the message "Rocket.Chat is ready" ([#2122](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2122)) ([5ac093e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/5ac093ec33c6b5dccc603b1fa4e91d0eaeb1a7d2)) # [3.3.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.2.2...3.3.0) (2021-06-17) - ### Bug Fixes -* **i18n:** Language update from LingoHub 🤖 ([#2042](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2042)) ([cb07526](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cb07526ed5f4831fd05458208561be8e81f5d218)) -* **i18n:** Language update from LingoHub 🤖 ([#2047](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2047)) ([891b7b7](https://github.com/RocketChat/Rocket.Chat.Electron/commit/891b7b70d9fb6382ca2070c1644669b3f38b17ef)) - - +- **i18n:** Language update from LingoHub 🤖 ([#2042](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2042)) ([cb07526](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cb07526ed5f4831fd05458208561be8e81f5d218)) +- **i18n:** Language update from LingoHub 🤖 ([#2047](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2047)) ([891b7b7](https://github.com/RocketChat/Rocket.Chat.Electron/commit/891b7b70d9fb6382ca2070c1644669b3f38b17ef)) ## [3.2.3](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.2.2...3.2.3) (2021-07-01) - ### Bug Fixes -* Desktop notifications not working ([2d4607d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/2d4607dd3bb49dfb27c2df24f6a5b95d0b675ee4)) - - +- Desktop notifications not working ([2d4607d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/2d4607dd3bb49dfb27c2df24f6a5b95d0b675ee4)) ## [3.2.2](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.2.1...3.2.2) (2021-05-14) - ### Bug Fixes -* **i18n:** Language update from LingoHub 🤖 ([#2032](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2032)) ([39719ef](https://github.com/RocketChat/Rocket.Chat.Electron/commit/39719ef60284381abf5c6b0356f8377858878f0c)) -* **jitsi:** JitsiMeetElectron unavailable on preload script ([#2031](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2031)) ([1322938](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1322938290e5aca5b9e6c2f335d1ee0f1132b2bc)) - - +- **i18n:** Language update from LingoHub 🤖 ([#2032](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2032)) ([39719ef](https://github.com/RocketChat/Rocket.Chat.Electron/commit/39719ef60284381abf5c6b0356f8377858878f0c)) +- **jitsi:** JitsiMeetElectron unavailable on preload script ([#2031](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2031)) ([1322938](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1322938290e5aca5b9e6c2f335d1ee0f1132b2bc)) ## [3.2.1](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.2.0...3.2.1) (2021-05-13) - ### Bug Fixes -* **about-dialog:** About dialog not displaying version ([#2030](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2030)) ([f82823c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/f82823c36a47eef448ed8a11045c8c0b162f4320)) - - +- **about-dialog:** About dialog not displaying version ([#2030](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2030)) ([f82823c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/f82823c36a47eef448ed8a11045c8c0b162f4320)) # [3.2.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.1.1...3.2.0) (2021-05-12) - ### Bug Fixes -* **deps:** Update electron-builder ([b704680](https://github.com/RocketChat/Rocket.Chat.Electron/commit/b7046803e3394c288fa7ecf280c5221ac8e3d718)) -* User presence not updating ([#2023](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2023)) ([f10f24f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/f10f24fbdbde4f21b2025f36b881066da846bd10)) -* **deps:** Patch dependencies ([1e976cc](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1e976cc09d526ec639fc4ba35a4d1e30cbf31b3a)) -* **deps:** Upgrade build dependencies ([61aee42](https://github.com/RocketChat/Rocket.Chat.Electron/commit/61aee42123b7fbb7a02bc7963318100f2eb7c9f6)) -* Download button size ([#1960](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1960)) ([3424e6d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/3424e6d297b48719c97362c7bb9fdad3cb068183)) -* macOS dock icon size ([#1941](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1941)) ([0eeb8b2](https://github.com/RocketChat/Rocket.Chat.Electron/commit/0eeb8b27c4f2b5b58ce12892aba5254801bb30bc)) -* Missing French translations ([#2014](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2014)) ([929e556](https://github.com/RocketChat/Rocket.Chat.Electron/commit/929e556352fb8043299958097180091f8360f2ec)) -* Stop grabbing focus when dom-ready is emitted; and restore window position correctly when x or y is 0 ([#1954](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1954)) ([c3ca0ef](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c3ca0ef75ac29eb19934cfc8ddf0ad9d15681b3e)), closes [#1934](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1934) [#1934](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1934) -* Tray icon toggle action ([#2006](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2006)) ([4d50b80](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4d50b8085813398c55c51faa238c6f92819c2e7c)), closes [#1700](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1700) [#1935](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1935) -* Update hu.i18n.json ([#1944](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1944)) ([e801f0f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e801f0f9d291fa7daefeefb085d240b13e0c425e)) - +- **deps:** Update electron-builder ([b704680](https://github.com/RocketChat/Rocket.Chat.Electron/commit/b7046803e3394c288fa7ecf280c5221ac8e3d718)) +- User presence not updating ([#2023](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2023)) ([f10f24f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/f10f24fbdbde4f21b2025f36b881066da846bd10)) +- **deps:** Patch dependencies ([1e976cc](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1e976cc09d526ec639fc4ba35a4d1e30cbf31b3a)) +- **deps:** Upgrade build dependencies ([61aee42](https://github.com/RocketChat/Rocket.Chat.Electron/commit/61aee42123b7fbb7a02bc7963318100f2eb7c9f6)) +- Download button size ([#1960](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1960)) ([3424e6d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/3424e6d297b48719c97362c7bb9fdad3cb068183)) +- macOS dock icon size ([#1941](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1941)) ([0eeb8b2](https://github.com/RocketChat/Rocket.Chat.Electron/commit/0eeb8b27c4f2b5b58ce12892aba5254801bb30bc)) +- Missing French translations ([#2014](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2014)) ([929e556](https://github.com/RocketChat/Rocket.Chat.Electron/commit/929e556352fb8043299958097180091f8360f2ec)) +- Stop grabbing focus when dom-ready is emitted; and restore window position correctly when x or y is 0 ([#1954](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1954)) ([c3ca0ef](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c3ca0ef75ac29eb19934cfc8ddf0ad9d15681b3e)), closes [#1934](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1934) [#1934](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1934) +- Tray icon toggle action ([#2006](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2006)) ([4d50b80](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4d50b8085813398c55c51faa238c6f92819c2e7c)), closes [#1700](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1700) [#1935](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1935) +- Update hu.i18n.json ([#1944](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1944)) ([e801f0f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e801f0f9d291fa7daefeefb085d240b13e0c425e)) ### Features -* CLI argument '--start-hidden' to put the app in background on start ([#1407](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1407)) ([223a698](https://github.com/RocketChat/Rocket.Chat.Electron/commit/223a698a73bb0c0157d649189e5f2a68ea369e2e)) -* Flash root window on all platforms ([#1949](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1949)) ([5934c56](https://github.com/RocketChat/Rocket.Chat.Electron/commit/5934c56f7a4551e3b527ba7ffb788648d501b80c)) - +- CLI argument '--start-hidden' to put the app in background on start ([#1407](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1407)) ([223a698](https://github.com/RocketChat/Rocket.Chat.Electron/commit/223a698a73bb0c0157d649189e5f2a68ea369e2e)) +- Flash root window on all platforms ([#1949](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1949)) ([5934c56](https://github.com/RocketChat/Rocket.Chat.Electron/commit/5934c56f7a4551e3b527ba7ffb788648d501b80c)) ### Performance Improvements -* TypeScript's strict mode ([#2015](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2015)) ([1d5e612](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1d5e61216ba91f77a8fc5fa676134cbbe9919af9)) - - +- TypeScript's strict mode ([#2015](https://github.com/RocketChat/Rocket.Chat.Electron/issues/2015)) ([1d5e612](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1d5e61216ba91f77a8fc5fa676134cbbe9919af9)) ## [3.1.1](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.1.0...3.1.1) (2020-12-25) - ### Bug Fixes -* Apply sidebar padding change on macOS ([#1926](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1926)) ([428ccf9](https://github.com/RocketChat/Rocket.Chat.Electron/commit/428ccf984f0154f89c44c1ba596313a74d9c825e)) -* Preload script in sandboxed iframes ([#1925](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1925)) ([15e39f3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/15e39f3153e7cf97d5d36632484c7b241547dc27)) - - +- Apply sidebar padding change on macOS ([#1926](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1926)) ([428ccf9](https://github.com/RocketChat/Rocket.Chat.Electron/commit/428ccf984f0154f89c44c1ba596313a74d9c825e)) +- Preload script in sandboxed iframes ([#1925](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1925)) ([15e39f3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/15e39f3153e7cf97d5d36632484c7b241547dc27)) # [3.1.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.0.7...3.1.0) (2020-12-21) - ### Bug Fixes -* Broken Jitsi's browser check ([#1902](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1902)) ([01fafff](https://github.com/RocketChat/Rocket.Chat.Electron/commit/01fafff822be397f33ebb8250c7b667be66df396)) -* Update hu.i18n.json ([#1886](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1886)) ([d36897e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d36897ee5deb4fe509cd4f36b6b39d4bb6bd47ce)) - +- Broken Jitsi's browser check ([#1902](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1902)) ([01fafff](https://github.com/RocketChat/Rocket.Chat.Electron/commit/01fafff822be397f33ebb8250c7b667be66df396)) +- Update hu.i18n.json ([#1886](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1886)) ([d36897e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d36897ee5deb4fe509cd4f36b6b39d4bb6bd47ce)) ### Features -* Download Manager ([#1700](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1700)) ([ac30ab3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ac30ab396ec66f2437bbe2c3ce8ae5042a2c2b77)) -* Isolated server view sessions ([#1883](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1883)) ([4884d3f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4884d3f066d40f2d97d8649d0a1f8bf5b048f911)) -* Rebranding ([#1884](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1884)) ([12b4ca6](https://github.com/RocketChat/Rocket.Chat.Electron/commit/12b4ca6378689aa967d5b33d5a768561d408c67e)) - - +- Download Manager ([#1700](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1700)) ([ac30ab3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ac30ab396ec66f2437bbe2c3ce8ae5042a2c2b77)) +- Isolated server view sessions ([#1883](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1883)) ([4884d3f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4884d3f066d40f2d97d8649d0a1f8bf5b048f911)) +- Rebranding ([#1884](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1884)) ([12b4ca6](https://github.com/RocketChat/Rocket.Chat.Electron/commit/12b4ca6378689aa967d5b33d5a768561d408c67e)) ## [3.0.7](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.0.6...3.0.7) (2020-11-09) - ### Bug Fixes -* **build:** Force 32 and 64-bit builds for Windows ([e808e7a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e808e7a991490e5079bc3b2be6b9012b1af0988f)) - - +- **build:** Force 32 and 64-bit builds for Windows ([e808e7a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e808e7a991490e5079bc3b2be6b9012b1af0988f)) ## [3.0.6](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.0.5...3.0.6) (2020-11-03) - ### Bug Fixes -* update.json files ignored ([#1861](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1861)) ([d4c3368](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d4c336870c51f518fbadb2f062ba523effba44bf)) - - +- update.json files ignored ([#1861](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1861)) ([d4c3368](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d4c336870c51f518fbadb2f062ba523effba44bf)) ## [3.0.5](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.0.4...3.0.5) (2020-10-28) - ### Bug Fixes -* Apply TouchBar formatting button in focused message box ([#1851](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1851)) ([52ca145](https://github.com/RocketChat/Rocket.Chat.Electron/commit/52ca1451ac5333578520c900e86b86968c69f76c)) -* Bugsnag error reporting ([#1843](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1843)) ([de0fe7f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/de0fe7fc7c95f66d8ed07abae68b8e7fcdf2311d)) -* Displaying warning every time the window is minimized to tray ([#1852](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1852)) ([769a210](https://github.com/RocketChat/Rocket.Chat.Electron/commit/769a210433dccd5c833cdaf74c7c5b276515e890)) -* Dragging and dropping outside content in servers list ([#1853](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1853)) ([1e68f50](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1e68f507dc325d4384aedf6a949edd3dc272d5c6)) -* Select first server on startup ([#1850](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1850)) ([c883d45](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c883d454b6df207b3b4b077c5e880967e9e5e798)) -* System idle threshold ([#1844](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1844)) ([6c6e2a9](https://github.com/RocketChat/Rocket.Chat.Electron/commit/6c6e2a98ca68a796fa685b75b41fd4287ee8ccbb)) - - +- Apply TouchBar formatting button in focused message box ([#1851](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1851)) ([52ca145](https://github.com/RocketChat/Rocket.Chat.Electron/commit/52ca1451ac5333578520c900e86b86968c69f76c)) +- Bugsnag error reporting ([#1843](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1843)) ([de0fe7f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/de0fe7fc7c95f66d8ed07abae68b8e7fcdf2311d)) +- Displaying warning every time the window is minimized to tray ([#1852](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1852)) ([769a210](https://github.com/RocketChat/Rocket.Chat.Electron/commit/769a210433dccd5c833cdaf74c7c5b276515e890)) +- Dragging and dropping outside content in servers list ([#1853](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1853)) ([1e68f50](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1e68f507dc325d4384aedf6a949edd3dc272d5c6)) +- Select first server on startup ([#1850](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1850)) ([c883d45](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c883d454b6df207b3b4b077c5e880967e9e5e798)) +- System idle threshold ([#1844](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1844)) ([6c6e2a9](https://github.com/RocketChat/Rocket.Chat.Electron/commit/6c6e2a98ca68a796fa685b75b41fd4287ee8ccbb)) ## [3.0.4](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.0.3...3.0.4) (2020-10-17) - ### Bug Fixes -* Recover minimized/maximized state on show window when unread count changes ([#1810](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1810)) ([15585f2](https://github.com/RocketChat/Rocket.Chat.Electron/commit/15585f21ff5405b6489edec833fd0fdadd589821)) -* Server in subdirectory ([#1820](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1820)) ([ff8b8a2](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ff8b8a2e0aecc04edbd5279d80023bc99af46523)) -* Unavailable languages in electron.session() ([#1813](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1813)) ([ef32900](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ef329006d323c4ac0089f6e445c33841f735a0d5)) - - +- Recover minimized/maximized state on show window when unread count changes ([#1810](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1810)) ([15585f2](https://github.com/RocketChat/Rocket.Chat.Electron/commit/15585f21ff5405b6489edec833fd0fdadd589821)) +- Server in subdirectory ([#1820](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1820)) ([ff8b8a2](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ff8b8a2e0aecc04edbd5279d80023bc99af46523)) +- Unavailable languages in electron.session() ([#1813](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1813)) ([ef32900](https://github.com/RocketChat/Rocket.Chat.Electron/commit/ef329006d323c4ac0089f6e445c33841f735a0d5)) ## [3.0.3](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.0.2...3.0.3) (2020-10-13) - ### Bug Fixes -* Cannot connect to server on subdirectory ([#1776](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1776)) ([50c8585](https://github.com/RocketChat/Rocket.Chat.Electron/commit/50c8585eddd68b3b62c769b3a501848a92859d0d)) -* Handle certificate errors and handshakes ([#1795](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1795)) ([220f8b1](https://github.com/RocketChat/Rocket.Chat.Electron/commit/220f8b10b897a40453850d4730b348ba4f6066ea)) -* Remove Electron and Chrome versions from User-Agent header ([#1809](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1809)) ([88cbb23](https://github.com/RocketChat/Rocket.Chat.Electron/commit/88cbb23c100a475498c6286c15d4b10e363a1079)) -* Set connection status ([#1800](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1800)) ([738f65c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/738f65cd1348850a2c05e916449fe39428ef1243)) - - +- Cannot connect to server on subdirectory ([#1776](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1776)) ([50c8585](https://github.com/RocketChat/Rocket.Chat.Electron/commit/50c8585eddd68b3b62c769b3a501848a92859d0d)) +- Handle certificate errors and handshakes ([#1795](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1795)) ([220f8b1](https://github.com/RocketChat/Rocket.Chat.Electron/commit/220f8b10b897a40453850d4730b348ba4f6066ea)) +- Remove Electron and Chrome versions from User-Agent header ([#1809](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1809)) ([88cbb23](https://github.com/RocketChat/Rocket.Chat.Electron/commit/88cbb23c100a475498c6286c15d4b10e363a1079)) +- Set connection status ([#1800](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1800)) ([738f65c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/738f65cd1348850a2c05e916449fe39428ef1243)) ## [3.0.2](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.0.1...3.0.2) (2020-10-03) - ### Bug Fixes -* Add extension filters to save download dialog ([#1772](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1772)) ([6c4fdcc](https://github.com/RocketChat/Rocket.Chat.Electron/commit/6c4fdcc894bac52fab875cca7b9146ee3cec40e1)) -* Asynchronous window state changes ([#1773](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1773)) ([0adc73c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/0adc73c2ecfe7010adbb06583b22c55ff172a169)) -* Increase server version range ([#1770](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1770)) ([4ea6fea](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4ea6fea99ed4dc4a11fe64119dc4d284d3a65a5f)) -* Update pt-BR.i18n.json ([#1774](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1774)) ([75f9c97](https://github.com/RocketChat/Rocket.Chat.Electron/commit/75f9c977b6a2ce895899f336dc06b2b9ccb86110)) -* Update tr-TR.i18n.json ([#1763](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1763)) ([61dac3e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/61dac3e1f66eb3908cb0155867cafdf4e6de0fea)) - - +- Add extension filters to save download dialog ([#1772](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1772)) ([6c4fdcc](https://github.com/RocketChat/Rocket.Chat.Electron/commit/6c4fdcc894bac52fab875cca7b9146ee3cec40e1)) +- Asynchronous window state changes ([#1773](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1773)) ([0adc73c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/0adc73c2ecfe7010adbb06583b22c55ff172a169)) +- Increase server version range ([#1770](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1770)) ([4ea6fea](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4ea6fea99ed4dc4a11fe64119dc4d284d3a65a5f)) +- Update pt-BR.i18n.json ([#1774](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1774)) ([75f9c97](https://github.com/RocketChat/Rocket.Chat.Electron/commit/75f9c977b6a2ce895899f336dc06b2b9ccb86110)) +- Update tr-TR.i18n.json ([#1763](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1763)) ([61dac3e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/61dac3e1f66eb3908cb0155867cafdf4e6de0fea)) ## [3.0.1](https://github.com/RocketChat/Rocket.Chat.Electron/compare/3.0.0...3.0.1) (2020-10-02) - ### Bug Fixes -* Server validation ([#1756](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1756)) ([91f7c5a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/91f7c5a2e4ad983c3d81a2383a96ef3b54850f51)) -* Window state loading ([#1758](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1758)) ([12ef49e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/12ef49e4f608324ffb5982ff4435623b43ece6f2)) - - +- Server validation ([#1756](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1756)) ([91f7c5a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/91f7c5a2e4ad983c3d81a2383a96ef3b54850f51)) +- Window state loading ([#1758](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1758)) ([12ef49e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/12ef49e4f608324ffb5982ff4435623b43ece6f2)) # [3.0.0](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.11...3.0.0) (2020-09-30) - ### Bug Fixes -* Add snap connection for camera ([#1484](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1484)) ([a6fe71d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a6fe71d054c08f2215017b5864067c41deb356ad)) -* Bugs related to focused webContents ([#1525](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1525)) ([402fec0](https://github.com/RocketChat/Rocket.Chat.Electron/commit/402fec00b3761fad55d5c570a2f93a1f968b20b0)) -* Command line args handling on packaged apps ([#1522](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1522)) ([cc99b78](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cc99b7830f6636d9714969f879bf2d4bca8a91fc)) -* Context isolation in preload script and `openExternal` handling ([#1710](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1710)) ([c43a8a3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c43a8a31289290e61eacd0e30dba5f14619ee911)) -* Display tray icon balloon when main window is hidden ([#1518](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1518)) ([9291b1e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/9291b1edc7c865c3830adcf964e851db9136114a)) -* Error reporting on Bugsnag ([#1655](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1655)) ([a6746f9](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a6746f9c23fb96d694c8ecdfe5e004d038ba3668)) -* Gracefully exit the app ([#1736](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1736)) ([4d0c28d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4d0c28d61813eb3855ccba3281249bddf5dbc808)) -* Media permissions ([#1740](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1740)) ([1a23d4b](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1a23d4b5fc5758b5ab1a14828ad18ceef7241e65)) -* Menu bar crashing ([#1722](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1722)) ([20bfcbf](https://github.com/RocketChat/Rocket.Chat.Electron/commit/20bfcbfa577887b3067de3d368f9ae083635ed69)) -* Minimum width in split view on OS X is 600px ([#1549](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1549)) ([8630847](https://github.com/RocketChat/Rocket.Chat.Electron/commit/86308478a8131396b35bbbbcdc066fd3c7f2d4e0)) -* Missing tslib at runtime ([#1721](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1721)) ([101957f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/101957f7d20ee2bfc3f8f468a4a80984bb8af433)) -* Opening links with external protocol ([#1709](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1709)) ([a497abc](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a497abc676a0750a73cef0a47a1d4d9f9a4022a2)) -* Remove wrong entitlements on MAS builds ([#1654](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1654)) ([394b19c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/394b19cbede1fce0c3bcde24e9d2bae90a5205ce)) -* Some structural issues ([#1515](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1515)) ([5604281](https://github.com/RocketChat/Rocket.Chat.Electron/commit/56042818d484bb7a68e4787e07c84b113492e936)), closes [#1514](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1514) -* Verify url protocol in window.open ([#1723](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1723)) ([f21cca3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/f21cca3d4302a103dcb1b9fcc32c08b82a78f8a9)) - +- Add snap connection for camera ([#1484](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1484)) ([a6fe71d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a6fe71d054c08f2215017b5864067c41deb356ad)) +- Bugs related to focused webContents ([#1525](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1525)) ([402fec0](https://github.com/RocketChat/Rocket.Chat.Electron/commit/402fec00b3761fad55d5c570a2f93a1f968b20b0)) +- Command line args handling on packaged apps ([#1522](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1522)) ([cc99b78](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cc99b7830f6636d9714969f879bf2d4bca8a91fc)) +- Context isolation in preload script and `openExternal` handling ([#1710](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1710)) ([c43a8a3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c43a8a31289290e61eacd0e30dba5f14619ee911)) +- Display tray icon balloon when main window is hidden ([#1518](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1518)) ([9291b1e](https://github.com/RocketChat/Rocket.Chat.Electron/commit/9291b1edc7c865c3830adcf964e851db9136114a)) +- Error reporting on Bugsnag ([#1655](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1655)) ([a6746f9](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a6746f9c23fb96d694c8ecdfe5e004d038ba3668)) +- Gracefully exit the app ([#1736](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1736)) ([4d0c28d](https://github.com/RocketChat/Rocket.Chat.Electron/commit/4d0c28d61813eb3855ccba3281249bddf5dbc808)) +- Media permissions ([#1740](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1740)) ([1a23d4b](https://github.com/RocketChat/Rocket.Chat.Electron/commit/1a23d4b5fc5758b5ab1a14828ad18ceef7241e65)) +- Menu bar crashing ([#1722](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1722)) ([20bfcbf](https://github.com/RocketChat/Rocket.Chat.Electron/commit/20bfcbfa577887b3067de3d368f9ae083635ed69)) +- Minimum width in split view on OS X is 600px ([#1549](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1549)) ([8630847](https://github.com/RocketChat/Rocket.Chat.Electron/commit/86308478a8131396b35bbbbcdc066fd3c7f2d4e0)) +- Missing tslib at runtime ([#1721](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1721)) ([101957f](https://github.com/RocketChat/Rocket.Chat.Electron/commit/101957f7d20ee2bfc3f8f468a4a80984bb8af433)) +- Opening links with external protocol ([#1709](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1709)) ([a497abc](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a497abc676a0750a73cef0a47a1d4d9f9a4022a2)) +- Remove wrong entitlements on MAS builds ([#1654](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1654)) ([394b19c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/394b19cbede1fce0c3bcde24e9d2bae90a5205ce)) +- Some structural issues ([#1515](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1515)) ([5604281](https://github.com/RocketChat/Rocket.Chat.Electron/commit/56042818d484bb7a68e4787e07c84b113492e936)), closes [#1514](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1514) +- Verify url protocol in window.open ([#1723](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1723)) ([f21cca3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/f21cca3d4302a103dcb1b9fcc32c08b82a78f8a9)) ### Features -* Add Hungarian translation ([#1554](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1554)) ([f2345aa](https://github.com/RocketChat/Rocket.Chat.Electron/commit/f2345aadd896c979b8821404e049273941b53c02)) -* Add new locale for Ukrainian ([#1559](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1559)) ([26e6c48](https://github.com/RocketChat/Rocket.Chat.Electron/commit/26e6c48044af9979c3f8f7bb18f707682af26b2c)) -* Add Polish translation ([#1586](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1586)) ([a2f4885](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a2f4885ba5c0ff995f3098e5280bc3e1f8d8d2dc)) -* Added option to disable GPU acceleration ([#1541](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1541)) ([18c850c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/18c850cc4ca175af17b9a04b413f4fc73fc4c76a)) -* Deep links ([#1726](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1726)) ([fda4bad](https://github.com/RocketChat/Rocket.Chat.Electron/commit/fda4badc06938e67a68cbc4a5857dcbd4e6e79b9)) -* Embedded spell checking dictionaries ([#1523](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1523)) ([c897582](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c897582a6df338eba1d0bc0a675687f66eb3cd5d)) -* Menu bar as components ([#1512](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1512)) ([67172f0](https://github.com/RocketChat/Rocket.Chat.Electron/commit/67172f0930ad25cf055d8c301e9039c99e6af73a)) -* MSI installer ([#1734](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1734)) ([64ab2f3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/64ab2f3d9d602fba282853a045d1e11148838d8c)) -* New "Add Server" layout ([#1738](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1738)) ([e0183d3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e0183d3db0f0509450b927cc1650a6719d77b4bd)) -* Select client certificate dialog ([#1511](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1511)) ([68e79d8](https://github.com/RocketChat/Rocket.Chat.Electron/commit/68e79d8bfbcfa231b2ed2cf64ede24dcf3d5f079)) -* Use current server's favicon as window/taskbar icon ([#1720](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1720)) ([c3d53c6](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c3d53c6813185c1652d39a25a0a56336858e0a3a)) - +- Add Hungarian translation ([#1554](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1554)) ([f2345aa](https://github.com/RocketChat/Rocket.Chat.Electron/commit/f2345aadd896c979b8821404e049273941b53c02)) +- Add new locale for Ukrainian ([#1559](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1559)) ([26e6c48](https://github.com/RocketChat/Rocket.Chat.Electron/commit/26e6c48044af9979c3f8f7bb18f707682af26b2c)) +- Add Polish translation ([#1586](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1586)) ([a2f4885](https://github.com/RocketChat/Rocket.Chat.Electron/commit/a2f4885ba5c0ff995f3098e5280bc3e1f8d8d2dc)) +- Added option to disable GPU acceleration ([#1541](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1541)) ([18c850c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/18c850cc4ca175af17b9a04b413f4fc73fc4c76a)) +- Deep links ([#1726](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1726)) ([fda4bad](https://github.com/RocketChat/Rocket.Chat.Electron/commit/fda4badc06938e67a68cbc4a5857dcbd4e6e79b9)) +- Embedded spell checking dictionaries ([#1523](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1523)) ([c897582](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c897582a6df338eba1d0bc0a675687f66eb3cd5d)) +- Menu bar as components ([#1512](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1512)) ([67172f0](https://github.com/RocketChat/Rocket.Chat.Electron/commit/67172f0930ad25cf055d8c301e9039c99e6af73a)) +- MSI installer ([#1734](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1734)) ([64ab2f3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/64ab2f3d9d602fba282853a045d1e11148838d8c)) +- New "Add Server" layout ([#1738](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1738)) ([e0183d3](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e0183d3db0f0509450b927cc1650a6719d77b4bd)) +- Select client certificate dialog ([#1511](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1511)) ([68e79d8](https://github.com/RocketChat/Rocket.Chat.Electron/commit/68e79d8bfbcfa231b2ed2cf64ede24dcf3d5f079)) +- Use current server's favicon as window/taskbar icon ([#1720](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1720)) ([c3d53c6](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c3d53c6813185c1652d39a25a0a56336858e0a3a)) ### Performance Improvements -* Improve away detection ([#1542](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1542)) ([6449554](https://github.com/RocketChat/Rocket.Chat.Electron/commit/6449554dec8c75338e83606835d4875d5e3f128b)) -* Notifications on main process ([#1675](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1675)) ([062b4f9](https://github.com/RocketChat/Rocket.Chat.Electron/commit/062b4f958c942f2dc4eee9066ec8c28aa93db705)) +- Improve away detection ([#1542](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1542)) ([6449554](https://github.com/RocketChat/Rocket.Chat.Electron/commit/6449554dec8c75338e83606835d4875d5e3f128b)) +- Notifications on main process ([#1675](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1675)) ([062b4f9](https://github.com/RocketChat/Rocket.Chat.Electron/commit/062b4f958c942f2dc4eee9066ec8c28aa93db705)) ## [2.17.11](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.10...2.17.11) (2020-07-21) - ### Bug Fixes -* No notification when the avatar icon comes from a relative URL ([#1662](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1662)) ([bad1562](https://github.com/RocketChat/Rocket.Chat.Electron/commit/bad156246bab4b9a0c478693dc397b9548177a6c)) -* Remove wrong entitlements on MAS builds ([#1654](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1654)) ([76c19ee](https://github.com/RocketChat/Rocket.Chat.Electron/commit/76c19eedac827f9833b5090eda4965fa438b9e25)) - - +- No notification when the avatar icon comes from a relative URL ([#1662](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1662)) ([bad1562](https://github.com/RocketChat/Rocket.Chat.Electron/commit/bad156246bab4b9a0c478693dc397b9548177a6c)) +- Remove wrong entitlements on MAS builds ([#1654](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1654)) ([76c19ee](https://github.com/RocketChat/Rocket.Chat.Electron/commit/76c19eedac827f9833b5090eda4965fa438b9e25)) ## [2.17.10](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.9...2.17.10) (2020-07-14) - ### Bug Fixes -* Patch Electron ([d53ec40](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d53ec4096c8fd9004821b13a755c2c5e818aa9c6)) - - +- Patch Electron ([d53ec40](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d53ec4096c8fd9004821b13a755c2c5e818aa9c6)) ## [2.17.9](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.8...2.17.9) (2020-03-12) - ### Bug Fixes -* Update provision profile ([617c964](https://github.com/RocketChat/Rocket.Chat.Electron/commit/617c964dacde3738d86d28779f01fa0c9208b6b3)) - - +- Update provision profile ([617c964](https://github.com/RocketChat/Rocket.Chat.Electron/commit/617c964dacde3738d86d28779f01fa0c9208b6b3)) ## [2.17.8](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.7...2.17.8) (2020-03-01) - ### Bug Fixes -* Allow MacOS users to browse for spell checking dictionaries ([3c75bfe](https://github.com/RocketChat/Rocket.Chat.Electron/commit/3c75bfe2270e5c2e434f85a27a717c875a63f9b0)) -* Patch Electron for MAS builds ([e9cd8ad](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e9cd8ad4b9cf0aeb5011189f6cedb24b71548579)) - - +- Allow MacOS users to browse for spell checking dictionaries ([3c75bfe](https://github.com/RocketChat/Rocket.Chat.Electron/commit/3c75bfe2270e5c2e434f85a27a717c875a63f9b0)) +- Patch Electron for MAS builds ([e9cd8ad](https://github.com/RocketChat/Rocket.Chat.Electron/commit/e9cd8ad4b9cf0aeb5011189f6cedb24b71548579)) ## [2.17.7](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.6...2.17.7) (2020-02-11) - ### Bug Fixes -* Spell checking dictionaries files encoded as UTF-8 ([18b9524](https://github.com/RocketChat/Rocket.Chat.Electron/commit/18b95241a9df47751c5d67a55c5e2cf73e2763ca)) - - +- Spell checking dictionaries files encoded as UTF-8 ([18b9524](https://github.com/RocketChat/Rocket.Chat.Electron/commit/18b95241a9df47751c5d67a55c5e2cf73e2763ca)) ## [2.17.6](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.5...2.17.6) (2020-02-11) - ### Bug Fixes -* Rollback to plain-text Hunspell dictionaries ([#1514](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1514)) ([0f16d32](https://github.com/RocketChat/Rocket.Chat.Electron/commit/0f16d32845faf5b9a9b475db3c34420d982cb6bc)) - - +- Rollback to plain-text Hunspell dictionaries ([#1514](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1514)) ([0f16d32](https://github.com/RocketChat/Rocket.Chat.Electron/commit/0f16d32845faf5b9a9b475db3c34420d982cb6bc)) ## [2.17.5](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.4...2.17.5) (2020-02-04) - ### Bug Fixes -* Handle unset enabled dictionaries ([2e3f203](https://github.com/RocketChat/Rocket.Chat.Electron/commit/2e3f20397f8b39d70e7e9261c20145b9e6987e91)) -* Ignore Hunspell dictionaries on MacOS ([cccca77](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cccca775e40c101798a870ee3f2ced79fdee20a3)) - - +- Handle unset enabled dictionaries ([2e3f203](https://github.com/RocketChat/Rocket.Chat.Electron/commit/2e3f20397f8b39d70e7e9261c20145b9e6987e91)) +- Ignore Hunspell dictionaries on MacOS ([cccca77](https://github.com/RocketChat/Rocket.Chat.Electron/commit/cccca775e40c101798a870ee3f2ced79fdee20a3)) ## [2.17.4](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.3...2.17.4) (2020-02-04) - ### Bug Fixes -* Broken spell checking dictionary selection ([c11600c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c11600ca509c8c1806c734b189a33981b5ba002e)) - - +- Broken spell checking dictionary selection ([c11600c](https://github.com/RocketChat/Rocket.Chat.Electron/commit/c11600ca509c8c1806c734b189a33981b5ba002e)) ## [2.17.3](https://github.com/RocketChat/Rocket.Chat.Electron/compare/2.17.2...2.17.3) (2020-01-30) - ### Bug Fixes -* Screen sharing in Jitsi ([#1486](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1486)) ([d7d463a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d7d463ae3cef91410525eb42d1333e3e18996d34)) - - +- Screen sharing in Jitsi ([#1486](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1486)) ([d7d463a](https://github.com/RocketChat/Rocket.Chat.Electron/commit/d7d463ae3cef91410525eb42d1333e3e18996d34)) -## 2.17.2 (2019-12-19) +## 2.17.2 (2019-12-19) ### Bug Fixes - [#1450](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1450) Windows select boxes - [#1447](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1447) TouchBar buttons - - -## 2.17.1 (2019-12-10) +## 2.17.1 (2019-12-10) ### Bug Fixes - [#1436](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1436) Disable hardenedRuntime - - -# 2.17.0 (2019-12-02) +# 2.17.0 (2019-12-02) ### Bug Fixes @@ -505,7 +377,6 @@ - [#1419](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1419) Preload script issues - [#1381](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1381) Update dialog events - ### Improvements - [#1418](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1418) Focus main window on second app instance event @@ -514,56 +385,46 @@ - [#1380](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1380) Notifications on Gnome - [#1392](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1392) Update to Electron 7 - Others - [#1401](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1401) Update TouchBar API usage - - -## 2.16.2 (2019-11-07) +## 2.16.2 (2019-11-07) ### Bug Fixes - [#1381](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1381) Update dialog events - ### Improvements - [#1380](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1380) Notifications on Gnome - - -## 2.16.1 (2019-11-04) +## 2.16.1 (2019-11-04) ### Bug Fixes - [#1365](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1365) powerMonitor API usage - [#1366](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1366) Spellchecker setup - - -# 2.16.0 (2019-10-11) +# 2.16.0 (2019-10-11) ### Bug Fixes - [#1275](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1275) Ignore spurious did-fail load, fixes [#1273](https://github.com/RocketChat/Rocket.Chat.Electron/issues/1273). - ### Improvements - [#1325](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1325) Add console warning to self-XSS - [#1312](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1312) Fixes and updates French translations - Others @@ -579,20 +440,17 @@ - [#1308](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1308) Bump mixin-deep from 1.3.1 to 1.3.2 - - -## 2.15.5 (2019-08-09) +## 2.15.5 (2019-08-09) ### Hot fix - Bugsnag dependency error - -## 2.15.4 (2019-08-08) +## 2.15.4 (2019-08-08) Others @@ -603,29 +461,24 @@ - [#1196](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1196) Safely compute initials for server name on sidebar - ### Bug Fixes - [#1286](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1286) "Show window on unread changed" not working - [#1285](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1285) Remove missing variable reference - [#1264](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1264) Resolves issue with timing out when Rocket.Chat is in the background … - - -## 2.15.3 (2019-04-30) +## 2.15.3 (2019-04-30) ### Bug Fixes - [#1198](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1198) Add a module to handle deep links following the documentation - [#1196](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1196) Safely compute initials for server name on sidebars - - -## 2.15.2 (2019-04-16) +## 2.15.2 (2019-04-16) ### Bug Fixes @@ -635,22 +488,18 @@ - [#1125](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1125) Sidebar and badges - [#1187](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1187) Update crashes when host is unreachable - ### New Features - [#1157](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1157) Add Traditional Chinese translation - - -## 2.15.1 (2019-03-13) +## 2.15.1 (2019-03-13) ### Improvements - [#1117](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1117) Updated Japanese translation - ### Bug Fixes - [#1132](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1132) Apply memoization to spell checking @@ -662,11 +511,9 @@ - [#1123](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1123) Update button - [#1115](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1115) Auto reload server - - -# 2.15.0 (2019-02-24) +# 2.15.0 (2019-02-24) ### Bug Fixes @@ -674,7 +521,6 @@ - [#1099](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1099) Updates preloads scripts to be compatible with Rocket.Chat >0.74.0 - [#1101](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1101) Use Electron notifications - ### Improvements - [#1096](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1096) i18next @@ -682,7 +528,6 @@ - [#1045](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1045) Pages and preload script changes - [#1076](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1076) Remove update-remind-later-dialog - Others @@ -691,7 +536,6 @@ - [#1080](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1080) Fix remember window state on load - ### New Features - [#919](https://github.com/RocketChat/Rocket.Chat.Electron/pull/919) Add "save image" to context menu @@ -699,43 +543,35 @@ - [#995](https://github.com/RocketChat/Rocket.Chat.Electron/pull/995) Automatic reload on error page - [#1044](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1044) Support for MacBooks Touch Bar - ### BREAKING CHANGES - [#1036](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1036) Update dependencies - - -## 2.14.7 (2019-01-09) +## 2.14.7 (2019-01-09) ### Bug Fixes - Main window destroyed when closing on MacOS - - -## 2.14.6 (2018-12-06) +## 2.14.6 (2018-12-06) ### Bug Fixes - Add strings specifying why some permissions are needed in MacOS - Fix servers.json path resolution - - -## 2.14.5 (2018-12-04) +## 2.14.5 (2018-12-04) ### Improvements - [#1010](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1010) Remove unused modules - ### Bug Fixes - [#1026](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1026) Add additional condition for option "Show on unread" @@ -743,11 +579,9 @@ - [#1025](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1025) Remove dependencies related to the npm's event-stream incident - [#1019](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1019) Rollback notifications for Windows - - -## 2.14.4 (2018-11-21) +## 2.14.4 (2018-11-21) ### Bug Fixes @@ -756,28 +590,23 @@ - [#1000](https://github.com/RocketChat/Rocket.Chat.Electron/pull/1000) Notification errors - [#990](https://github.com/RocketChat/Rocket.Chat.Electron/pull/990) Speed up servers.json loading - Others - [#987](https://github.com/RocketChat/Rocket.Chat.Electron/pull/987) Update Russian translation - - -## 2.14.3 (2018-11-14) +## 2.14.3 (2018-11-14) ### Bug Fixes - [#978](https://github.com/RocketChat/Rocket.Chat.Electron/pull/978) Fallback notifications for Windows 7 - - -## 2.14.2 (2018-11-13) +## 2.14.2 (2018-11-13) ### Bug Fixes @@ -787,28 +616,23 @@ - [#949](https://github.com/RocketChat/Rocket.Chat.Electron/pull/949) Reset app data under Windows - [#959](https://github.com/RocketChat/Rocket.Chat.Electron/pull/959) System tray, dock, task bar and main window issues - ### Improvements - [#968](https://github.com/RocketChat/Rocket.Chat.Electron/pull/968) Node 11 support - - -## 2.14.1 (2018-10-25) +## 2.14.1 (2018-10-25) ### New Features - [#914](https://github.com/RocketChat/Rocket.Chat.Electron/pull/914) Tray tooltip - ### Improvements - [#933](https://github.com/RocketChat/Rocket.Chat.Electron/pull/933) App (main) page - [#932](https://github.com/RocketChat/Rocket.Chat.Electron/pull/932) Report issue menu item links to desktop app repository - ### Bug Fixes - [#936](https://github.com/RocketChat/Rocket.Chat.Electron/pull/936) Autoupdate in MacOS @@ -818,11 +642,9 @@ - [#922](https://github.com/RocketChat/Rocket.Chat.Electron/pull/922) Server icon not displayed on sidebar if server url ending with a trailing slash - [#941](https://github.com/RocketChat/Rocket.Chat.Electron/pull/941) Window closing behavior for Linux environments without a system tray - - -# 2.14.0 (2018-10-11) +# 2.14.0 (2018-10-11) ### New Features @@ -835,7 +657,6 @@ - [#887](https://github.com/RocketChat/Rocket.Chat.Electron/pull/887) Update ESLint rules following Rocket.Chat guidelines - ### Bug Fixes - [#889](https://github.com/RocketChat/Rocket.Chat.Electron/pull/889) About dialog @@ -846,25 +667,21 @@ - [#912](https://github.com/RocketChat/Rocket.Chat.Electron/pull/912) Condition to quit on window close - [#913](https://github.com/RocketChat/Rocket.Chat.Electron/pull/913) Show window on second instance running - Others - [#916](https://github.com/RocketChat/Rocket.Chat.Electron/pull/916) Change back and forward shortcuts - - -## 2.13.3 (2018-09-18) +## 2.13.3 (2018-09-18) ### Improvements - [#881](https://github.com/RocketChat/Rocket.Chat.Electron/pull/881) End-to-end tests - [#882](https://github.com/RocketChat/Rocket.Chat.Electron/pull/882) Set new DMG background - ### Bug Fixes - [#884](https://github.com/RocketChat/Rocket.Chat.Electron/pull/884) Show tray icon status again @@ -872,13 +689,12 @@ - [#880](https://github.com/RocketChat/Rocket.Chat.Electron/pull/880) Tray icon toggle crashes in MacOS - [#869](https://github.com/RocketChat/Rocket.Chat.Electron/pull/869) Window state errors on save when antivirus software is present - - -## 2.13.2 (2018-09-10) +## 2.13.2 (2018-09-10) ### Bug Fixes + - Dependencies updated - Window state persistency triggering redefined - AppId for Windows setups recovered @@ -886,29 +702,25 @@ - Fixed multiple issues in provisioning profiles and entitlements for MacOS builds -## 2.13.1 (2018-08-30) +## 2.13.1 (2018-08-30) Fixes for MacOS and Windows builds. - - -# 2.13.0 (2018-08-27) +# 2.13.0 (2018-08-27) ### New Features - [#838](https://github.com/RocketChat/Rocket.Chat.Electron/pull/838) Russian translation - [#837](https://github.com/RocketChat/Rocket.Chat.Electron/pull/837) Auto update fixes and settings enforcement - ### Improvements - [#821](https://github.com/RocketChat/Rocket.Chat.Electron/pull/821) Always force download of uploaded files - [#824](https://github.com/RocketChat/Rocket.Chat.Electron/pull/824) Background process rearranged - ### Bug Fixes - [#817](https://github.com/RocketChat/Rocket.Chat.Electron/pull/817) Disabled update in builds for Mac App Store @@ -917,20 +729,17 @@ Fixes for MacOS and Windows builds. - [#835](https://github.com/RocketChat/Rocket.Chat.Electron/pull/835) Support On-Premise Jitsi screen sharing - [#818](https://github.com/RocketChat/Rocket.Chat.Electron/pull/818) Fixed dock icon badge counter showing zero - - + ## 2.12.1 (2018-08-14) ### Bug Fixes - macOS dock badge fixed - - -# 2.12.0 (2018-08-04) +# 2.12.0 (2018-08-04) ### New Features @@ -939,31 +748,26 @@ Fixes for MacOS and Windows builds. - [#760](https://github.com/RocketChat/Rocket.Chat.Electron/pull/760) Notification on app icon - [#776](https://github.com/RocketChat/Rocket.Chat.Electron/pull/776) Updated with new logo - ### Bug Fixes - [#778](https://github.com/RocketChat/Rocket.Chat.Electron/pull/778) Add snap build back - [#791](https://github.com/RocketChat/Rocket.Chat.Electron/pull/791) Mac osx menubar color - Others - [#785](https://github.com/RocketChat/Rocket.Chat.Electron/pull/785) Replace last couple of icons - - -# 2.11.0 (2018-06-10) +# 2.11.0 (2018-06-10) ### New Features - [#562](https://github.com/RocketChat/Rocket.Chat.Electron/pull/562) Add option to install language dictionaries - [#691](https://github.com/RocketChat/Rocket.Chat.Electron/pull/691) Add german translation - ### Bug Fixes - [#670](https://github.com/RocketChat/Rocket.Chat.Electron/pull/670) Add & to menu items to avoid alt-shift menu popup @@ -971,35 +775,33 @@ Fixes for MacOS and Windows builds. - [#742](https://github.com/RocketChat/Rocket.Chat.Electron/pull/742) cve 2018 1000136 - [#710](https://github.com/RocketChat/Rocket.Chat.Electron/pull/710) recompress PNG files lossless - - + ## 2.10.5 (2018-02-07) ### Bug Fixes -- Dependencies updated - +- Dependencies updated + ## 2.10.4 (2018-02-05) ### Bug Fixes -- macOS bundle version fixed - +- macOS bundle version fixed + ## 2.10.3 (2018-02-02) ### Bug Fixes -- Dependencies updated - +- Dependencies updated -## 2.10.2 (2018-01-26) +## 2.10.2 (2018-01-26) Others @@ -1008,34 +810,35 @@ Fixes for MacOS and Windows builds. - [#520](https://github.com/RocketChat/Rocket.Chat.Electron/pull/520) npm deps update - - + # 2.10.1 (2017-11-09) ### Bug Fixes -- [#597](https://github.com/RocketChat/Rocket.Chat.Electron/pull/597) Fix MacOS dmg build +- [#597](https://github.com/RocketChat/Rocket.Chat.Electron/pull/597) Fix MacOS dmg build + # 2.10.0 (2017-10-27) ### New Features + - [#552](https://github.com/RocketChat/Rocket.Chat.Electron/pull/552) Add context menu option for links - [#556](https://github.com/RocketChat/Rocket.Chat.Electron/pull/556) Sidebar redesign and dynamic background color - [#539](https://github.com/RocketChat/Rocket.Chat.Electron/pull/539) Adds drag and drop for servers in the sidebar - [#533](https://github.com/RocketChat/Rocket.Chat.Electron/pull/533) New shortcut for moving back/forward between rooms ### Bug Fixes + - [#521](https://github.com/RocketChat/Rocket.Chat.Electron/pull/521) Fixes OSX build for AppStore - [#546](https://github.com/RocketChat/Rocket.Chat.Electron/pull/546) Fixed wrong window size on loading screen - [#532](https://github.com/RocketChat/Rocket.Chat.Electron/pull/532) Restores the help menu on Windows and Linux - [#526](https://github.com/RocketChat/Rocket.Chat.Electron/pull/526) Fix notifications not opening the correct room - -# 2.9.0 (2017-08-23) +# 2.9.0 (2017-08-23) ### New Features @@ -1043,7 +846,6 @@ Fixes for MacOS and Windows builds. - [#490](https://github.com/RocketChat/Rocket.Chat.Electron/pull/490) Default servers improvements - [#509](https://github.com/RocketChat/Rocket.Chat.Electron/pull/509) Add missing Services menu in application menu on macOS - ### Bug Fixes - [#494](https://github.com/RocketChat/Rocket.Chat.Electron/pull/494) Adding ESLint and fixing lint errors @@ -1052,10 +854,9 @@ Fixes for MacOS and Windows builds. - [#464](https://github.com/RocketChat/Rocket.Chat.Electron/pull/464) Remove duplicate notification on windows 7 - [#453](https://github.com/RocketChat/Rocket.Chat.Electron/pull/453) Read update settings from install location - -# 2.8.0 (2017-05-17) +# 2.8.0 (2017-05-17) ### New Features @@ -1066,19 +867,15 @@ Fixes for MacOS and Windows builds. - [#440](https://github.com/RocketChat/Rocket.Chat.Electron/pull/440) Fix bug on some OS versions on about window, closes [#427](https://github.com/RocketChat/Rocket.Chat.Electron/issues/427) - [#445](https://github.com/RocketChat/Rocket.Chat.Electron/pull/445) Fix bug when closing app in fullscreen - - - -# 2.7.0 (2017-04-26) +# 2.7.0 (2017-04-26) ### New Features - [#411](https://github.com/RocketChat/Rocket.Chat.Electron/pull/411) Auto update when new version is released - [#423](https://github.com/RocketChat/Rocket.Chat.Electron/pull/423) Open host from add new server page if it exists - ### Bug Fixes - [#417](https://github.com/RocketChat/Rocket.Chat.Electron/pull/417) Don't open dev tools on about, and show message when no updates @@ -1086,17 +883,17 @@ Fixes for MacOS and Windows builds. - [#426](https://github.com/RocketChat/Rocket.Chat.Electron/pull/426) Reduce drag region to fix manual scroll - [#415](https://github.com/RocketChat/Rocket.Chat.Electron/pull/415) Updated README with servers.json instructions - + ## 2.6.1 (2017-04-04) ### Bug Fixes -- [#412](https://github.com/RocketChat/Rocket.Chat.Electron/pull/412) Fix bug with highlighting text & drag region on macOS +- [#412](https://github.com/RocketChat/Rocket.Chat.Electron/pull/412) Fix bug with highlighting text & drag region on macOS -# 2.6.0 (2017-03-29) +# 2.6.0 (2017-03-29) ### Bug Fixes @@ -1104,7 +901,6 @@ Fixes for MacOS and Windows builds. - [#390](https://github.com/RocketChat/Rocket.Chat.Electron/pull/390) Fix speed issues with spellcheck on windows - [#391](https://github.com/RocketChat/Rocket.Chat.Electron/pull/391) Only show reload screen if main webview error - Others @@ -1299,14 +1095,14 @@ Fixes for MacOS and Windows builds. ### Updated Electron to 0.31.0 -- New demo URL +- New demo URL ## 0.5.0 - 2015-Jul-27 ### Upgrade electron to 0.30.2 - Better error message -- Disabled _tray.setTitle(title); until it can be optional +- Disabled \_tray.setTitle(title); until it can be optional - Fix crash when closing app from try in OS X - Fixed oAuth logins - Increase start window size diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..b32549f531 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,3 @@ +# Claude Instructions + +See [AGENTS.md](./AGENTS.md) for development guidelines. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 13c93becc2..0b4e9380c3 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,21 +14,21 @@ and orientation. Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or +- The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities diff --git a/LICENSE b/LICENSE index 41deab4110..00f22618cf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2025 Rocket.Chat Technologies Corp. +Copyright (c) 2015-2026 Rocket.Chat Technologies Corp. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 63a65091e8..a906183bb9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # Rocket.Chat Desktop App -[](https://travis-ci.org/RocketChat/Rocket.Chat.Electron) -[](https://ci.appveyor.com/project/RocketChat/rocket-chat-electron) -[](https://www.codacy.com/app/tassoevan/Rocket.Chat.Electron?utm_source=github.com&utm_medium=referral&utm_content=RocketChat/Rocket.Chat.Electron&utm_campaign=Badge_Grade) -[](https://david-dm.org/RocketChat/Rocket.Chat.Electron) +[](https://github.com/RocketChat/Rocket.Chat.Electron/actions) + [](https://github.com/RocketChat/Rocket.Chat.Electron/releases/latest)  @@ -14,13 +12,25 @@ using [Electron][]. --- +## Supported Platforms + +| Platform | Minimum Version | Architectures | Formats | +| -------- | --------------------------- | ------------------------------- | -------------------------------- | +| Windows | 10 | x64, ia32, arm64 | NSIS, MSI | +| macOS | 12 (Monterey) | Universal (x64 + Apple Silicon) | DMG, PKG, ZIP | +| Linux | Ubuntu 22.04+ or equivalent | x64 | AppImage, deb, rpm, snap, tar.gz | + +--- + ## Engage with us ### Share your story -We’d love to hear about [your experience][] and potentially feature it on our + +We'd love to hear about [your experience][] and potentially feature it on our [Blog][]. ### Subscribe for Updates + Once a month our marketing team releases an email update with news about product releases, company related topics, events and use cases. [Sign Up!][] @@ -30,7 +40,9 @@ releases, company related topics, events and use cases. [Sign Up!][] You can download the latest version from the [Releases][] page. -[](https://snapcraft.io/rocketchat-desktop) + + + ## Install @@ -43,7 +55,7 @@ add the options below: - `/S` - Silent install - `/allusers` - Install for all users (requires admin) -- `/currentuser` - Install only the for current user (default) +- `/currentuser` - Install only for the current user (default) - `/disableAutoUpdates` - Disable automatic updates ## Development @@ -53,9 +65,8 @@ add the options below: Prerequisites: - [Git](http://git-scm.com/book/en/v2/Getting-Started-Installing-Git) -- [Node.js](https://nodejs.org) -- [node-gyp](https://github.com/nodejs/node-gyp#installation) -- [Yarn](http://yarnpkg.com/) is recommended instead of npm. +- [Node.js](https://nodejs.org) >= 24.11.1 +- [Yarn](http://yarnpkg.com/) >= 4.0.2 Now just clone and start the app: @@ -78,7 +89,7 @@ full, runnable application. ### TypeScript Following the [ongoing changes in Rocket.Chat codebase][], the app was -rewritten in TypeScript 4 to address issues regarding maintainability. +rewritten in TypeScript 5 to address issues regarding maintainability. ### The build pipeline @@ -88,7 +99,7 @@ for your code: - `src/main.ts`, the script running at the main Electron process, orchestrating the whole application; -- `src/rootWindow.ts`, the script that renders the UI of the *root window*, the +- `src/rootWindow.ts`, the script that renders the UI of the _root window_, the app's main window; - and `src/preload.ts`, which runs in a privileged mode to connect the app and @@ -102,35 +113,22 @@ distributable app. ### Troubleshooting -#### node-gyp - -Follow the installation instruction on [node-gyp readme][]. - -#### Ubuntu +#### Ubuntu/Debian -You will need to install the following packages: +You may need to install the following packages for development: ```sh -build-essential -libevas-dev -libxss-dev +sudo apt install build-essential libxss-dev ``` -#### Fedora +#### Fedora/RHEL -You will need to install the following packages: +You may need to install the following packages for development: ```sh -libX11 -libXScrnSaver-devel -gcc-c++ +sudo dnf install libX11-devel libXScrnSaver-devel gcc-c++ ``` -#### Windows 7 - -On Windows 7 you may have to follow option 2 of the [node-gyp install guide] -and install Visual Studio. - ### Testing #### Unit tests @@ -218,61 +216,52 @@ a single server mode. #### The settings that can be overridden are: -| Setting | Description | -| ----------- | ----------- | -| `"isReportEnabled": true,` | Sets if the bugs will be reported to developers. -| `"isInternalVideoChatWindowEnabled": true,` | Sets the video calls will be opened in an internal window. -| `"isFlashFrameEnabled": true,` | Sets if the flash frame will be enabled. -| `"isMinimizeOnCloseEnabled": false,` | Sets if the app will be minimized on close. -|`"doCheckForUpdatesOnStartup": true,` | Sets if the app will check for updates on startup. -| `"isMenuBarEnabled": true,` | Sets if the menu bar will be enabled. -|`"isTrayIconEnabled": true,` | Enables Tray Icon, the app will be hidden to the tray on close. Overrides `"isMinimizeOnCloseEnabled"` -|`"isUpdatingEnabled": true,` | Sets if the app can be updated by the user. -|`"isAddNewServersEnabled": true,` | Sets if the user can add new servers. +| Setting | Description | +| ------------------------------------------ | ------------------------------------------------------------------------------------------------------ | +| `"isReportEnabled": true` | Sets if the bugs will be reported to developers. | +| `"isInternalVideoChatWindowEnabled": true` | Sets if video calls will be opened in an internal window. | +| `"isFlashFrameEnabled": true` | Sets if the flash frame will be enabled. | +| `"isMinimizeOnCloseEnabled": false` | Sets if the app will be minimized on close. | +| `"doCheckForUpdatesOnStartup": true` | Sets if the app will check for updates on startup. | +| `"isMenuBarEnabled": true` | Sets if the menu bar will be enabled. | +| `"isTrayIconEnabled": true` | Enables Tray Icon, the app will be hidden to the tray on close. Overrides `"isMinimizeOnCloseEnabled"` | +| `"isUpdatingEnabled": true` | Sets if the app can be updated by the user. | +| `"isAddNewServersEnabled": true` | Sets if the user can add new servers. | ##### Single server mode + If the setting `"isAddNewServersEnabled": false` is set, the user will not be able to add new servers. The buttons and shortcuts will be disabled. Then you will have to add the server to the `servers.json` file. -With this, you can create a single server mode or just don't let the user to add new servers by his own. +With this, you can create a single server mode or just don't let the user add new servers on their own. ##### Example configuration + `overridden-settings.json` file: - { - "isTrayIconEnabled": false, - "isMinimizeOnCloseEnabled": false - } -When `isTrayIconEnabled` is enabled, the app will be hidden on close. -When `isMinimizeOnCloseEnabled` is enabled, the app will be minimized on close. -When both are disabled, the app will quit on close. +```json +{ + "isTrayIconEnabled": false, + "isMinimizeOnCloseEnabled": false +} +``` + +If `isTrayIconEnabled` is enabled, the app will be hidden on close. +If `isMinimizeOnCloseEnabled` is enabled, the app will be minimized on close. +With both disabled, the app will quit on close. + ## License Released under the MIT license. [Rocket.Chat]: https://rocket.chat - [Electron]: https://electronjs.org/ - [your experience]: https://survey.zohopublic.com/zs/e4BUFG - [Blog]: https://rocket.chat/case-studies/?utm_source=github&utm_medium=readme&utm_campaign=community - [Sign Up!]: https://rocket.chat/newsletter/?utm_source=github&utm_medium=readme&utm_campaign=community - [Releases]: https://github.com/RocketChat/Rocket.Chat.Electron/releases/latest - [ongoing changes in Rocket.Chat codebase]: https://forums.rocket.chat/t/moving-away-from-meteor-and-beyond/3270 - [rollup]: https://github.com/rollup/rollup - -[node-gyp readme]: https://github.com/nodejs/node-gyp#installation - [Jest]: https://jestjs.io/ - -[Jest electron runner]: https://github.com/facebook-atom/jest-electron-runner - +[Jest electron runner]: https://github.com/kayahr/jest-electron-runner [electron-builder]: https://github.com/electron-userland/electron-builder - -[customization options]: https://github.com/electron-userland/electron-builder/wiki/Options - -[node-gyp install guide]: https://github.com/nodejs/node-gyp#installation +[customization options]: https://www.electron.build/configuration diff --git a/alpha-app-update.yml b/alpha-app-update.yml new file mode 100644 index 0000000000..6310bdd48b --- /dev/null +++ b/alpha-app-update.yml @@ -0,0 +1,5 @@ +owner: RocketChat +repo: Rocket.Chat.Electron +provider: github +vPrefixedTagName: false +channel: alpha diff --git a/beta-app-update.yml b/beta-app-update.yml new file mode 100644 index 0000000000..0b3d17108b --- /dev/null +++ b/beta-app-update.yml @@ -0,0 +1,5 @@ +owner: RocketChat +repo: Rocket.Chat.Electron +provider: github +vPrefixedTagName: false +channel: beta diff --git a/build/afterPack.js b/build/afterPack.js new file mode 100644 index 0000000000..3f58b4b361 --- /dev/null +++ b/build/afterPack.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path'); + +const { flipFuses, FuseVersion, FuseV1Options } = require('@electron/fuses'); + +async function setupLinuxWrapper(context) { + const { appOutDir } = context; + + const binaryPath = path.join(appOutDir, 'rocketchat-desktop'); + const binaryBinPath = path.join(appOutDir, 'rocketchat-desktop.bin'); + const wrapperSrc = path.join(__dirname, 'linux', 'wrapper.sh'); + const wrapperDest = path.join(appOutDir, 'rocketchat-desktop'); + + if (fs.existsSync(binaryBinPath)) { + console.log('Wrapper already installed, skipping'); + return; + } + + console.log('Setting up Linux display server wrapper...'); + + fs.renameSync(binaryPath, binaryBinPath); + console.log(' Renamed binary to rocketchat-desktop.bin'); + + fs.copyFileSync(wrapperSrc, wrapperDest); + fs.chmodSync(wrapperDest, 0o755); + console.log(' Installed wrapper script as rocketchat-desktop'); + + console.log('Linux wrapper setup complete'); +} + +exports.default = async function afterPack(context) { + console.log( + 'AfterPack: Platform =', + context.electronPlatformName, + 'OutDir =', + context.appOutDir + ); + + let appPath; + switch (context.electronPlatformName) { + case 'darwin': + case 'mas': + appPath = `${context.appOutDir}/Rocket.Chat.app`; + break; + case 'win32': + appPath = `${context.appOutDir}/Rocket.Chat.exe`; + break; + default: + appPath = `${context.appOutDir}/rocketchat-desktop`; + break; + } + + // Fuses MUST be applied BEFORE signing. Per Electron docs: + // "Because they are flipped at package time before you code sign your app, + // the OS becomes responsible for ensuring those bits aren't flipped back + // via OS-level code signing validation" + // See: https://www.electronjs.org/docs/latest/tutorial/fuses + console.log('Applying electron fuses for enhanced security to:', appPath); + + await flipFuses(appPath, { + version: FuseVersion.V1, + [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true, + [FuseV1Options.OnlyLoadAppFromAsar]: true, + [FuseV1Options.RunAsNode]: false, + [FuseV1Options.EnableCookieEncryption]: false, + [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, + [FuseV1Options.EnableNodeCliInspectArguments]: false, + [FuseV1Options.LoadBrowserProcessSpecificV8Snapshot]: false, + [FuseV1Options.GrantFileProtocolExtraPrivileges]: true, + }); + + console.log('Electron fuses applied successfully'); + + if (context.electronPlatformName === 'linux') { + await setupLinuxWrapper(context); + } +}; diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist index c5b6fabfa1..8a8d017cf9 100644 --- a/build/entitlements.mac.plist +++ b/build/entitlements.mac.plist @@ -24,5 +24,7 @@ com.apple.security.network.client + com.apple.security.network.server + diff --git a/build/entitlements.mas.inherit.plist b/build/entitlements.mas.inherit.plist index 2745dc5fdc..fee15c7fb6 100644 --- a/build/entitlements.mas.inherit.plist +++ b/build/entitlements.mas.inherit.plist @@ -8,7 +8,5 @@ com.apple.security.cs.allow-unsigned-executable-memory - com.apple.security.cs.disable-library-validation - diff --git a/build/entitlements.mas.plist b/build/entitlements.mas.plist index e9ba08843d..5f63491a01 100644 --- a/build/entitlements.mas.plist +++ b/build/entitlements.mas.plist @@ -12,8 +12,6 @@ com.apple.security.cs.allow-unsigned-executable-memory - com.apple.security.cs.disable-library-validation - com.apple.security.device.audio-input com.apple.security.device.camera @@ -26,6 +24,8 @@ com.apple.security.network.client + com.apple.security.network.server + com.apple.security.application-groups S6UPZG7ZR3.chat.rocket diff --git a/build/install-kms-cng-provider.ps1 b/build/install-kms-cng-provider.ps1 new file mode 100644 index 0000000000..63e89a8b09 --- /dev/null +++ b/build/install-kms-cng-provider.ps1 @@ -0,0 +1,92 @@ +param( + [switch]$Force +) + +$ErrorActionPreference = 'Stop' + +# Configuration +$KMS_VERSION = "cng-v1.2" +$ZIP_FILENAME = "kmscng-1.2-windows-amd64.zip" +$DOWNLOAD_URL = "https://github.com/GoogleCloudPlatform/kms-integrations/releases/download/$KMS_VERSION/$ZIP_FILENAME" +$CacheDir = Join-Path $env:GITHUB_WORKSPACE "build\installers" +$CachedMsiPath = Join-Path $CacheDir "google-cloud-kms-cng-provider.msi" +$TempZipPath = Join-Path $env:TEMP "kms-cng-provider.zip" +$TempExtractDir = Join-Path $env:TEMP "kms-cng-extract" + +Write-Host "Google Cloud KMS CNG Provider Installation Script" +Write-Host "==================================================" + +# Create cache directory if needed +if (-not (Test-Path $CacheDir)) { + Write-Host "Creating cache directory..." + New-Item -ItemType Directory -Path $CacheDir -Force | Out-Null +} + +# Check for cached MSI +if ((Test-Path $CachedMsiPath) -and -not $Force) { + Write-Host "Using cached MSI file" + $MsiPath = $CachedMsiPath +} else { + Write-Host "Downloading KMS CNG provider..." + + # Clean up any existing temp files + if (Test-Path $TempZipPath) { + Remove-Item $TempZipPath -Force + } + if (Test-Path $TempExtractDir) { + Remove-Item $TempExtractDir -Recurse -Force + } + + # Download the ZIP file + Write-Host "Downloading from: $DOWNLOAD_URL" + try { + Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $TempZipPath -UseBasicParsing + Write-Host "Download completed" + } catch { + Write-Error "Failed to download KMS CNG provider: $_" + exit 1 + } + + # Extract the ZIP file + Write-Host "Extracting files..." + Expand-Archive -Path $TempZipPath -DestinationPath $TempExtractDir -Force + + # Find the MSI file + $ExtractedMsi = Get-ChildItem -Path $TempExtractDir -Filter "*.msi" -Recurse | Select-Object -First 1 + if (-not $ExtractedMsi) { + Write-Error "MSI file not found in archive" + exit 1 + } + + # Copy to cache + Copy-Item -Path $ExtractedMsi.FullName -Destination $CachedMsiPath -Force + Write-Host "MSI file cached" + $MsiPath = $CachedMsiPath +} + +# Install the MSI +Write-Host "Installing KMS CNG provider..." +$InstallArgs = @( + "/i", "`"$MsiPath`"", + "/quiet", + "/norestart", + "/l*v", "`"$env:TEMP\kms-cng-install.log`"" +) + +$Process = Start-Process -FilePath "msiexec.exe" -ArgumentList $InstallArgs -Wait -PassThru +if ($Process.ExitCode -eq 0) { + Write-Host "Installation completed successfully" +} else { + Write-Error "Installation failed with exit code: $($Process.ExitCode)" + exit 1 +} + +# Cleanup +if (Test-Path $TempZipPath) { + Remove-Item $TempZipPath -Force -ErrorAction SilentlyContinue +} +if (Test-Path $TempExtractDir) { + Remove-Item $TempExtractDir -Recurse -Force -ErrorAction SilentlyContinue +} + +Write-Host "Setup completed" \ No newline at end of file diff --git a/build/installers/.gitkeep b/build/installers/.gitkeep new file mode 100644 index 0000000000..39ef05c0cb --- /dev/null +++ b/build/installers/.gitkeep @@ -0,0 +1 @@ +# Cache directory for Google Cloud KMS CNG Provider installer diff --git a/build/linux/wrapper.sh b/build/linux/wrapper.sh new file mode 100644 index 0000000000..169cff80f1 --- /dev/null +++ b/build/linux/wrapper.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Rocket.Chat Desktop - Linux Display Server Wrapper +# Ensures proper display server selection before Chromium initializes. +# See docs/linux-wayland-bug-postmortem.md for technical details. + +set -euo pipefail + +SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")" +SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" +BINARY="$SCRIPT_DIR/rocketchat-desktop.bin" + +EXTRA_ARGS="" + +should_force_x11() { + [[ "${XDG_SESSION_TYPE:-}" != "wayland" ]] && return 0 + [[ -z "${WAYLAND_DISPLAY:-}" ]] && return 0 + + local runtime_dir="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}" + local socket="$runtime_dir/$WAYLAND_DISPLAY" + [[ ! -S "$socket" ]] && return 0 + + return 1 +} + +if should_force_x11; then + EXTRA_ARGS="--ozone-platform=x11" +fi + +exec "$BINARY" $EXTRA_ARGS "$@" diff --git a/build/notarize.js b/build/notarize.js index a85110a7db..7bee980b79 100644 --- a/build/notarize.js +++ b/build/notarize.js @@ -1,7 +1,13 @@ const { notarize } = require('electron-notarize'); -exports.default = function notarizing(context) { +exports.default = async function afterSign(context) { const { electronPlatformName, appOutDir } = context; + + if (electronPlatformName === 'win32') { + console.log('AfterSign: Windows - fuses already applied in afterPack'); + return; + } + if ( electronPlatformName !== 'darwin' || process.env.FORCE_NOTARIZE !== 'true' @@ -27,12 +33,12 @@ exports.default = function notarizing(context) { teamId: 'S6UPZG7ZR3', }) .then(() => { - clearTimeout(timer); + clearInterval(timer); console.log(); resolve(); }) .catch((error) => { - clearTimeout(timer); + clearInterval(timer); console.log(); reject(error); }); diff --git a/build/winSignKms.js b/build/winSignKms.js new file mode 100644 index 0000000000..74165d7f90 --- /dev/null +++ b/build/winSignKms.js @@ -0,0 +1,446 @@ +const { spawnSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// Forward declarations +let signWindowsOnLinux; +let signWindowsOnWindows; + +/** + * Check required environment variables for signing + */ +function validateEnvironment() { + const kmsKeyResource = process.env.WIN_KMS_KEY_RESOURCE; + const certFile = process.env.WIN_CERT_FILE; + + if (!kmsKeyResource) { + console.log( + '[winSignKms] WIN_KMS_KEY_RESOURCE not set - skipping signing (validation build)' + ); + return null; + } + if (!certFile || !fs.existsSync(certFile)) { + console.log( + '[winSignKms] WIN_CERT_FILE not set or does not exist - skipping signing (validation build)' + ); + return null; + } + + return { kmsKeyResource, certFile }; +} + +/** + * Extract key alias from KMS resource + */ +function extractKeyAlias(kmsKeyResource) { + // Format: projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEY/cryptoKeyVersions/VERSION + const parts = kmsKeyResource.split('/'); + if (parts.length >= 6 && parts[4] === 'cryptoKeys') { + return parts[5]; + } + // Fallback to default key name if we can't parse + return 'Electron_Desktop_App_Key'; +} + +/** + * Check available tools + */ +function checkAvailableTools() { + let jsignCmd; + let gcloudCmd; + + if (process.platform === 'win32') { + const jsignChocolateyPath = + 'C:\\ProgramData\\chocolatey\\lib\\jsign\\tools\\jsign.cmd'; + const gcloudChocolateyPath = + 'C:\\ProgramData\\chocolatey\\lib\\gcloudsdk\\tools\\google-cloud-sdk\\bin\\gcloud.cmd'; + const gcloudWingetPath1 = + 'C:\\Program Files\\Google\\Cloud SDK\\google-cloud-sdk\\bin\\gcloud.cmd'; + const gcloudWingetPath2 = process.env.LOCALAPPDATA + ? path.join( + process.env.LOCALAPPDATA, + 'Google', + 'Cloud SDK', + 'google-cloud-sdk', + 'bin', + 'gcloud.cmd' + ) + : null; + + const jsignAvailable = fs.existsSync(jsignChocolateyPath); + + if (jsignAvailable) { + jsignCmd = jsignChocolateyPath; + console.log(`[winSignKms] jsign found at: ${jsignCmd}`); + } else { + console.log(`[winSignKms] jsign not found at: ${jsignChocolateyPath}`); + } + + if (fs.existsSync(gcloudChocolateyPath)) { + gcloudCmd = gcloudChocolateyPath; + } else if (fs.existsSync(gcloudWingetPath1)) { + gcloudCmd = gcloudWingetPath1; + } else if (gcloudWingetPath2 && fs.existsSync(gcloudWingetPath2)) { + gcloudCmd = gcloudWingetPath2; + } else { + const whereRes = spawnSync('where', ['gcloud'], { stdio: 'pipe' }); + if (whereRes.status === 0) { + const first = whereRes.stdout.toString().split(/\r?\n/).find(Boolean); + if (first) gcloudCmd = first.trim(); + } + } + + const gcloudAvailable = Boolean(gcloudCmd); + + if (gcloudAvailable) { + console.log(`[winSignKms] gcloud found at: ${gcloudCmd}`); + } else { + console.log('[winSignKms] gcloud not found in known locations or PATH'); + } + + console.log( + `[winSignKms] jsign available: ${jsignAvailable} (${jsignCmd})` + ); + console.log( + `[winSignKms] gcloud available: ${gcloudAvailable} (${gcloudCmd})` + ); + + return { jsignAvailable, gcloudAvailable, jsignCmd, gcloudCmd }; + } + + // Linux/macOS - use normal PATH resolution + jsignCmd = 'jsign'; + gcloudCmd = 'gcloud'; + + const jsignResult = spawnSync(jsignCmd, ['--help'], { stdio: 'pipe' }); + const jsignAvailable = jsignResult.status === 0; + + const gcloudResult = spawnSync(gcloudCmd, ['--version'], { stdio: 'pipe' }); + const gcloudAvailable = gcloudResult.status === 0; + + console.log(`[winSignKms] jsign available: ${jsignAvailable}`); + console.log(`[winSignKms] gcloud available: ${gcloudAvailable}`); + + if (!jsignAvailable) { + console.log( + '[winSignKms] jsign check failed:', + jsignResult.error?.message || 'No error message' + ); + } + if (!gcloudAvailable) { + console.log( + '[winSignKms] gcloud check failed:', + gcloudResult.error?.message || 'No error message' + ); + } + + return { jsignAvailable, gcloudAvailable, jsignCmd, gcloudCmd }; +} + +module.exports = async function signWithGoogleKms(config) { + // Handle Linux-based signing for Windows executables + if (process.platform === 'linux') { + return signWindowsOnLinux(config); + } + + // Handle Windows-based signing with jsign (preferred method) + if (process.platform === 'win32') { + return signWindowsOnWindows(config); + } + + console.log('[winSignKms] Skipping (unsupported platform).'); +}; + +/** + * Sign Windows executables on Linux using jsign with Google Cloud KMS + */ +signWindowsOnLinux = async function (config) { + console.log( + '[winSignKms] Linux-based Windows signing with jsign + Google Cloud KMS' + ); + + const input = config.path; + const name = config.name || 'Rocket.Chat'; + const { site } = config; + + // Skip files that don't need standard Authenticode signing + const ext = path.extname(input).toLowerCase(); + const skipExtensions = ['.appx', '.zip']; + if (skipExtensions.includes(ext)) { + console.log( + `[winSignKms] Skipping ${ext} file (not applicable for Authenticode signing):`, + path.basename(input) + ); + return; + } + + // Validate environment + const envConfig = validateEnvironment(); + if (!envConfig) { + return; + } + + const { kmsKeyResource, certFile } = envConfig; + + // Check available tools + const { jsignAvailable, gcloudAvailable, jsignCmd, gcloudCmd } = + checkAvailableTools(); + + if (!jsignAvailable) { + throw new Error('[winSignKms] jsign not available. Please install jsign.'); + } + + if (!gcloudAvailable) { + throw new Error( + '[winSignKms] gcloud not available. Please install Google Cloud CLI.' + ); + } + + // Extract components from KMS resource path + // Format: projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEY/cryptoKeyVersions/VERSION + const resourceParts = kmsKeyResource.split('/'); + const projectId = resourceParts[1]; + const location = resourceParts[3]; + const keyRingName = resourceParts[5]; + const keyName = extractKeyAlias(kmsKeyResource); + + console.log(`[winSignKms] Using project: ${projectId}`); + console.log(`[winSignKms] Using location: ${location}`); + console.log(`[winSignKms] Using keyring: ${keyRingName}`); + console.log(`[winSignKms] Using key: ${keyName}`); + + // Get access token using gcloud + console.log('[winSignKms] Getting access token from gcloud...'); + const gcloudResult = spawnSync(gcloudCmd, ['auth', 'print-access-token'], { + stdio: 'pipe', + timeout: 30000, + env: { + ...process.env, + GOOGLE_APPLICATION_CREDENTIALS: + process.env.GOOGLE_APPLICATION_CREDENTIALS, + CLOUDSDK_PYTHON: process.env.CLOUDSDK_PYTHON, + }, + }); + + if (gcloudResult.status !== 0) { + const errorOutput = gcloudResult.stderr + ? gcloudResult.stderr.toString() + : 'Unknown error'; + throw new Error(`[winSignKms] Failed to get access token: ${errorOutput}`); + } + + const accessToken = gcloudResult.stdout.toString().trim(); + if (!accessToken) { + throw new Error('[winSignKms] Empty access token received from gcloud'); + } + + console.log('[winSignKms] Successfully obtained access token'); + + // Build jsign command + const jsignArgs = [ + '--storetype', + 'GOOGLECLOUD', + '--keystore', + `projects/${projectId}/locations/${location}/keyRings/${keyRingName}`, + '--storepass', + accessToken, + '--alias', + keyName, + '--certfile', + certFile, + '--tsaurl', + 'http://timestamp.digicert.com', + path.resolve(input), + ]; + + if (name) { + jsignArgs.push('--name', name); + } + + if (site) { + jsignArgs.push('--url', site); + } + + console.log('[winSignKms] Running jsign with masked token...'); + console.log( + `[winSignKms] jsign --storetype GOOGLECLOUD --keystore projects/${projectId}/locations/${location}/keyRings/${keyRingName} --storepass [MASKED] --alias ${keyName} --certfile ${certFile} --tsaurl http://timestamp.digicert.com ${path.resolve(input)}` + ); + + // Execute jsign + const result = spawnSync(jsignCmd, jsignArgs, { + stdio: 'pipe', + timeout: 120000, + env: { + ...process.env, + GOOGLE_APPLICATION_CREDENTIALS: + process.env.GOOGLE_APPLICATION_CREDENTIALS, + CLOUDSDK_PYTHON: process.env.CLOUDSDK_PYTHON, + }, + }); + + if (result.status !== 0) { + const stderr = result.stderr ? result.stderr.toString() : ''; + const stdout = result.stdout ? result.stdout.toString() : ''; + console.error('[winSignKms] jsign stderr:', stderr); + console.error('[winSignKms] jsign stdout:', stdout); + throw new Error( + `[winSignKms] jsign failed with exit code ${result.status}: ${stderr}` + ); + } + + const stdout = result.stdout ? result.stdout.toString() : ''; + if (stdout) { + console.log('[winSignKms] jsign output:', stdout); + } + + console.log('[winSignKms] Successfully signed Windows executable with jsign'); +}; + +/** + * Sign Windows executables on Windows using jsign with Google Cloud KMS + */ +signWindowsOnWindows = async function (config) { + console.log( + '[winSignKms] Windows-based signing with jsign + Google Cloud KMS' + ); + + const input = config.path; + const name = config.name || 'Rocket.Chat'; + const { site } = config; + + // Skip files that don't need standard Authenticode signing + const ext = path.extname(input).toLowerCase(); + const skipExtensions = ['.appx', '.zip']; + if (skipExtensions.includes(ext)) { + console.log( + `[winSignKms] Skipping ${ext} file (not applicable for Authenticode signing):`, + path.basename(input) + ); + return; + } + + // Validate environment + const envConfig = validateEnvironment(); + if (!envConfig) { + return; + } + + const { kmsKeyResource, certFile } = envConfig; + + // Check available tools + const { jsignAvailable, gcloudAvailable, jsignCmd, gcloudCmd } = + checkAvailableTools(); + + if (!jsignAvailable) { + throw new Error('[winSignKms] jsign not available. Please install jsign.'); + } + + if (!gcloudAvailable) { + throw new Error( + '[winSignKms] gcloud not available. Please install Google Cloud CLI.' + ); + } + + // Extract components from KMS resource path + // Format: projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEY/cryptoKeyVersions/VERSION + const resourceParts = kmsKeyResource.split('/'); + const projectId = resourceParts[1]; + const location = resourceParts[3]; + const keyRingName = resourceParts[5]; + const keyName = extractKeyAlias(kmsKeyResource); + + console.log(`[winSignKms] Using project: ${projectId}`); + console.log(`[winSignKms] Using location: ${location}`); + console.log(`[winSignKms] Using keyring: ${keyRingName}`); + console.log(`[winSignKms] Using key: ${keyName}`); + + // Get access token using gcloud + console.log('[winSignKms] Getting access token from gcloud...'); + const gcloudResult = spawnSync( + 'cmd', + ['/c', gcloudCmd, 'auth', 'print-access-token'], + { + stdio: 'pipe', + timeout: 30000, + env: { + ...process.env, + GOOGLE_APPLICATION_CREDENTIALS: + process.env.GOOGLE_APPLICATION_CREDENTIALS, + CLOUDSDK_PYTHON: process.env.CLOUDSDK_PYTHON, + }, + } + ); + + if (gcloudResult.status !== 0) { + const errorOutput = gcloudResult.stderr + ? gcloudResult.stderr.toString() + : 'Unknown error'; + throw new Error(`[winSignKms] Failed to get access token: ${errorOutput}`); + } + + const accessToken = gcloudResult.stdout.toString().trim(); + if (!accessToken) { + throw new Error('[winSignKms] Empty access token received from gcloud'); + } + + console.log('[winSignKms] Successfully obtained access token'); + + // Build jsign command + const jsignArgs = [ + '--storetype', + 'GOOGLECLOUD', + '--keystore', + `projects/${projectId}/locations/${location}/keyRings/${keyRingName}`, + '--storepass', + accessToken, + '--alias', + keyName, + '--certfile', + certFile, + '--tsaurl', + 'http://timestamp.digicert.com', + path.resolve(input), + ]; + + if (name) { + jsignArgs.push('--name', name); + } + + if (site) { + jsignArgs.push('--url', site); + } + + console.log('[winSignKms] Running jsign with masked token...'); + console.log( + `[winSignKms] jsign --storetype GOOGLECLOUD --keystore projects/${projectId}/locations/${location}/keyRings/${keyRingName} --storepass [MASKED] --alias ${keyName} --certfile ${certFile} --tsaurl http://timestamp.digicert.com ${path.resolve(input)}` + ); + + // Execute jsign + const result = spawnSync('cmd', ['/c', jsignCmd].concat(jsignArgs), { + stdio: 'pipe', + timeout: 120000, + env: { + ...process.env, + GOOGLE_APPLICATION_CREDENTIALS: + process.env.GOOGLE_APPLICATION_CREDENTIALS, + CLOUDSDK_PYTHON: process.env.CLOUDSDK_PYTHON, + }, + }); + + if (result.status !== 0) { + const stderr = result.stderr ? result.stderr.toString() : ''; + const stdout = result.stdout ? result.stdout.toString() : ''; + console.error('[winSignKms] jsign stderr:', stderr); + console.error('[winSignKms] jsign stdout:', stdout); + throw new Error( + `[winSignKms] jsign failed with exit code ${result.status}: ${stderr}` + ); + } + + const stdout = result.stdout ? result.stdout.toString() : ''; + if (stdout) { + console.log('[winSignKms] jsign output:', stdout); + } + + console.log('[winSignKms] Successfully signed Windows executable with jsign'); +}; diff --git a/docs/alpha-release-process.md b/docs/alpha-release-process.md new file mode 100644 index 0000000000..c14da17084 --- /dev/null +++ b/docs/alpha-release-process.md @@ -0,0 +1,142 @@ +# Alpha Release Process + +This document describes how to create alpha releases for QA testing and early customer access. + +## Overview + +The Rocket.Chat Desktop app supports three release channels: + +- **Stable** (`latest`) - Production releases for all users +- **Beta** - Pre-release testing with broader audience +- **Alpha** - Early testing for QA and select customers + +## How Channels Work + +| Channel | Version Format | Who Gets It | Update File | +| ------- | ---------------- | ------------------- | ------------ | +| Stable | `4.12.0` | All users (default) | `latest.yml` | +| Beta | `4.12.0-beta.1` | Beta opt-in users | `beta.yml` | +| Alpha | `4.12.0-alpha.1` | Alpha opt-in users | `alpha.yml` | + +**Channel hierarchy**: Alpha users receive alpha, beta, AND stable updates. Beta users receive beta AND stable. Stable users only receive stable. + +## Creating an Alpha Release + +### 1. Create Release Branch + +```bash +git checkout master +git pull +git checkout -b release/4.12.0-alpha.1 +``` + +### 2. Update Version + +Edit `package.json`: + +```json +{ + "version": "4.12.0-alpha.1" +} +``` + +### 3. Commit and Push + +```bash +git add package.json +git commit -m "chore: bump version to 4.12.0-alpha.1" +git push origin release/4.12.0-alpha.1 +``` + +### 4. Create and Push Tag + +```bash +git tag 4.12.0-alpha.1 +git push origin 4.12.0-alpha.1 +``` + +### 5. CI Builds Automatically + +The GitHub Actions workflow triggers on tag push and: + +- Builds for all platforms (Windows, macOS, Linux) +- Generates `alpha.yml`, `alpha-mac.yml`, `alpha-linux.yml` metadata +- Creates a draft GitHub release marked as **Pre-release** +- Publishes Linux snap to the `edge` channel + +### 6. Publish the Release + +1. Go to GitHub Releases +2. Find the draft release for your version +3. Review the release notes +4. Click "Publish release" + +## How Users Opt Into Alpha + +### Option A: Developer Mode (Recommended for QA) + +1. Open Settings in the app +2. Enable **Developer Mode** +3. Open **About** dialog (Help > About) +4. Select **Alpha (Experimental)** from the Update Channel dropdown +5. Click **Check for Updates** + +The setting persists - users don't need to select it again. + +### Option B: Configuration File (For Managed Deployments) + +Create `update.json` in the user data directory: + +| Platform | Location | +| -------- | ------------------------------------------------------- | +| Windows | `%APPDATA%\Rocket.Chat\update.json` | +| macOS | `~/Library/Application Support/Rocket.Chat/update.json` | +| Linux | `~/.config/Rocket.Chat/update.json` | + +Content: + +```json +{ + "channel": "alpha" +} +``` + +For enterprise deployments where you want to force the setting: + +```json +{ + "channel": "alpha", + "forced": true +} +``` + +## Version Numbering Guidelines + +- **Alpha**: `4.12.0-alpha.1`, `4.12.0-alpha.2`, etc. +- **Beta**: `4.12.0-beta.1`, `4.12.0-beta.2`, etc. +- **Stable**: `4.12.0` + +When promoting: + +- Alpha `4.12.0-alpha.5` → Beta `4.12.0-beta.1` +- Beta `4.12.0-beta.3` → Stable `4.12.0` + +## Safety Guarantees + +- Stable users **never** see alpha releases (they check `latest.yml`, not `alpha.yml`) +- Users must explicitly opt into alpha channel +- Alpha releases are marked as "Pre-release" on GitHub +- Users can switch back to stable at any time + +## Troubleshooting + +### Alpha update not showing + +1. Verify the release is published (not draft) +2. Check that `alpha.yml` exists in the release assets +3. Ensure user has selected "Alpha" channel +4. Check for updates manually via About dialog + +### Checking current channel + +In Developer Mode, open About dialog - the current channel is shown in the dropdown. diff --git a/docs/linux-display-server.md b/docs/linux-display-server.md new file mode 100644 index 0000000000..f43d04adec --- /dev/null +++ b/docs/linux-display-server.md @@ -0,0 +1,154 @@ +# Linux Display Server Configuration + +Rocket.Chat Desktop supports both Wayland and X11 display servers on Linux, with automatic detection and safe fallback mechanisms. + +## How It Works + +Rocket.Chat Desktop uses a **wrapper script** to detect your display server before the application starts. This prevents crashes that occur when Chromium tries to connect to an unavailable Wayland compositor. + +### Detection Logic + +The wrapper checks (in order): + +1. Is `XDG_SESSION_TYPE` set to `wayland`? +2. Is `WAYLAND_DISPLAY` set? +3. Does the Wayland socket actually exist? + +If any check fails, the wrapper forces X11 mode via `--ozone-platform=x11`. + +| Session Type | WAYLAND_DISPLAY | Socket Exists | Result | +| ------------ | --------------- | ------------- | -------------- | +| wayland | wayland-0 | Yes | Native Wayland | +| wayland | wayland-0 | No | Force X11 | +| wayland | (empty) | N/A | Force X11 | +| x11 | (any) | (any) | Force X11 | +| tty | (any) | (any) | Force X11 | + +## Package Behavior + +| Package Type | Display Server | Notes | +| ------------ | ----------------------- | ------------------------------------------------ | +| **deb/rpm** | Auto-detect via wrapper | Native Wayland when available, safe X11 fallback | +| **AppImage** | Auto-detect via wrapper | Native Wayland when available, safe X11 fallback | +| **Snap** | Force X11 | Wayland disabled via `allowNativeWayland: false` | + +## Why a Wrapper Script? + +Chromium (which powers Electron) selects its display server during native initialization, **before** any JavaScript code runs. This means: + +- `app.commandLine.appendSwitch()` in Electron code runs too late +- Environment variable checks in JavaScript run too late +- The only reliable fix is a shell wrapper that sets flags **before** the binary starts + +For technical details, see [linux-wayland-bug-postmortem.md](./linux-wayland-bug-postmortem.md). + +## Wayland Support + +When running on a proper Wayland session, Rocket.Chat Desktop provides: + +- Native Wayland rendering +- PipeWire screen capture for video calls +- Native OS screen picker integration +- Hardware acceleration + +### Requirements for Wayland + +- Active Wayland compositor with accessible socket +- `xdg-desktop-portal` (recommended, auto-suggested by deb/rpm packages) +- `xdg-desktop-portal-gtk` or desktop-specific portal backend +- PipeWire for screen sharing + +## GPU Crash Recovery + +Rocket.Chat Desktop includes automatic GPU crash detection: + +1. **Crash Detection**: If the GPU process crashes during startup (e.g., incompatible drivers), the app detects this. +2. **Automatic Fallback**: The app relaunches with GPU acceleration disabled. +3. **Software Rendering**: The app continues with software rendering. + +This is separate from display server selection and handles GPU driver issues. + +## Common Scenarios + +### Ubuntu 22.04 LTS (X11 Session) + +Ubuntu 22.04 defaults to X11 with GNOME. The wrapper detects `XDG_SESSION_TYPE=x11` and forces X11 mode, preventing Wayland connection errors. + +### Fedora (Wayland Session) + +Fedora defaults to Wayland with GNOME. The wrapper detects the Wayland session and socket, allowing native Wayland operation. + +### SSH Sessions + +SSH sessions inherit environment variables from the graphical session but don't have Wayland socket access. The wrapper detects the missing socket and forces X11 mode. + +### Virtual Machines + +VMs with paravirtual graphics may not support GPU acceleration. The GPU crash recovery system handles this automatically. + +## Manual Override + +You can force a specific display server: + +```bash +# Force X11 (bypass wrapper detection) +rocketchat-desktop --ozone-platform=x11 + +# Force Wayland (use with caution) +rocketchat-desktop --ozone-platform=wayland + +# Disable GPU acceleration +rocketchat-desktop --disable-gpu +``` + +## Troubleshooting + +### Check Your Display Server + +```bash +echo "Session Type: $XDG_SESSION_TYPE" +echo "Wayland Display: $WAYLAND_DISPLAY" +echo "X11 Display: $DISPLAY" +``` + +### Check Wayland Socket + +```bash +ls -la ${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/${WAYLAND_DISPLAY:-wayland-0} +``` + +### Enable Verbose Logging + +```bash +rocketchat-desktop --enable-logging --v=1 +``` + +### Screen Sharing Not Working on Wayland + +Install portal dependencies: + +```bash +# Ubuntu/Debian +sudo apt install xdg-desktop-portal xdg-desktop-portal-gtk pipewire + +# Fedora +sudo dnf install xdg-desktop-portal xdg-desktop-portal-gtk pipewire +``` + +Verify PipeWire is running: + +```bash +systemctl --user status pipewire +``` + +## Reporting Issues + +When reporting display server issues, include: + +1. Session type: `echo $XDG_SESSION_TYPE` +2. Wayland display: `echo $WAYLAND_DISPLAY` +3. Socket check: `ls -la ${XDG_RUNTIME_DIR}/${WAYLAND_DISPLAY}` +4. Graphics card: `lspci | grep -i vga` +5. Distribution: `cat /etc/os-release` +6. Package type: snap/deb/rpm/AppImage +7. Full error output with `--enable-logging --v=1` diff --git a/docs/linux-wayland-bug-postmortem.md b/docs/linux-wayland-bug-postmortem.md new file mode 100644 index 0000000000..59d5da973f --- /dev/null +++ b/docs/linux-wayland-bug-postmortem.md @@ -0,0 +1,370 @@ +# Linux Wayland/X11 Display Server Bug - Post-Mortem Analysis + +## Issue Reference + +- **GitHub Issue**: [#3154](https://github.com/RocketChat/Rocket.Chat.Electron/issues/3154) +- **Related PR**: [#3171](https://github.com/RocketChat/Rocket.Chat.Electron/pull/3171) +- **Date**: January 2026 +- **Severity**: Critical (application crash/segfault) + +## Executive Summary + +Rocket.Chat Desktop crashes with SEGFAULT when environment variables suggest a Wayland session but no valid Wayland compositor is available. The initial fix attempt using `app.commandLine.appendSwitch()` was **ineffective** because Chromium's display platform initialization occurs before any Electron JavaScript code executes. The solution requires a **shell wrapper script** that detects the display server situation and passes appropriate command-line flags before the binary starts. + +--- + +## The Solution That Actually Worked + +### Problem + +Rocket.Chat Desktop crashes with SEGFAULT when environment variables suggest a Wayland session but no valid Wayland compositor is available, because Chromium's display platform initialization occurs before any Electron JavaScript code executes. + +### Solution + +Implement a shell wrapper script (`build/linux/wrapper.sh`) that detects the display server situation before the binary starts. The wrapper checks XDG_SESSION_TYPE, WAYLAND_DISPLAY, and socket existence, then passes `--ozone-platform=x11` when needed. + +### Result + +All test scenarios pass on Ubuntu 22.04 and Fedora 42 (physical and VM). The wrapper correctly: + +- Allows native Wayland when valid socket exists +- Forces X11 in all failure scenarios (fake socket, missing display, X11 session, etc.) +- Prevents all previously occurring segfaults + +### PR + +[#3171](https://github.com/RocketChat/Rocket.Chat.Electron/pull/3171) + +--- + +## Problem Description + +### Symptoms + +Users reported segmentation faults when launching Rocket.Chat Desktop on: + +- Ubuntu 22.04 LTS with X11 sessions +- SSH sessions into machines with graphical desktops +- Systems where `XDG_SESSION_TYPE=wayland` but no Wayland compositor is running + +### Error Output + +``` +ERROR:ui/ozone/platform/wayland/host/wayland_connection.cc:202 +Failed to connect to Wayland display: No such file or directory (2) + +ERROR:ui/ozone/platform/wayland/ozone_platform_wayland.cc:282 +Failed to initialize Wayland platform + +ERROR:ui/aura/env.cc:257 +The platform failed to initialize. Exiting. + +Segmentation fault (core dumped) +``` + +### Root Cause + +Chromium's Ozone platform layer auto-detects the display server based on environment variables. When `WAYLAND_DISPLAY` is set (even to a non-existent socket), Chromium attempts to connect to Wayland. If the connection fails, Chromium crashes instead of falling back to X11. + +--- + +## Test Environments + +### Hardware Tested + +| Environment | OS | GPU | Display Server | +| ------------------ | ------------------ | ---------------------------------- | --------------- | +| Physical Machine 1 | Fedora 42 | Intel UHD 630 + NVIDIA GTX 1660 Ti | Wayland (GNOME) | +| Physical Machine 2 | Ubuntu 22.04.2 LTS | Intel UHD 630 + NVIDIA GTX 1660 Ti | X11 (GNOME) | +| Virtual Machine | Fedora 42 | Virtual (no GPU) | Wayland (GNOME) | +| Virtual Machine | Ubuntu 22.04 | Virtual (no GPU) | X11 (GNOME) | + +### Package Formats Tested + +- Snap (4.11.0 from store) +- AppImage (4.11.0 release) +- DEB (built from source with fix) + +--- + +## Test Scenarios + +| # | Scenario | Environment Variables | Simulates | +| --- | ------------------------ | ---------------------------------------------------------- | ----------------------------------------- | +| 1 | Real Wayland session | `XDG_SESSION_TYPE=wayland`, `WAYLAND_DISPLAY=wayland-0` | Normal Wayland desktop | +| 2 | Fake Wayland socket | `XDG_SESSION_TYPE=wayland`, `WAYLAND_DISPLAY=wayland-fake` | **Bug trigger** - vars set, no compositor | +| 3 | Wayland type, no display | `XDG_SESSION_TYPE=wayland`, no `WAYLAND_DISPLAY` | Misconfigured session | +| 4 | X11 session | `XDG_SESSION_TYPE=x11`, `DISPLAY=:0` | Normal X11 desktop | +| 5 | No session type | Only `DISPLAY=:0` | Minimal/legacy config | +| 6 | TTY session | `XDG_SESSION_TYPE=tty` | Console session | + +--- + +## Test Results + +### Fedora 42 Physical (GTX 1660 Ti) - Baseline 4.11.0 + +| Scenario | Result | Notes | +| ------------------------ | ------------ | ----------------------------------------------- | +| Real Wayland session | **PASS** | Window visible, native Wayland, GPU accelerated | +| Fake Wayland socket | **SEGFAULT** | Exit code 139, core dumped | +| Wayland type, no display | **PASS** | Falls back correctly | +| X11 via XWayland | **PASS** | Window visible | +| No session type | **PASS** | Window visible | + +### Fedora 42 VM (No GPU) - Baseline 4.11.0 + +| Scenario | Result | Notes | +| ------------------------ | ------------ | ----------------------------------- | +| Real Wayland session | **PASS** | Falls back to X11 (GPU unavailable) | +| Fake Wayland socket | **SEGFAULT** | Exit code 139 | +| Wayland type, no display | **PASS** | Falls back to X11 | +| X11 via XWayland | **PASS** | Works | +| No session type | **PASS** | Works | + +### Ubuntu 22.04 Physical (GTX 1660 Ti) - Snap 4.11.0 Baseline + +| Scenario | Result | Notes | +| ------------------------ | ------------ | -------------- | +| X11 Session | **PASS** | Window visible | +| Fake Wayland socket | **SEGFAULT** | Exit code 139 | +| Wayland type, no display | **SEGFAULT** | Exit code 139 | +| No session type | **PASS** | Window visible | +| TTY type | **PASS** | Window visible | + +### Ubuntu 22.04 Physical - DEB with Code Fix (`app.commandLine.appendSwitch`) + +| Scenario | Result | Notes | +| ------------------------ | ------------ | --------------- | +| X11 Session | **PASS** | - | +| Fake Wayland socket | **SEGFAULT** | Fix ineffective | +| Wayland type, no display | **SEGFAULT** | Fix ineffective | +| No session type | **PASS** | - | +| TTY type | **PASS** | - | + +### Ubuntu 22.04 Physical - DEB with Wrapper Script Fix + +| Scenario | Result | Notes | +| ------------------------ | -------- | ------------------ | +| X11 Session | **PASS** | Window visible | +| Fake Wayland socket | **PASS** | Wrapper forces X11 | +| Wayland type, no display | **PASS** | Wrapper forces X11 | +| No session type | **PASS** | Window visible | +| TTY type | **PASS** | Window visible | + +### Fedora 42 Physical - All Packages with Wrapper Script Fix + +| Scenario | RPM | AppImage | tar.gz | +| -------------------- | -------- | -------- | -------- | +| Real Wayland session | **PASS** | **PASS** | **PASS** | +| Fake Wayland socket | **PASS** | **PASS** | **PASS** | + +**Key validation**: Real Wayland session shows "Using Wayland platform" in logs, confirming wrapper correctly allows native Wayland when socket exists. + +### Fedora 42 VM (No GPU) - RPM with Wrapper Script Fix + +| Scenario | Result | Notes | +| -------------------- | -------- | -------------------------------------------- | +| Real Wayland session | **PASS** | Detects no GPU, gracefully falls back to X11 | +| Fake Wayland socket | **PASS** | Wrapper forces X11 | + +--- + +## Technical Analysis + +### Why `app.commandLine.appendSwitch()` Doesn't Work + +``` +Process Startup Timeline: + +[Binary Start] + │ + ▼ +[Chromium Native Initialization] + │ + ├── Read environment variables + ├── WAYLAND_DISPLAY="wayland-0" found + ├── Select Ozone Wayland platform + ├── wl_display_connect("wayland-0") + ├── Connection fails (socket doesn't exist) + └── SEGFAULT ◄─── Crash happens HERE + │ + ✗ (Never reached) + │ +[V8 JavaScript Engine Init] + │ +[Electron main.ts execution] + │ +[app.commandLine.appendSwitch()] ◄─── Too late! +``` + +The JavaScript-level fix cannot work because: + +1. Chromium must initialize before V8 (JavaScript engine) can run +2. Ozone platform selection happens during Chromium initialization +3. The crash occurs before any Electron/Node.js code executes + +### Why Wrapper Script Works + +``` +Process Startup Timeline with Wrapper: + +[Wrapper Script Start] + │ + ├── Check XDG_SESSION_TYPE + ├── Check WAYLAND_DISPLAY + ├── Check if socket exists + ├── Decision: Force X11 + └── exec binary --ozone-platform=x11 + │ + ▼ +[Binary Start with --ozone-platform=x11] + │ + ▼ +[Chromium Native Initialization] + │ + ├── Read command-line arguments + ├── --ozone-platform=x11 found + ├── Select Ozone X11 platform (ignores WAYLAND_DISPLAY) + ├── Connect to X11 display + └── SUCCESS + │ + ▼ +[Application runs normally] +``` + +Command-line flags take precedence over environment-based auto-detection. + +--- + +## The Fix + +### Wrapper Script (`/opt/Rocket.Chat/rocketchat-desktop`) + +```bash +#!/bin/bash +EXTRA_ARGS="" + +if [ "$XDG_SESSION_TYPE" != "wayland" ]; then + EXTRA_ARGS="--ozone-platform=x11" +elif [ -z "$WAYLAND_DISPLAY" ]; then + EXTRA_ARGS="--ozone-platform=x11" +else + SOCKET="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/$WAYLAND_DISPLAY" + [ ! -S "$SOCKET" ] && EXTRA_ARGS="--ozone-platform=x11" +fi + +exec /opt/Rocket.Chat/rocketchat-desktop.bin $EXTRA_ARGS "$@" +``` + +### Detection Logic + +| XDG_SESSION_TYPE | WAYLAND_DISPLAY | Socket Exists | Action | +| ---------------- | --------------- | ------------- | -------------------- | +| wayland | wayland-0 | Yes | Use Wayland (native) | +| wayland | wayland-0 | No | Force X11 | +| wayland | (empty) | N/A | Force X11 | +| x11 | (any) | (any) | Force X11 | +| tty | (any) | (any) | Force X11 | +| (empty) | (any) | (any) | Force X11 | + +### Implementation by Package Type + +| Package | Fix Method | Notes | +| ------------------ | --------------------------- | ---------------------------------------------------- | +| **deb/rpm/tar.gz** | Wrapper script | Binary renamed, wrapper installed via `afterPack.js` | +| **Snap** | `allowNativeWayland: false` | electron-builder forces X11 in Snap launcher | +| **Flatpak** | electron-builder config | Uses `executableArgs` for X11 fallback | +| **AppImage** | electron-builder config | Uses internal launcher with X11 fallback | + +For deb/rpm/tar.gz, the implementation in `build/afterPack.js`: + +1. **Rename binary**: `rocketchat-desktop` → `rocketchat-desktop.bin` +2. **Install wrapper**: Copy `build/linux/wrapper.sh` as `rocketchat-desktop` +3. **Set permissions**: `chmod 755` on wrapper + +--- + +## Affected Scenarios + +### When This Bug Occurs + +1. **SSH Sessions**: User SSHs into a machine with Wayland desktop. Environment variables are inherited but SSH has no Wayland access. + +2. **VNC/Remote Desktop**: Remote access to Wayland systems where environment says Wayland but remote protocol uses X11. + +3. **X11 Fallback Sessions**: User selected X11 session at login but `XDG_SESSION_TYPE=wayland` persists from previous session. + +4. **Crashed Compositor**: Wayland compositor crashed but environment variables remain. + +5. **Container/Sandbox**: Application running in container inherits host's Wayland vars but has no socket access. + +--- + +## Validation Commands + +### Check Display Server + +```bash +echo "Session Type: $XDG_SESSION_TYPE" +echo "Wayland Display: $WAYLAND_DISPLAY" +echo "X11 Display: $DISPLAY" +``` + +### Check Wayland Socket + +```bash +ls -la ${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/${WAYLAND_DISPLAY:-wayland-0} +``` + +### Test with Forced X11 + +```bash +rocketchat-desktop --ozone-platform=x11 +``` + +### Test Wrapper Detection + +```bash +# Should show what the wrapper decides +bash -x /opt/Rocket.Chat/rocketchat-desktop --help 2>&1 | grep ozone +``` + +--- + +## Timeline + +| Date | Event | +| ---------- | ------------------------------------------------------------- | +| 2026-01-07 | Comprehensive testing reveals code fix is ineffective | +| 2026-01-07 | Wrapper script solution validated on Fedora 42 + Ubuntu 22.04 | +| 2026-01-07 | Fix implemented in build process | + +--- + +## Lessons Learned + +1. **Electron/Chromium initialization order matters**: JavaScript code cannot affect Chromium's native initialization. + +2. **Environment-based bugs require environment-level fixes**: When the bug is triggered by environment variables before app code runs, the fix must also run before app code. + +3. **Test on real hardware**: VM-only testing missed GPU-related code paths. Physical hardware testing with real GPUs caught additional edge cases. + +4. **Test multiple distributions**: Fedora and Ubuntu behaved slightly differently (Ubuntu crashed on more scenarios). + +5. **Snap, DEB, AppImage behave differently**: Each package format has different environment isolation. Test all formats. + +--- + +## References + +- Chromium Ozone Platform: https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ozone_overview.md +- Electron Command Line Switches: https://www.electronjs.org/docs/latest/api/command-line-switches +- Wayland Protocol: https://wayland.freedesktop.org/ +- XDG Base Directory Specification: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + +--- + +_Post-mortem completed: 2026-01-08_ +_Validated on: Fedora 42 (physical + VM), Ubuntu 22.04 LTS (physical)_ +_Packages validated: DEB, AppImage, tar.gz, Snap, RPM_ diff --git a/docs/pexip-auth-credentials.md b/docs/pexip-auth-credentials.md new file mode 100644 index 0000000000..f0cd54bda4 --- /dev/null +++ b/docs/pexip-auth-credentials.md @@ -0,0 +1,59 @@ +# Pexip Video Call — Auth Credentials for Persistent Chat + +## Overview + +When a Pexip video call is opened from the Rocket.Chat Electron desktop app, the app automatically captures the logged-in user's authentication credentials and makes them available to the Pexip page. Pexip can use these credentials to authenticate the embedded Rocket.Chat iframe for persistent chat. + +## How It Works + +```text +┌──────────────────────────────────────────────────────────┐ +│ Rocket.Chat Desktop App │ +│ ├── User is logged in to workspace │ +│ ├── User starts a Pexip video call │ +│ ├── App captures userId + authToken automatically │ +│ └── Opens Pexip in a dedicated video call window │ +├──────────────────────────────────────────────────────────┤ +│ Pexip Page (inside video call window) │ +│ └── Calls window.videoCallWindow.getAuthCredentials() │ +│ → Returns { userId, authToken, serverUrl } │ +│ └── Uses credentials to authenticate the RC iframe │ +└──────────────────────────────────────────────────────────┘ +``` + +## API + +### `window.videoCallWindow.getAuthCredentials()` + +Returns a Promise that resolves to the user's credentials or `null`. + +**Signature:** + +```typescript +getAuthCredentials(): Promise<{ + userId: string; + authToken: string; + serverUrl: string; +} | null> +``` + +**Response fields:** + +| Field | Type | Description | +| ----------- | -------- | ----------------------------------------------------------------------- | +| `userId` | `string` | The Rocket.Chat user ID of the logged-in user | +| `authToken` | `string` | The Meteor login token (session token) for the user | +| `serverUrl` | `string` | The origin of the Rocket.Chat server (e.g., `https://chat.example.com`) | + +Returns `null` if credentials are not available. + +## Multi-Workspace Support + +The credentials are always captured from the specific Rocket.Chat workspace that initiated the video call. If the user is connected to multiple workspaces, the correct `userId`, `authToken`, and `serverUrl` for the calling workspace are returned. + +## Lifecycle + +- Credentials are captured at the moment the Pexip call is opened +- They are held in memory only (never written to disk) +- They are cleared immediately when the video call window closes +- Each new call captures fresh credentials diff --git a/docs/pre-release-process.md b/docs/pre-release-process.md new file mode 100644 index 0000000000..5e96476af0 --- /dev/null +++ b/docs/pre-release-process.md @@ -0,0 +1,225 @@ +# Alpha and Beta Release Process + +This document describes how to create alpha and beta releases for QA testing and early customer access. + +## Overview + +The Rocket.Chat Desktop app supports three release channels: + +- **Stable** (`latest`) - Production releases for all users +- **Beta** - Pre-release testing with broader audience +- **Alpha** - Early testing for QA and select customers + +## How Channels Work + +| Channel | Version Format | Who Gets It | Update File | +| ------- | ---------------- | ------------------- | ------------ | +| Stable | `4.12.0` | All users (default) | `latest.yml` | +| Beta | `4.12.0-beta.1` | Beta opt-in users | `beta.yml` | +| Alpha | `4.12.0-alpha.1` | Alpha opt-in users | `alpha.yml` | + +**Channel hierarchy**: Alpha users receive alpha, beta, AND stable updates. Beta users receive beta AND stable. Stable users only receive stable. + +## Creating an Alpha Release + +### 1. Update Version on dev branch + +```bash +git checkout dev +git pull origin dev +``` + +Edit `package.json`: + +```json +{ + "version": "4.12.0-alpha.1" +} +``` + +Also increment `bundleVersion` in `electron-builder.json`. + +### 2. Commit and Push + +```bash +git add package.json electron-builder.json +git commit -m "chore: bump version to 4.12.0-alpha.1" +git push origin dev +``` + +### 3. Create and Push Tag + +```bash +git tag 4.12.0-alpha.1 +git push origin 4.12.0-alpha.1 +``` + +Or use the release-tag script: + +```bash +npx ts-node scripts/release-tag.ts +``` + +### 4. CI Builds Automatically + +The GitHub Actions workflow triggers on tag push and: + +- Builds for all platforms (Windows, macOS, Linux) +- Generates `alpha.yml`, `alpha-mac.yml`, `alpha-linux.yml` metadata +- Creates a draft GitHub release marked as **Pre-release** +- Publishes Linux snap to the `edge` channel + +### 5. Publish the Release + +1. Go to GitHub Releases +2. Find the draft release for your version +3. Review the release notes +4. Click "Publish release" + +## Creating a Beta Release + +Beta releases follow the same process as alpha, but use `-beta.X` suffix: + +```bash +git checkout dev +# Edit package.json to "4.12.0-beta.1" +git commit -am "chore: bump version to 4.12.0-beta.1" +git push origin dev +git tag 4.12.0-beta.1 +git push origin 4.12.0-beta.1 +``` + +## How Users Opt Into Alpha/Beta Channels + +### Option A: Via the App UI (Recommended) + +1. Open **Settings** in the app (gear icon) +2. Enable **Developer Mode** (scroll down to find it) +3. Go to **Help > About** (or **Rocket.Chat > About** on macOS) +4. You will see an **Update Channel** dropdown +5. Select the desired channel: + - **Stable** - Production releases only + - **Beta** - Beta and stable releases + - **Alpha (Experimental)** - Alpha, beta, and stable releases +6. Click **Check for Updates** + +The setting is persisted automatically and survives app restarts. + +### Option B: Configuration File (For Managed Deployments) + +Create `update.json` in the user data directory: + +| Platform | Location | +| -------- | ------------------------------------------------------- | +| Windows | `%APPDATA%\Rocket.Chat\update.json` | +| macOS | `~/Library/Application Support/Rocket.Chat/update.json` | +| Linux | `~/.config/Rocket.Chat/update.json` | + +Content for alpha channel: + +```json +{ + "channel": "alpha" +} +``` + +Content for beta channel: + +```json +{ + "channel": "beta" +} +``` + +For enterprise deployments where you want to force the setting (users cannot change it): + +```json +{ + "channel": "beta", + "forced": true +} +``` + +## Switching Channels + +### Switching to a pre-release channel (stable → alpha/beta) + +1. Open Settings > Enable Developer Mode +2. Open About dialog +3. Select the desired channel from dropdown +4. Click "Check for Updates" +5. The next pre-release version will be offered + +### Switching back to stable (alpha/beta → stable) + +1. Open About dialog +2. Select "Stable" from the Update Channel dropdown +3. Click "Check for Updates" + +**Important**: Switching to stable does NOT automatically downgrade the app. What happens: + +- If you're on `4.12.0-alpha.2` and switch to stable channel: + + - You will receive the next **stable** release (e.g., `4.12.0`) + - Semver considers `4.12.0` greater than `4.12.0-alpha.2`, so the stable release will be offered as an update + - You won't receive further alpha/beta releases until you switch back + +- If you need to immediately downgrade: + - Uninstall the current version + - Download and install the stable version from GitHub releases + +## Version Numbering Guidelines + +- **Alpha**: `4.12.0-alpha.1`, `4.12.0-alpha.2`, etc. +- **Beta**: `4.12.0-beta.1`, `4.12.0-beta.2`, etc. +- **Stable**: `4.12.0` + +When promoting: + +- Alpha `4.12.0-alpha.5` → Beta `4.12.0-beta.1` +- Beta `4.12.0-beta.3` → Stable `4.12.0` + +Typical release progression: + +```text +4.12.0-alpha.1 → 4.12.0-alpha.2 → 4.12.0-beta.1 → 4.12.0-beta.2 → 4.12.0 +``` + +## Safety Guarantees + +- Stable users **never** see alpha/beta releases (they check `latest.yml` only) +- Users must explicitly enable Developer Mode and select alpha/beta channel +- Alpha and beta releases are marked as "Pre-release" on GitHub +- Channel selection is persisted and survives restarts +- Users can switch channels at any time via the About dialog + +## Troubleshooting + +### Update not showing after channel switch + +1. Verify the release is published (not draft) on GitHub +2. Check that the corresponding `.yml` file exists in the release assets: + - Alpha: `alpha.yml`, `alpha-mac.yml`, `alpha-linux.yml` + - Beta: `beta.yml`, `beta-mac.yml`, `beta-linux.yml` +3. Click "Check for Updates" in the About dialog +4. Restart the app and try again + +### Channel dropdown not visible + +1. Make sure **Developer Mode** is enabled in Settings +2. Close and reopen the About dialog +3. Restart the app completely + +### Checking current channel + +With Developer Mode enabled, open the About dialog - the current channel is shown in the dropdown selector. + +### Where settings are stored + +The channel preference is stored in the app's config file: + +- **Windows**: `%APPDATA%\Rocket.Chat\config.json` +- **macOS**: `~/Library/Application Support/Rocket.Chat/config.json` +- **Linux**: `~/.config/Rocket.Chat/config.json` + +Look for the `updateChannel` key (values: `latest`, `beta`, or `alpha`). diff --git a/docs/qa-alpha-update-testing.md b/docs/qa-alpha-update-testing.md new file mode 100644 index 0000000000..17da6e5e97 --- /dev/null +++ b/docs/qa-alpha-update-testing.md @@ -0,0 +1,99 @@ +# QA Testing: Alpha Channel Updates + +This guide explains how to test the alpha update flow from `4.12.0-alpha.1` to `4.12.0-alpha.2`. + +## Prerequisites + +- Access to the GitHub releases page: https://github.com/RocketChat/Rocket.Chat.Electron/releases +- A clean test environment (no previous Rocket.Chat Desktop installation, or uninstall first) + +## Step 1: Download and Install Alpha 1 + +1. Go to [GitHub Releases](https://github.com/RocketChat/Rocket.Chat.Electron/releases) +2. Find the release **4.12.0-alpha.1** (marked as "Pre-release") +3. Download the installer for your platform: + - **Windows**: `rocketchat-4.12.0-alpha.1-win-x64.exe` + - **macOS**: `rocketchat-4.12.0-alpha.1-mac-universal.dmg` + - **Linux**: `rocketchat-4.12.0-alpha.1-linux-amd64.deb` or `.AppImage` +4. Install and launch the application + +## Step 2: Verify Alpha 1 Version + +1. Open the app +2. Go to **Help > About** (or **Rocket.Chat > About** on macOS) +3. Confirm the version shows **4.12.0-alpha.1** + +## Step 3: Enable Alpha Update Channel + +By default, the app checks for stable updates only. To receive alpha updates: + +1. Go to **Settings** (gear icon) +2. Enable **Developer Mode** (scroll down to find it) +3. Go back to **Help > About** +4. You should now see an **Update Channel** dropdown +5. Select **Alpha (Experimental)** from the dropdown + +## Step 4: Check for Updates + +1. In the About dialog, click **Check for Updates** +2. The app should find **4.12.0-alpha.2** as an available update +3. You should see a notification or dialog indicating the new version + +## Step 5: Install the Update + +1. Click **Download** or **Install** when prompted +2. Wait for the download to complete +3. The app will prompt you to restart to apply the update +4. Click **Restart** (or close and reopen the app) + +## Step 6: Verify Alpha 2 Version + +1. After restart, go to **Help > About** +2. Confirm the version now shows **4.12.0-alpha.2** + +## Expected Results + +| Step | Expected Behavior | +| --------------------- | ---------------------------------------- | +| Install Alpha 1 | App installs and runs without errors | +| About dialog | Shows version 4.12.0-alpha.1 | +| Enable Developer Mode | Update Channel dropdown appears in About | +| Select Alpha channel | Channel selection is saved | +| Check for Updates | Finds 4.12.0-alpha.2 | +| Download update | Downloads without errors | +| Restart | App restarts and applies update | +| Final version | Shows 4.12.0-alpha.2 | + +## Troubleshooting + +### Update not found + +- Verify **Developer Mode** is enabled in Settings +- Confirm **Alpha** channel is selected in the About dialog +- Check that `4.12.0-alpha.2` release is published (not draft) on GitHub +- Try clicking **Check for Updates** again + +### Update downloads but doesn't install + +- Check your system permissions (may need admin rights on Windows) +- Try manually closing and reopening the app +- Check the app logs for errors + +### Channel dropdown not visible + +- Make sure Developer Mode is enabled +- Close and reopen the About dialog +- Restart the app completely + +## Reporting Issues + +When reporting issues, please include: + +1. Operating system and version +2. Screenshot of the About dialog showing current version +3. Steps to reproduce the issue +4. Any error messages displayed +5. App logs if available: + - **Windows**: `%APPDATA%\Rocket.Chat\logs` + - **macOS**: `~/Library/Logs/Rocket.Chat` + - **Linux**: `~/.config/Rocket.Chat/logs` diff --git a/docs/supported-versions-flow.md b/docs/supported-versions-flow.md new file mode 100644 index 0000000000..9f18d13eaa --- /dev/null +++ b/docs/supported-versions-flow.md @@ -0,0 +1,593 @@ +# Version Support Data Loading & Caching Architecture + +## Overview + +This document describes how the desktop app fetches, caches, and uses version support data from Rocket.Chat servers. The system implements smart retry logic with per-server caching to handle network issues gracefully. + +**Key principle**: Block users only when we have **definitive proof** that their server version is unsupported. Allow access during data loading and when using fallback sources. + +--- + +## State Machine Flow + +The fetch process follows a state machine with 4 states: + +```mermaid +stateDiagram-v2 + [*] --> idle + + idle --> loading: updateSupportedVersionsData() + + loading --> success: Server/Cloud returns valid data + loading --> error: All sources fail, use fallback + + success --> loading: Manual refresh or retry + error --> loading: Retry after delay + + success --> [*] + error --> [*] + + note right of idle + Initial state, no data fetched yet + end note + + note right of loading + Fetching from Server/Cloud + User NOT blocked + end note + + note right of success + Fresh data from Server or Cloud + Apply blocking if unsupported + end note + + note right of error + Using fallback (cache or builtin) + Allow access (uncertain data) + end note +``` + +--- + +## Complete Fetch Sequence + +```mermaid +flowchart TD + Start["User opens serverfetchState = loading"] --> CheckServer{"Retry Server3x with 2s delay"} + + CheckServer -->|Success| ServerOK["Use Server dataSave to localStoragefetchState = success"] + ServerOK --> Dispatch1["Dispatch UPDATEDDone"] + + CheckServer -->|All 3 fail| CheckCloud{"Retry Cloud3x with 2s delay"} + + CheckCloud -->|Success| CloudOK["Use Cloud dataSave to localStoragefetchState = success"] + CloudOK --> Dispatch2["Dispatch UPDATEDDone"] + + CheckCloud -->|All 3 fail| CheckCache{"Try localStoragesupportedVersions:url"} + + CheckCache -->|Found| CacheOK["Use Cached datafetchState = error"] + CacheOK --> Dispatch3["Dispatch UPDATEDAllow access"] + + CheckCache -->|Not found| UseBuiltin["Use Builtin datafetchState = error"] + UseBuiltin --> Dispatch3 + + Dispatch1 --> EvalBlock + Dispatch2 --> EvalBlock + Dispatch3 --> EvalBlock + + EvalBlock{"Blocking DecisionisSupported === falseANDfetchState !== loading"} + + EvalBlock -->|No| AllowAccess["✅ ALLOW ACCESSUser can access webview"] + EvalBlock -->|Yes| BlockAccess["❌ BLOCK ACCESSShow UnsupportedServer overlay"] + + style Start fill:#e1f5ff + style ServerOK fill:#c8e6c9 + style CloudOK fill:#c8e6c9 + style CacheOK fill:#fff9c4 + style UseBuiltin fill:#fff9c4 + style AllowAccess fill:#c8e6c9 + style BlockAccess fill:#ffcdd2 +``` + +--- + +## Retry Logic Detail + +Each source (Server and Cloud) is retried 3 times with 2-second delays: + +```mermaid +sequenceDiagram + participant Desktop + participant Server + participant Delay + + Desktop->>+Server: Attempt 1 (fetch) + Server-->>-Desktop: Timeout/Error + Desktop->>Delay: Wait 2 seconds + Delay-->>Desktop: ✓ Ready + + Desktop->>+Server: Attempt 2 (fetch) + Server-->>-Desktop: Timeout/Error + Desktop->>Delay: Wait 2 seconds + Delay-->>Desktop: ✓ Ready + + Desktop->>+Server: Attempt 3 (fetch) + Server-->>-Desktop: Timeout/Error + Desktop->>Delay: Move to Cloud +``` + +**Total wait per source**: Up to 4 seconds (3 attempts with 2 waits × 2s delays) + +--- + +## Blocking Decision Logic + +```mermaid +graph TD + Check{"Is serverunsupported?"} + + Check -->|isSupported = true| Allow["✅ ALLOW"] + Check -->|isSupported = false| CheckState + + CheckState{"What isfetchState?"} + + CheckState -->|loading| Allow2["✅ ALLOWStill fetching, not proven"] + CheckState -->|success| Block["❌ BLOCKFresh data confirms unsupported"] + CheckState -->|error| Block2["❌ BLOCKBlock if fallback confirms unsupported; otherwise allow (uncertain data)"] + + style Allow fill:#c8e6c9 + style Allow2 fill:#c8e6c9 + style Block fill:#ffcdd2 + style Block2 fill:#ffcdd2 +``` + +--- + +## Validation Throttling (30-Minute Window) + +To reduce expensive validation checks on every navigation, the app implements a **30-minute throttle** on the `isServerVersionSupported()` validation logic (independent from the 12-hour dialog suppression). + +### How it Works + +```mermaid +timeline + title Validation Throttle Timeline + 0min: User navigates → Full validation runs + 0min: supportedVersionsValidatedAt = now + 10min: User navigates again → Check throttle + 10min: Time since last validation < 30min → SKIP validation + 10min: Still check 12-hour dialog suppression + 30min: User navigates → 30min has passed → Run full validation + 30min: supportedVersionsValidatedAt = now + 40min: User navigates → SKIP validation (within 30min window) + 12h: User navigates → Even within throttle, show dialog if suppression expired +``` + +### Throttle Behavior + +| Navigation | Time Since Last Validation | Action | +| --------------------- | -------------------------- | ----------------------------------------------- | +| First load | N/A | ✅ Run full validation | +| Within 30 minutes | < 30 min | ⏸️ Skip validation, check 12h timer | +| After 30 minutes | ≥ 30 min | ✅ Run full validation again | +| Dialog showing timing | Independent | ✅ Can show if 12h passed, even within throttle | + +### Benefits + +- **Reduces API load**: Validation runs at most once per 30 minutes per server +- **Maintains accuracy**: Fresh check every 30 minutes ensures data isn't stale +- **Preserves warning display**: 12-hour dialog suppression works independently +- **Navigation performance**: Repeated navigation within 30 min doesn't trigger expensive checks + +### When Throttle is Bypassed + +- ✅ Server reload (manual or forced) - Always validates fresh +- ✅ Server switch - Different server always validates +- ✅ Dialog dismissed - Triggers fetch, resets validation timer +- ✅ App startup (WEBVIEW_READY) - Always validates first time + +--- + +## Modal Display Logic + +The version support modal (warning dialog) appears independently from the blocking overlay. It shows version expiration information and is controlled by the `fetchState`: + +```mermaid +graph TD + Check{"Has versiondata available?"} + + Check -->|No| NoModal["❌ NO MODALsupportedVersions = undefined"] + Check -->|Yes| CheckFetch + + CheckFetch{"Is datacurrently loading?"} + + CheckFetch -->|loading| NoModal2["❌ NO MODALfetchState = loadingWait for data"] + CheckFetch -->|not loading| HasModal + + HasModal["✅ SHOW MODALif expiration warning exists(any data source: fresh, cached, builtin)"] + + style NoModal fill:#fff9c4 + style NoModal2 fill:#fff9c4 + style HasModal fill:#c8e6c9 +``` + +**Modal shows when**: + +- `supportedVersions` data exists AND +- `fetchState !== 'loading'` (not actively fetching) AND +- 12 hours have passed since last shown (independent timer) AND +- Server version is expiring soon (has expiration message) + +**Modal skips when**: + +- No data available (`supportedVersions` undefined) +- Currently fetching (`fetchState === 'loading'`) +- Within 12-hour display suppression window (shows max once per 12 hours) + +**Data sources that trigger modal**: + +- ✅ Fresh server data (`fetchState === 'success'`) +- ✅ Fresh cloud data (`fetchState === 'success'`) +- ✅ Stale cached data (`fetchState === 'error'`) +- ✅ Generic builtin fallback (`fetchState === 'error'`) + +**Key difference from blocking overlay**: + +- **Overlay blocks access** - only when definitely unsupported with fresh data +- **Modal warns users** - shows expiration info from any available data, but not while actively loading +- **Modal has separate suppression** - shows max once per 12 hours independent of validation throttle + +--- + +## Per-Server State Structure + +```typescript +// Redux state for each server +{ + url: "https://chat.example.com", + version: "7.1.0", + + // Version support data + supportedVersionsData: SupportedVersions | undefined, + supportedVersionsFetchState: 'idle' | 'loading' | 'success' | 'error', + isSupportedVersion: boolean | undefined, + supportedVersionsValidatedAt?: Date, // Timestamp of last validation (for 30-min throttle) + + // Warning display management + expirationMessageLastTimeShown?: Date, // 12-hour dialog suppression timer + + // Other fields... + title: string, + failed: boolean, + // ... +} + +// localStorage structure (per server) +localStorage['supportedVersions:https://chat.example.com'] = { + versions: [...], + exceptions: {...}, + timestamp: "2024-10-31T...", + enforcementStartDate: "2024-11-15T..." +} +``` + +**New field `supportedVersionsValidatedAt`**: + +- Tracks when the last version validation check occurred +- Used to implement 30-minute validation throttle (see "Validation Throttling" section) +- Independent from the 12-hour dialog suppression timer + +--- + +## Scenario Examples + +### Scenario 1: Fast Network (Server Responds) + +```mermaid +timeline + title Fast Network - Server Available + 0s: User opens server → fetchState = loading + 1s: Server responds ✓ + 1s: Save to localStorage + 1s: fetchState = success + 1s: Evaluate blocking + 1s: User sees webview (or overlay if unsupported) +``` + +**Total time**: ~1 second + +--- + +### Scenario 2: Slow Server + Fast Cloud + +```mermaid +timeline + title Slow Server - Cloud Available + 0s: User opens server → fetchState = loading + 2s: Server attempt 1 timeout + 2s: Wait 2s + 4s: Server attempt 2 timeout + 4s: Wait 2s + 6s: Server attempt 3 timeout + 6s: Try Cloud + 7s: Cloud responds ✓ + 7s: Save to localStorage + 7s: fetchState = success + 7s: User sees webview +``` + +**Total time**: ~7 seconds + +--- + +### Scenario 3: Offline with Cached Data + +```mermaid +timeline + title Offline - Cache Available + 0s: User opens server → fetchState = loading + 2s: Server attempt 1 timeout + 2s: Wait 2s + 4s: Server attempt 2 timeout + 4s: Wait 2s + 6s: Server attempt 3 timeout + 6s: Try Cloud + 8s: Cloud attempt 1 timeout + 8s: Wait 2s + 10s: Cloud attempt 2 timeout + 10s: Wait 2s + 12s: Cloud attempt 3 timeout + 12s: Try localStorage + 12s: Found cached data ✓ + 12s: fetchState = error + 12s: User sees webview (using cache) +``` + +**Total time**: ~12 seconds +**Result**: User can work, using last-known-good data + +--- + +### Scenario 4: Offline + No Cache + Builtin Blocks + +```mermaid +timeline + title Offline, No Cache, Unsupported + 0s: User opens server → fetchState = loading + 6s: Server retries fail + 12s: Cloud retries fail + 12s: localStorage check → not found + 12s: Use Builtin + 12s: Builtin says unsupported + 12s: fetchState = error + 12s: Blocking condition met + 12s: UnsupportedServer overlay shown +``` + +**Total time**: ~12 seconds +**Result**: User blocked (builtin confirms unsupported) + +--- + +### Scenario 5: Airgapped Network (Server Available) + +```mermaid +timeline + title Airgapped - Local Server Only + 0s: User opens server → fetchState = loading + 1s: Server responds ✓ + 1s: Save to localStorage + 1s: fetchState = success + 1s: Cloud not attempted (server succeeded) + 1s: User sees webview +``` + +**Total time**: ~1 second +**Key**: Cloud retries skipped because Server already succeeded + +--- + +## Data Flow Architecture + +```mermaid +graph LR + A["Update triggered(WEBVIEW_READY,manual refresh)"] + + A --> B["DispatchLOADING"] + B --> C["Set fetchState= loading"] + + C --> D["updateSupportedVersionsData()Start fetch sequence"] + + D --> E1["Retry Server3x with delays"] + E1 -->|Success| F["Dispatch UPDATEDfetchState = success"] + E1 -->|Fail| E2["Retry Cloud3x with delays"] + + E2 -->|Success| F + E2 -->|Fail| E3["Load localStorage"] + + E3 -->|Found| G["Dispatch UPDATEDfetchState = error"] + E3 -->|Not found| E4["Use Builtinalways available"] + E4 --> G + + F --> H["Update Redux state"] + G --> H + + H --> I["Component re-renders"] + I --> J{"Blockingcondition?"} + + J -->|No| K["Show webview"] + J -->|Yes| L["Show UnsupportedServeroverlay"] + + style A fill:#e1f5ff + style B fill:#e1f5ff + style C fill:#e1f5ff + style D fill:#fff3e0 + style E1 fill:#fff3e0 + style E2 fill:#fff3e0 + style E3 fill:#fff9c4 + style E4 fill:#fff9c4 + style F fill:#c8e6c9 + style G fill:#fff9c4 + style H fill:#e1f5ff + style K fill:#c8e6c9 + style L fill:#ffcdd2 +``` + +--- + +## Component Integration + +```mermaid +graph TD + SV["ServersViewmaps servers"] + SP["ServerPaneper server UI"] + US["UnsupportedServeroverlay component"] + + SV --> SP + SP --> US + + Props["Props passed down:- isSupportedVersion- supportedVersionsFetchState"] + + SP -.->|passes| Props + Props --> US + + US --> Logic["if fetchState !== 'loading'AND isSupported === false→ show overlay"] + + style SV fill:#e1f5ff + style SP fill:#e3f2fd + style US fill:#f3e5f5 + style Logic fill:#ffcdd2 +``` + +--- + +## URL Encoding & Cloud API Requirements + +### URL Parameter Encoding + +The app constructs API URLs with proper URL encoding to ensure reliable server communication: + +```typescript +// Query parameter for uniqueID lookup (< v7.0.0) +// ✅ Properly encoded: +`${serverUrl}api/v1/settings.public?query=${encodeURIComponent(JSON.stringify({ _id: 'uniqueID' }))}`; +// Results in: ?query=%7B%22_id%22%3A%20%22uniqueID%22%7D + +// ❌ Would fail if unencoded (causes "Invalid query parameter" errors): +// `${serverUrl}api/v1/settings.public?query={"_id": "uniqueID"}` +``` + +**Why encoding matters**: + +- Servers validate query parameters strictly +- Special characters in JSON (quotes, braces) must be URL-encoded +- Without encoding, older Rocket.Chat versions (especially v6.x) reject the request +- With proper encoding, all server versions handle it reliably + +### Cloud API Domain Requirement + +The Cloud API endpoint requires a valid **domain** (not IP address) to look up server-specific version policies: + +```text +https://releases.rocket.chat/v2/server/supportedVersions?domain={domain}&uniqueId={uniqueId}&source=desktop +``` + +**Domain requirement**: + +- ✅ Works: `domain=chat.company.com` +- ✅ Works: `domain=ngrok-provided-url.ngrok.io` (tunneled service) +- ❌ Fails: `domain=192.168.1.100:3000` (IP addresses not recognized by Cloud API) +- ❌ Fails: `domain=192.168.1.100:3000:3620` (malformed) + +**Workarounds for local/internal servers**: + +1. Use a reverse proxy with domain routing (ngrok, Caddy, nginx) +2. Add DNS record pointing to the server IP +3. Configure local `/etc/hosts` entry if only for local access + +**Impact when domain is unavailable**: + +- Cloud API returns generic/default version policies +- Server may not be correctly identified +- Version validation uses fallback data (cache or builtin) +- May not reflect server-specific policies + +--- + +## Cache Management + +### Storage Strategy + +- **Key format**: `supportedVersions:${serverUrl}` +- **Value**: Complete `SupportedVersions` object with timestamp +- **Per-server**: Each server has its own cache entry +- **On success**: Automatically saved to localStorage +- **On failure**: Automatically loaded if available + +### Lifecycle + +```mermaid +stateDiagram-v2 + [*] --> NoCache: App loadsno data + + NoCache --> Fetch: User opens server + Fetch --> Success: Server/Cloud succeeds + Fetch --> NoCacheFail: All fail + + Success --> Cached: Save to localStorage + + NoCacheFail --> Builtin: Use builtin + Builtin --> Cached2: If builtin saved + + Cached --> Ready: Data available + Cached2 --> Ready + + Ready --> UseCached: On next openor on fetch fail + UseCached --> Ready + + note right of Cached + Data persists across + app sessions + end note + + note right of UseCached + Faster recovery on + network issues + end note +``` + +--- + +## Summary + +### Fetch Scenarios + +| Scenario | Server | Cloud | Cache | Builtin | Result | Time | +| ------------------ | ------ | ----- | ----- | ------- | ---------------------------- | ---- | +| Fast network | ✓ | - | - | - | Allow/Block based on data | ~1s | +| Slow server | ✗ | ✓ | - | - | Allow/Block based on data | ~7s | +| Offline + cache | ✗ | ✗ | ✓ | - | ALLOW (uncertain) | ~10s | +| Offline + no cache | ✗ | ✗ | ✗ | ✓ | Allow/Block based on builtin | ~12s | +| Airgapped | ✓ | ✗ | - | - | Allow/Block (Cloud skipped) | ~1s | + +### Validation Throttle Scenarios + +| Scenario | Last Validation | Time Elapsed | Action | +| ------------------------ | --------------- | ------------ | ------------------------------------ | +| First navigation | None | N/A | ✅ Run full validation | +| Subsequent nav (quick) | 5 min ago | 5 min | ⏸️ Skip validation (within 30 min) | +| Subsequent nav (delayed) | 35 min ago | 35 min | ✅ Run validation (30 min passed) | +| Server reload | Any time | Any time | ✅ Always validate (bypass throttle) | +| Dialog dismissed | Any time | Any time | ✅ Always validate (bypass throttle) | + +### Key Principles + +1. **Blocking decision**: Block only on fresh `success` state with confirmed unsupported version. Allow on `loading` state. On `error` state, block if fallback data (cache or builtin) confirms unsupported version. + +2. **Validation throttle**: Expensive validation checks run max once per 30 minutes per server. Reduces API load while maintaining accuracy. + +3. **Warning display**: Modal shows max once per 12 hours (independent of validation throttle). Can appear even within 30-minute validation window if 12 hours have passed. + +4. **False positive prevention**: With proper URL encoding and Cloud API integration, supported servers no longer show false unsupported warnings due to transient failures. + +5. **Fallback reliability**: Multiple fallback sources (cache, builtin) ensure users can always access servers, even in offline scenarios. diff --git a/docs/video-call-screen-sharing.md b/docs/video-call-screen-sharing.md new file mode 100644 index 0000000000..2d3bec6080 --- /dev/null +++ b/docs/video-call-screen-sharing.md @@ -0,0 +1,485 @@ +# Video Call Screen Sharing + +## Overview + +This document shows how screen sharing works within an active video call in Rocket.Chat Electron. + +## Cache Architecture + +The screen sharing system uses a **stale-while-revalidate** caching pattern for optimal performance: + +``` +Desktop Capturer Cache +├── Always returns cached data immediately (instant UX) +├── If data older than 3s, triggers background refresh +├── Next request gets fresh data +└── Cache persists indefinitely (never expires) +``` + +## Screen Sharing Flow + +```mermaid +flowchart TD + %% Pre-warming Phase (on Video Call Load) + START[Video Call Window Loaded] --> PW1[did-finish-load Event] + PW1 --> PW2[Trigger prewarm-capturer-cache IPC] + PW2 --> PW3[Call refreshDesktopCapturerCache] + PW3 --> PW4[Fetch Sources in Background] + PW4 --> PW5[Populate Cache with Timestamp] + PW5 --> PW6[Cache Ready for Instant Access] + + %% Screen Sharing Initiation + A[Video Call Active] --> B[User Requests Screen Share] + B --> C[Webview Calls requestScreenSharing API] + C --> D[Preload Script Receives Request] + + %% IPC to Renderer + D --> E[Send video-call-window/open-screen-picker IPC] + E --> F[Renderer Receives Request] + F --> G{ScreenSharePicker Mounted?} + G -->|No| G1[Lazy Import React Module] + G1 --> G2[Mount ScreenSharePicker Hidden] + G -->|Yes| H[Show ScreenSharePicker] + G2 --> H + + %% Stale-While-Revalidate Cache + H --> I[Request Desktop Capturer Sources] + I --> J{Cache Exists?} + J -->|No| K[First Fetch - Wait for Sources] + J -->|Yes| L[Return Cached Sources Immediately] + + %% Staleness Check + L --> M{Cache Age > 3 seconds?} + M -->|No| N[Use Fresh Cache] + M -->|Yes| O[Trigger Background Refresh] + O --> P[refreshDesktopCapturerCache Async] + P --> Q[Fetch New Sources] + Q --> R[Update Cache with New Data] + R --> S[Next Request Gets Fresh Data] + N --> T[Display Sources in UI] + L --> T + + %% First Fetch Path + K --> K1[Call desktopCapturer.getSources] + K1 --> K2[System Returns Sources] + K2 --> K3[Filter Invalid Sources] + K3 --> K4[Cache Sources with Timestamp] + K4 --> T + + %% Source Selection UI + T --> U[Display Sources in Tabs] + U --> V[Windows Tab - Application Windows] + U --> W[Screens Tab - Desktop Displays] + V --> X[User Selects Window] + W --> Y[User Selects Screen] + X --> Z[Validate Selected Source] + Y --> Z + + %% Source Validation with Caching + Z --> AA{Source in Validation Cache?} + AA -->|Yes - Within 30s| AB[Return Cached Validation] + AA -->|No| AC[Validate Source Availability] + AC --> AD[Check Source Still Exists] + AD --> AE{Source Valid?} + AE -->|Yes| AF[Cache Validation 30s] + AE -->|No| AG[Show Source Unavailable Error] + AF --> AB + AB --> AH[Return Source ID] + + %% IPC Response to Webview + AH --> AI[Send Source ID via IPC Response] + AI --> AJ[Preload Script Receives Source ID] + AJ --> AK[Return Source to Webview] + AK --> AL[Webview Gets Stream Access] + AL --> AM[Screen Share Active] + + %% Error Recovery + AG --> AN[User Selects Different Source] + AN --> Z + + %% User Closes Picker Without Selecting + H --> AO[User Closes Picker] + AO --> AP[Hide ScreenSharePicker] + AP --> AQ[Component Stays Mounted] + AQ --> A + + %% Cache Persistence + AM --> AR[Video Call Continues] + AR --> AS{User Requests Screen Share Again?} + AS -->|Yes| B + AS -->|No| AT[Video Call Ends] + AT --> AU[Window Closes] + AU --> AV[Cache Persists in Memory] + AV --> AW{App Quit or Error?} + AW -->|Yes| AX[Clear All Caches] + AW -->|No| AY[Cache Available for Next Call] + + %% Background Refresh Details + P --> BA{Fetch Already in Progress?} + BA -->|Yes| BB[Skip - Deduplication] + BA -->|No| BC[Start Async Fetch] + BC --> Q + + %% Styling + classDef mainProcess fill:#e1f5fe,stroke:#0277bd,stroke-width:2px + classDef renderer fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px + classDef webview fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px + classDef cache fill:#e3f2fd,stroke:#1976d2,stroke-width:2px + classDef ui fill:#fff3e0,stroke:#f57c00,stroke-width:2px + classDef validation fill:#f3e5f5,stroke:#8e24aa,stroke-width:2px + classDef error fill:#ffebee,stroke:#c62828,stroke-width:2px + classDef success fill:#e8f5e8,stroke:#388e3c,stroke-width:2px + classDef prewarm fill:#fff8e1,stroke:#ffa000,stroke-width:2px + + class PW2,PW3,K1,K2,K3,AI mainProcess + class D,E,F,G,G1,G2,AJ,AK renderer + class A,C,AL,AM,AR webview + class I,J,K4,L,M,N,O,P,Q,R,S,BA,BB,BC,AV,AW,AX,AY cache + class H,T,U,V,W,X,Y,AO,AP,AQ ui + class Z,AA,AB,AC,AD,AE,AF validation + class AG,AN error + class AH,AL,AM success + class START,PW1,PW2,PW3,PW4,PW5,PW6 prewarm +``` + +## Explanation + +**What this diagram shows:** + +- Cache pre-warming when video call loads +- Stale-while-revalidate pattern for instant display +- Background refresh keeping thumbnails current +- Deferred React loading for the picker component + +**Key parts:** + +1. **Pre-warming** - Cache populated when video call loads +2. **Instant Display** - Always returns cached data immediately +3. **Background Refresh** - Updates stale cache without blocking UI +4. **Source Selection** - Tabbed interface for windows and screens +5. **Validation** - Ensures selected source still exists +6. **Persistence** - Cache survives window close for next call + +**Cache Behavior:** + +- **Stale threshold**: 3 seconds +- **Validation cache**: 30 seconds +- **Expiration**: Never (persists until app quit or error) +- **Memory**: Varies based on number of sources and thumbnail sizes + +**Color Guide:** + +- **Blue** - Main process operations +- **Purple** - Renderer process & Validation +- **Green** - Webview & Success states +- **Light Blue** - Cache operations +- **Orange** - User interface elements +- **Yellow** - Pre-warming phase +- **Red** - Error states + +## Detailed Step-by-Step Explanation + +### 1. Cache Pre-warming + +When the video call webview finishes loading, the app proactively populates the cache. + +**What happens:** + +- `did-finish-load` event triggers pre-warming +- `video-call-window/prewarm-capturer-cache` IPC called +- `refreshDesktopCapturerCache()` fetches sources in background +- Cache populated before user ever clicks screen share +- First screen picker open shows sources instantly + +### 2. Starting Screen Sharing + +When you click the screen share button during a video call. + +**What happens:** + +- Video call website detects your click +- Calls the screen sharing API provided by the preload script +- IPC message sent to renderer process +- ScreenSharePicker component shown (or mounted if first time) + +### 3. Deferred React Loading + +The ScreenSharePicker React component is loaded lazily. + +**What happens:** + +- First screen share request triggers dynamic import +- `screenSharePickerMount.tsx` loaded asynchronously +- React component mounted and kept in DOM +- Subsequent opens just toggle visibility (instant) + +### 4. Stale-While-Revalidate Cache + +The core caching strategy that makes screen sharing feel instant. + +**What happens:** + +- Request comes in for desktop capturer sources +- **If cache exists**: Return immediately (no waiting) +- **If cache older than 3s**: Trigger background refresh +- **Background refresh**: Fetches new sources asynchronously +- **Next request**: Gets the fresh data +- **Cache never expires**: Only cleared on error or app quit + +**Example flow (illustrative timings):** + +``` +T=0s: User opens picker → Cache returns instantly (2s old) +T=0s: Background refresh starts (async) +T=1s: Background refresh completes, cache updated +T=5s: User opens picker again → Gets fresh data instantly +``` + +### 5. Source Filtering and Validation + +Sources are filtered and validated before display. + +**Filtering (during fetch):** + +- Removes sources with empty names +- Removes sources with empty thumbnails +- Only valid sources cached + +**Validation (on selection):** + +- Checks if selected source still exists +- Cached for 30 seconds to avoid repeated checks +- Shows error if source no longer available + +### 6. Source Selection UI + +Clean tabbed interface for choosing what to share. + +**What happens:** + +- **Windows tab**: Shows application windows with thumbnails +- **Screens tab**: Shows connected displays +- Preview thumbnails help identify correct source +- Click to select, picker closes automatically + +### 7. Cache Persistence + +Cache survives beyond the video call window lifecycle. + +**What happens:** + +- Video call window closes +- Cache remains in memory +- Next video call benefits from existing cache +- Only cleared on: + - Application quit + - Explicit cleanup call + - Fetch error (keeps last good data on error) + +### 8. Promise Deduplication + +Prevents multiple simultaneous fetches. + +**What happens:** + +- If fetch already in progress, new requests wait for it +- Single system call even with rapid requests +- Reduces CPU and memory pressure + +## Why Stale-While-Revalidate? + +### Previous Approach (TTL-based) + +``` +Cache hit within 3s → Return cached data +Cache expired → Fetch new data (blocking) +Window close → Schedule 60s cleanup timer +``` + +**Problems:** + +- Empty state possible if cache expired +- Blocking fetch when cache expired +- Complex cleanup logic + +### Current Approach (Stale-While-Revalidate) + +``` +Cache exists → Return immediately (always instant) +Cache stale → Background refresh (non-blocking) +Window close → Keep cache (no cleanup timer) +``` + +**Benefits:** + +- Never shows empty/loading state +- Thumbnails stay current through background refresh +- Simpler code, better UX + +## Performance Characteristics + +### Response Times + +| Scenario | Behavior | +| --------------------- | --------------------------------------------- | +| Pre-warmed cache | Instant (no delay) | +| Cache hit (fresh) | Instant (no delay) | +| Cache hit (stale) | Instant return with background refresh | +| Cold start (no cache) | Requires initial system call to fetch sources | + +### Memory Usage + +| Component | Behavior | +| ---------------- | ------------------------------------------------------ | +| Source metadata | Minimal overhead per source | +| Thumbnails | Varies with number of sources and thumbnail resolution | +| Validation cache | Negligible overhead | + +### Cache Lifecycle + +``` +App Start + │ + ▼ +Video Call Opens + │ + ├─► did-finish-load → Pre-warm cache + │ + ▼ +Screen Share Request + │ + ├─► Return cached (instant) + ├─► Background refresh if stale + │ + ▼ +Video Call Closes + │ + ├─► Cache persists + │ + ▼ +Next Video Call + │ + ├─► Cache still available + │ + ▼ +App Quit + │ + └─► Cache cleared +``` + +## Technical Implementation + +### Stale-While-Revalidate Logic + +```typescript +const DESKTOP_CAPTURER_STALE_THRESHOLD = 3000; + +handle('desktop-capturer-get-sources', async (_webContents, opts) => { + const options = Array.isArray(opts) ? opts[0] : opts; + + if (desktopCapturerCache) { + const isStale = + Date.now() - desktopCapturerCache.timestamp > + DESKTOP_CAPTURER_STALE_THRESHOLD; + if (isStale && !desktopCapturerPromise) { + refreshDesktopCapturerCache(options); // Fire and forget + } + return desktopCapturerCache.sources; // Always return immediately + } + + // No cache - wait for initial fetch + refreshDesktopCapturerCache(options); + if (desktopCapturerPromise) { + return await desktopCapturerPromise; + } + return []; +}); +``` + +### Background Refresh Function + +```typescript +const refreshDesktopCapturerCache = ( + options: Electron.SourcesOptions +): void => { + if (desktopCapturerPromise) return; // Deduplication + + desktopCapturerPromise = (async () => { + try { + const sources = await desktopCapturer.getSources(options); + const validSources = sources.filter(/* validation logic */); + + desktopCapturerCache = { + sources: validSources, + timestamp: Date.now(), + }; + return validSources; + } catch (error) { + console.error('Background cache refresh failed:', error); + return desktopCapturerCache?.sources || []; // Keep last good data + } finally { + desktopCapturerPromise = null; + } + })(); +}; +``` + +### Pre-warming Handler + +```typescript +handle('video-call-window/prewarm-capturer-cache', async () => { + refreshDesktopCapturerCache({ types: ['window', 'screen'] }); + return { success: true }; +}); +``` + +### Source Validation Cache + +```typescript +const SOURCE_VALIDATION_CACHE_TTL = 30000; + +// Validates selected source still exists +// Cached for 30 seconds to avoid repeated system calls +const sourceValidationCache: Set = new Set(); +let sourceValidationCacheTimestamp = 0; + +const cacheExpired = + now - sourceValidationCacheTimestamp > SOURCE_VALIDATION_CACHE_TTL; + +if (!cacheExpired && sourceValidationCache.has(source.id)) { + return { isValid: true }; // Use cached validation +} + +// Re-validate and update cache +sourceValidationCache.clear(); +sourceValidationCache.add(source.id); +sourceValidationCacheTimestamp = now; +``` + +## User Experience Benefits + +### Instant Screen Picker + +- Cache pre-warmed when video call loads +- First open shows sources immediately +- No loading spinner or empty state + +### Current Thumbnails + +- Background refresh keeps previews up-to-date +- User always sees accurate window previews +- Changes detected within 3 seconds + +### Seamless Repeat Use + +- Second screen share opens instantly +- Cache persists between calls +- No degradation over session length + +### Reliable Selection + +- 30-second validation cache prevents repeated checks +- Clear error message if source unavailable +- Easy recovery with different selection + +This caching architecture ensures screen sharing is fast, reliable, and provides an excellent user experience throughout the video call session. diff --git a/docs/video-call-window-flow.md b/docs/video-call-window-flow.md new file mode 100644 index 0000000000..292c115281 --- /dev/null +++ b/docs/video-call-window-flow.md @@ -0,0 +1,213 @@ +# Video Call Window Documentation + +## Overview + +The video call window system in Rocket.Chat Electron provides a dedicated, high-performance environment for video conferencing with any provider (Jitsi, PEXIP, self-hosted, and others). The core system is provider-agnostic and works with any HTTP/HTTPS video call URL. + +## Architecture + +The system uses a **vanilla JavaScript bootstrap** with **deferred React loading**: + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Video Call Window │ +├─────────────────────────────────────────────────────────────┤ +│ video-call-window.ts (Vanilla JS) │ +│ ├── i18n initialization │ +│ ├── Webview lifecycle management │ +│ ├── Loading/error overlays (DOM) │ +│ └── IPC communication │ +├─────────────────────────────────────────────────────────────┤ +│ screenSharePickerMount.tsx (React - Lazy Loaded) │ +│ └── ScreenSharePicker component │ +├─────────────────────────────────────────────────────────────┤ +│ Desktop Capturer Cache (Stale-While-Revalidate) │ +│ ├── Pre-warmed on webview load │ +│ ├── Instant response, background refresh │ +│ └── Persists across calls │ +└─────────────────────────────────────────────────────────────┘ +``` + +**Key design decisions:** + +- Vanilla JS for critical path (faster load, simpler recovery) +- React only for ScreenSharePicker (complex UI component) +- Stale-while-revalidate caching (instant UX, fresh data) +- Cache pre-warming (no loading state on first screen share) + +## Documentation Files + +### [Window Management Flow](./video-call-window-management.md) + +**What it covers:** Complete lifecycle from click to active video call. + +This document explains the vanilla JS architecture that manages the video call window. It shows how the app creates a separate window, initializes without React overhead, handles loading states through direct DOM manipulation, and provides automatic error recovery. + +**You'll learn about:** + +- Vanilla JS bootstrap architecture +- Why React is deferred (performance benefits) +- Webview creation and attribute ordering +- Loading and error overlay management +- Smart loading system (no flicker, no interruption) +- Progressive error recovery strategies +- Window cleanup and resource management + +**Architecture highlights:** + +- No React in critical path +- Direct DOM manipulation for overlays +- Deferred React import for screen sharing +- Cache pre-warming on load completion + +### [Screen Sharing Flow](./video-call-screen-sharing.md) + +**What it covers:** Screen sharing with stale-while-revalidate caching. + +This document explains the caching architecture that makes screen sharing feel instant. It shows how the cache is pre-warmed when the video call loads, how stale data is returned immediately while fresh data is fetched in the background, and why the cache persists indefinitely. + +**You'll learn about:** + +- Stale-while-revalidate pattern +- Cache pre-warming strategy +- Why cache never expires +- Background refresh mechanics +- Deferred React loading for picker +- Source validation and filtering +- Memory usage characteristics + +**Cache characteristics:** + +- Always returns data immediately +- Background refresh when stale (>3s) +- Persists until app quit or error + +### [WGC Limitations](./video-call-window-wgc-limitations.md) + +**What it covers:** Windows Graphics Capture limitations and workarounds. + +This document explains why screen sharing fails in Remote Desktop (RDP) sessions and how the app handles this through automatic detection and fallback mechanisms. + +## How They Work Together + +### The Complete User Journey + +``` +1. User clicks video call button + └─► Window Management Flow + ├── Create BrowserWindow + ├── Vanilla JS bootstrap + ├── Load webview with provider URL + └── Pre-warm desktop capturer cache + +2. Video call active + └─► User clicks screen share + └─► Screen Sharing Flow + ├── Show ScreenSharePicker (React, lazy loaded) + ├── Return cached sources instantly + ├── Background refresh if stale + └── Stream selected source +``` + +### Real-World Example + +**Starting a call:** + +1. Click "Join Video Call" in Rocket.Chat +2. Window Management Flow creates dedicated window +3. Vanilla JS shows loading overlay with localized text +4. Webview loads video call provider interface (Jitsi, PEXIP, or any provider) +5. Cache pre-warmed in background +6. Loading overlay hidden, video call visible +7. If Jitsi: jitsiBridge auto-detects and initializes (required for screen sharing) + +**Sharing your screen:** + +1. Click screen share button in video call +2. ScreenSharePicker React component loaded (first time) or shown +3. Cached sources displayed instantly (no loading spinner) +4. Background refresh updates thumbnails if stale +5. Select window or screen +6. Stream starts immediately + +## Key Innovations + +### Vanilla JS Bootstrap + +Traditional Electron apps render everything with React. This video call window uses vanilla JavaScript for the critical path: + +- **Faster startup**: No React bundle to parse/execute initially +- **Simpler recovery**: Direct DOM manipulation, no state management +- **Lower memory**: React only loaded when needed + +### Stale-While-Revalidate Cache + +Traditional caching uses TTL (time-to-live) expiration. This system uses stale-while-revalidate: + +- **Always instant**: Returns cached data immediately +- **Always fresh**: Background refresh keeps data current +- **Never empty**: Cache persists, no expiration +- **Error resilient**: Keeps last good data on fetch failure + +### Cache Pre-warming + +Traditional apps fetch data on demand. This system pre-warms: + +- **Proactive fetch**: Cache populated when webview loads +- **Zero wait**: First screen share shows sources instantly +- **Background operation**: No impact on video call startup + +## Provider Support + +The video call window is designed to work with any video conferencing provider. The core system is generic and provider-agnostic: + +### Generic Support (All Providers) + +- **URL validation**: Only checks HTTP/HTTPS protocol (no provider-specific patterns) +- **Webview lifecycle**: Standard Electron webview handling +- **Screen sharing**: Uses generic Electron desktop capturer API +- **Partition**: Uses generic `persist:video-call-session` partition + +### Jitsi-Specific Requirements + +For Jitsi servers, a required bridge module (`jitsiBridge.ts`) is essential for screen sharing: + +- **Auto-detection**: Automatically detects Jitsi meetings and initializes integration +- **Screen sharing coordination**: Required for screen sharing to work with Jitsi's External API +- **Event handling**: Listens for Jitsi-specific events and messages + +**Important**: The jitsiBridge is **required** for Jitsi calls - screen sharing will not work without it. Other providers (like PEXIP) work with the standard generic events and do not need a bridge. + +### PEXIP and Other Providers + +PEXIP and other providers work seamlessly with the generic event system: + +- No special bridge code required +- Uses standard webview events and IPC communication +- Screen sharing works through the generic desktop capturer API +- Easier to integrate than Jitsi (no bridge needed) + +## File Structure + +``` +src/videoCallWindow/ +├── video-call-window.ts # Vanilla JS bootstrap (generic) +├── screenSharePicker.tsx # React screen picker UI (generic) +├── screenSharePickerMount.tsx # React mounting utilities (generic) +├── ipc.ts # Main process handlers (generic) +└── preload/ + ├── index.ts # Webview preload script (generic) + └── jitsiBridge.ts # Jitsi-specific bridge (required for Jitsi screen sharing) + +src/public/ +├── video-call-window.html # Window HTML with overlay containers +├── loading.css # Loading overlay styles +└── error.css # Error overlay styles +``` + +## Related Documentation + +- [Supported Versions Flow](./supported-versions-flow.md) - Server version compatibility +- [Testing Documentation](./testing/) - Test procedures and guidelines + +Both window management and screen sharing systems work together seamlessly to provide a fast, reliable, and user-friendly video calling experience. diff --git a/docs/video-call-window-management.md b/docs/video-call-window-management.md new file mode 100644 index 0000000000..0d5061168a --- /dev/null +++ b/docs/video-call-window-management.md @@ -0,0 +1,415 @@ +# Video Call Window Management + +## Overview + +This document shows how the video call window is created and managed in Rocket.Chat Electron. + +## Architecture + +The video call window uses a **vanilla JavaScript bootstrap** architecture for optimal performance: + +```text +video-call-window.ts (Vanilla JS) +├── i18n initialization +├── Webview creation and lifecycle management +├── Loading/error overlays (vanilla DOM manipulation) +└── Deferred React import for ScreenSharePicker only +``` + +**Key benefits:** + +- Faster initial load (no React bundle required for core functionality) +- Simpler error recovery (direct DOM manipulation) +- React only loaded when screen sharing is needed +- Reduced memory footprint + +## Window Management Flow + +```mermaid +flowchart TD + %% Main Process Entry Point + A[User Initiates Video Call] --> B[Main Process: video-call-window/open-window IPC] + + %% URL Validation + B --> C{URL Valid?} + C -->|Invalid| C1[Reject Request] + C -->|Google URL| C2[Open External Browser] + C -->|Valid| D[Check Existing Window] + + %% Window Management + D --> E{Window Exists?} + E -->|Yes| F[Close Existing Window] + E -->|No| G[Create New BrowserWindow] + F --> F1[Wait for Destruction] + F1 --> G + + %% Window Creation + G --> H[Configure Window Properties] + H --> I[Set Permissions & Handlers] + I --> J[Load video-call-window.html] + + %% Renderer Process - Vanilla JS Bootstrap + J --> K[DOM Ready Event] + K --> L[Execute JavaScript Test] + L --> M[video-call-window.ts Starts] + + %% Vanilla JS Initialization + M --> N[Initialize i18n] + N --> O[Show Loading Overlay] + O --> P[IPC Handshake with invokeWithRetry] + + %% IPC Handshake with Retry + P --> Q{Handshake Success?} + Q -->|No| Q1[Retry with 1s Delay - 3 attempts] + Q1 --> Q2{Max Attempts?} + Q2 -->|No| P + Q2 -->|Yes| Q3[Show Fallback UI] + Q -->|Yes| R[Signal Renderer Ready] + + %% URL Request + R --> S[Request Pending URL with invokeWithRetry] + S --> S1{URL Request Success?} + S1 -->|IPC Error| S2[URL Retry 1s Delay - 3 attempts] + S2 --> S3{URL Retry Attempts Left?} + S3 -->|Yes| S + S3 -->|No| S4[Fall Back to Full Retry] + S4 --> Q1 + S1 -->|No URL Yet| S5[URL Not Ready - Retry] + S5 --> S3 + S1 -->|Success| T[Create Webview Element] + + %% Webview Creation - Vanilla JS + T --> U[Set Webview Attributes] + U --> U1[preload, partition, webpreferences] + U1 --> U2[Set src URL - Triggers Loading] + U2 --> V[Setup Webview Event Handlers] + + %% Webview Loading States + V --> W[did-start-loading] + W --> W1{Initial Load Complete?} + W1 -->|No| X[Update Loading Overlay Text] + W1 -->|Yes - Navigation| X2[Skip Loading UI - Internal Navigation] + X --> X1[Start Loading Timeout 15s] + X2 --> Y1[Continue Without Loading UI] + + %% Success Path + X1 --> Y[did-finish-load] + Y1 --> Y + Y --> Z[Hide Loading Overlay] + Z --> Z1[Show Webview] + Z1 --> Z2[Mark Initial Load Complete] + Z2 --> Z3[Pre-warm Desktop Capturer Cache] + Z3 --> Z4[Preload ScreenSharePicker React Component] + Z4 --> Z5{Auto-open DevTools?} + Z5 -->|Yes| Z6[Open DevTools] + Z5 -->|No| AA[Video Call Active] + Z6 --> AA + + %% Error Handling + X1 --> BB[did-fail-load OR Timeout OR Crash] + BB --> BB0{404-like Error?} + BB0 -->|Yes| BB1[Extended 1500ms Delay] + BB0 -->|No| BB2[Standard 800ms Delay] + BB1 --> CC[Show Error Overlay] + BB2 --> CC + CC --> DD{Recovery Attempt?} + DD -->|Attempt 1| EE[Webview Reload - 1s Delay] + DD -->|Attempt 2| FF[URL Refresh - 2s Delay] + DD -->|Attempt 3| GG[Full Reinitialize - 3s Delay] + DD -->|Max Attempts| HH[Show Manual Reload Button] + + %% Window Lifecycle + AA --> II[Window Events] + II --> JJ[Move/Resize/Focus Events] + JJ --> KK[Update Redux State] + + %% Screen Sharing - Deferred React + AA --> LL[User Requests Screen Share] + LL --> MM[IPC: video-call-window/open-screen-picker] + MM --> NN{ScreenSharePicker Mounted?} + NN -->|No| OO[Lazy Import screenSharePickerMount] + OO --> PP[Mount React ScreenSharePicker] + NN -->|Yes| QQ[Show ScreenSharePicker] + PP --> QQ + QQ --> RR[User Selects Source] + RR --> SS[Hide ScreenSharePicker] + SS --> AA + + %% Cleanup + AA --> TT[User Closes Window] + TT --> UU[beforeunload Event] + UU --> VV[Remove IPC Listeners] + VV --> WW[Cleanup Resources] + WW --> XX[Window Destroyed] + + %% Recovery Flows + EE --> W + FF --> T + GG --> M + + %% Manual Recovery + HH --> YY[User Clicks Reload] + YY --> YY1[Reset State] + YY1 --> W + + %% Styling + classDef mainProcess fill:#e1f5fe,stroke:#0277bd,stroke-width:2px + classDef renderer fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px + classDef webview fill:#e8f5e8,stroke:#2e7d32,stroke-width:2px + classDef error fill:#ffebee,stroke:#c62828,stroke-width:2px + classDef success fill:#e8f5e8,stroke:#388e3c,stroke-width:2px + classDef retry fill:#fff3e0,stroke:#f57c00,stroke-width:2px + classDef react fill:#61dafb,stroke:#087ea4,stroke-width:2px + classDef cache fill:#e3f2fd,stroke:#1976d2,stroke-width:2px + + class A,B,C,D,E,F,G,H,I,J,WW,XX mainProcess + class K,L,M,N,O,P,R,S,T,U,U1,U2,V,KK renderer + class W,X,X1,Y,Z,Z1,Z2,AA,II,JJ webview + class BB,CC,DD,EE,FF,GG,HH error + class Z,Z1,Z2,AA success + class Q1,Q2,S2,S3,S4,S5 retry + class LL,MM,NN,OO,PP,QQ,RR,SS react + class Z3,Z4 cache +``` + +## Explanation + +**What this diagram shows:** + +- How a video call window is created from start to finish +- The vanilla JS architecture that handles core functionality +- Deferred React loading for screen sharing only +- Cache pre-warming for instant screen picker experience + +**Key parts:** + +1. **URL Validation** - Validates video call URLs for security +2. **Window Creation** - Creates a new BrowserWindow for video calls +3. **Vanilla JS Bootstrap** - Initializes i18n, manages webview lifecycle +4. **Loading/Error Overlays** - Pure DOM manipulation for UI states +5. **Webview Loading** - Loads the video call provider (Jitsi/Pexip) +6. **Cache Pre-warming** - Populates desktop capturer cache in background +7. **Deferred React** - ScreenSharePicker loaded only when needed +8. **Error Recovery** - Automatic retry with progressive strategies + +**Color Guide:** + +- **Blue** - Main process (core app) +- **Purple** - Renderer process (vanilla JS) +- **Green** - Webview (video call content) & Success states +- **Red** - Error states +- **Orange** - Retry attempts +- **Cyan** - React components (deferred) +- **Light Blue** - Cache operations + +## Detailed Step-by-Step Explanation + +### 1. Starting a Video Call + +When you click a video call button in Rocket.Chat, the app opens a dedicated window for the video call. + +**What happens:** + +- Main app receives the request with the video call URL +- Validates the URL (only allows https:// and http:// - no provider-specific patterns) +- Google Meet links open in your default browser instead +- Valid URLs proceed to window creation +- Works with any provider: Jitsi, PEXIP, self-hosted, or any other video call service + +### 2. Managing Windows + +The app ensures only one video call window exists at a time. + +**What happens:** + +- Checks for existing video call window +- If one exists, closes it gracefully with destruction tracking +- Creates a new BrowserWindow with optimized settings +- Configures permissions for camera, microphone, and screen sharing + +### 3. Vanilla JS Bootstrap + +Unlike typical Electron apps that render everything with React, the video call window uses vanilla JavaScript for core functionality. + +**What happens:** + +- Loads `video-call-window.html` with pre-defined overlay containers +- `video-call-window.ts` initializes without any framework dependencies +- Sets up i18n for localized loading/error messages +- Manages loading and error overlays through direct DOM manipulation + +**Why vanilla JS:** + +- Faster initial render (no React hydration) +- Simpler error recovery (no React state to manage) +- Reduced bundle size for critical path +- React only loaded when screen sharing is needed + +### 4. Webview Creation + +The webview element is created and configured before loading begins. + +**What happens:** + +- Creates webview element with required attributes +- Sets `preload`, `partition`, and `webpreferences` first +- Sets `src` last to trigger loading (attribute order matters) +- Attaches event handlers for loading states + +**Partition support:** + +- Video call windows use `persist:video-call-session` partition for storage isolation +- This generic partition name works with all providers (Jitsi, PEXIP, self-hosted, etc.) +- The detection logic checks for this partition to identify video call webviews +- All video call providers share the same partition for consistent behavior + +### 5. Loading States + +Loading UI is shown during initial load but not during in-call navigation. + +**What happens:** + +- Initial load shows loading overlay with localized text +- 15-second timeout prevents indefinite loading +- Internal navigation (room transitions) skips loading UI +- Webview hidden until content is ready (prevents 404 flicker) + +### 6. Cache Pre-warming + +When the webview finishes loading, the app prepares for screen sharing. + +**What happens:** + +- Triggers `video-call-window/prewarm-capturer-cache` IPC +- Desktop capturer fetches available sources in background +- Cache is populated before user opens screen picker +- First screen share request shows sources instantly + +### 7. Deferred React Loading + +React is only loaded when the user requests screen sharing. + +**What happens:** + +- `screenSharePickerMount.tsx` is dynamically imported +- React root created only for the ScreenSharePicker component +- Component stays mounted (hidden) for fast subsequent opens +- Visibility controlled through React state, not mount/unmount + +### 8. Error Handling + +Progressive error recovery with smart delays. + +**Recovery strategies:** + +1. **Attempt 1**: Simple webview reload (1 second delay) +2. **Attempt 2**: Recreate webview with same URL (2 second delay) +3. **Attempt 3**: Full reinitialization from scratch (3 second delay) +4. **Final**: Show manual reload button + +**Smart delays:** + +- 404-like errors (-6, -105, -106): 1500ms delay +- Other errors: 800ms delay +- Prevents premature error display during redirects + +### 9. Window Cleanup + +Proper cleanup when the window closes. + +**What happens:** + +- `beforeunload` event triggers cleanup +- IPC listeners removed to prevent memory leaks +- All timers and timeouts cleared +- Window destroyed after cleanup completes + +## Key Features + +### Vanilla JS Architecture + +- **Direct DOM manipulation** for loading/error overlays +- **No framework overhead** for critical path +- **Faster error recovery** without React state management +- **React isolation** - only ScreenSharePicker uses React + +### Smart Loading System + +- **Initial load tracking** - distinguishes first load from navigation +- **Webview visibility control** - hidden during loading to prevent flicker +- **Provider agnostic** - works with any video call provider (Jitsi, PEXIP, self-hosted, etc.) +- **Error-specific delays** - 404 errors get longer delays + +### Cache Pre-warming + +- **Background fetch** on webview load completion +- **Instant screen picker** - sources already cached +- **Stale-while-revalidate** - always returns data, refreshes in background + +### Retry System + +- **Multiple attempts** with progressive strategies +- **Smart delays** between retries +- **State reset** on recovery attempts +- **Manual fallback** when automatic recovery fails + +### Performance Features + +- **Fast startup** with vanilla JS bootstrap +- **Deferred loading** for React components +- **Background throttling** when window is hidden +- **Memory efficiency** with proper cleanup + +## File Structure + +```text +src/videoCallWindow/ +├── video-call-window.ts # Vanilla JS bootstrap (main entry, generic) +├── screenSharePicker.tsx # React component for source selection (generic) +├── screenSharePickerMount.tsx # React mounting utilities (generic) +├── ipc.ts # Main process IPC handlers (generic) +└── preload/ + ├── index.ts # Webview preload script (generic) + └── jitsiBridge.ts # Jitsi-specific bridge (required for Jitsi screen sharing) +``` + +## Technical Implementation + +### Webview Attribute Order + +```typescript +const webview = document.createElement('webview'); +webview.setAttribute('preload', preloadPath); +webview.setAttribute('webpreferences', 'nodeIntegration,nativeWindowOpen=true'); +webview.setAttribute('allowpopups', 'true'); +webview.setAttribute('partition', 'persist:video-call-session'); // Generic partition for all providers +webview.src = url; // Set last - triggers loading +``` + +### Deferred React Import + +```typescript +let screenPickerModule: typeof import('./screenSharePickerMount') | null = null; + +const preloadScreenSharePicker = async (): Promise => { + if (screenPickerModule) return; + screenPickerModule = await import('./screenSharePickerMount'); + screenPickerModule.mount(); // Mount hidden, ready for instant show +}; +``` + +### Loading Overlay Control + +```typescript +const updateLoadingUI = (textKey: string, descKey?: string): void => { + const overlay = document.getElementById('loading-overlay-root'); + const textEl = document.querySelector('.loading-text'); + + if (textEl) { + textEl.textContent = i18next.t(textKey, { defaultValue: 'Loading...' }); + } + overlay?.classList.add('show'); +}; +``` + +This architecture ensures video calls start quickly and reliably while maintaining a smooth user experience throughout the call lifecycle. diff --git a/docs/video-call-window-wgc-limitations.md b/docs/video-call-window-wgc-limitations.md new file mode 100644 index 0000000000..d4de4a969b --- /dev/null +++ b/docs/video-call-window-wgc-limitations.md @@ -0,0 +1,83 @@ +# Video Call Window – Windows Graphics Capture Limitation + +## Summary + +- Users on Windows 10 and 11 hit a stalled "Loading video call" screen when they join Jitsi calls through Rocket.Chat Desktop 4.9.0. +- The freeze happens mostly on Remote Desktop (RDP) sessions, but it can also appear on some on-console sessions depending on policy. +- The root cause is Chromium switching to the Windows Graphics Capture (WGC) path, which cannot capture remote sessions. WebRTC reports "Source is not capturable" and the meeting never progresses. + +## Impact + +- Auto-open devtools never launches because the webview does not reach `dom-ready`. +- The user-facing loading overlay never clears, so the call window appears frozen. +- Frequency is “often but not always”, matching the intermittent success of WGC when users move between console and remote sessions. + +## Detection + +- On Windows, `process.env.SESSIONNAME` is `Console` for local sessions and `RDP-Tcp#*` (or similar) for remote sessions. We can check this once during startup. +- If the value changes after launch (user switches to RDP), Chromium must be restarted to apply a new screen-capture backend. + +## Implemented Solution + +1. **Windows-only "Screen Capture Fallback" toggle**: + - A new setting is available in `Settings → General` (only visible on Windows). + - When enabled, this setting forces Chromium to use the legacy Desktop Duplication capturer, which works reliably in RDP sessions. +2. **Automatic RDP detection**: + - If `process.env.SESSIONNAME` is anything other than `Console` when the app starts, the app automatically disables Windows Graphics Capture, even if the toggle is off. This protects users who launch the client directly inside an RDP session. +3. **Comprehensive WGC disabling**: + - Both `WebRtcAllowWgcDesktopCapturer` and `WebRtcAllowWgcScreenCapturer` Chrome features are disabled to ensure complete fallback to Desktop Duplication API. + - This dual-flag approach ensures that both desktop capturer and screen capturer variants properly fall back to the legacy API that works in RDP sessions. +4. **Smart restart behavior**: + - When running locally (not in RDP), toggling the setting automatically restarts the app to apply the change. + - When running in an RDP session: + - The toggle is automatically shown as ON (enabled) + - The toggle is disabled (grayed out) and cannot be changed + - This clearly indicates that WGC is already disabled by RDP detection + - The descriptive text explains: "Currently enforced because the app detected a Remote Desktop session. Toggle now controls future launches when running locally." + - Users should restart the desktop app after switching between local and RDP sessions to ensure the correct screen capture backend is active. + +## Technical Details + +### Disabled Chrome Features + +When RDP session is detected or the fallback setting is enabled, the following Chrome features are disabled: + +- `WebRtcAllowWgcDesktopCapturer` - Disables Windows Graphics Capture for desktop capture +- `WebRtcAllowWgcScreenCapturer` - Disables Windows Graphics Capture for screen capture + +Both flags are necessary because Chromium's WebRTC implementation uses different code paths for desktop and screen capturing. Disabling both ensures complete fallback to the Desktop Duplication API. + +### Implementation Location + +The flags are disabled at two levels for maximum reliability: + +1. **App-level** (`src/app/main/app.ts`): + + - Disabled during `performElectronStartup()` function before any windows are created + - Uses `app.commandLine.appendSwitch('disable-features', ...)` + - This affects all Chromium processes in the app + +2. **Video call window level** (`src/videoCallWindow/ipc.ts`): + - Explicitly passes the flags via `additionalArguments` in the BrowserWindow's `webPreferences` + - This ensures the webview tag inside the video call window definitely inherits the flags + - Acts as a safeguard since webview tags can have isolated renderer processes + +This dual-layer approach ensures WGC is disabled both at the app level and specifically for the webview that loads Jitsi/video call content. + +## References + +### Windows Graphics Capture Limitations + +- Microsoft documentation on Windows Graphics Capture limitations (remote sessions unsupported): https://learn.microsoft.com/windows/uwp/audio-video-camera/screen-capture#limitations +- Chromium WebRTC bug describing the RDP failure with WGC: https://bugs.chromium.org/p/chromium/issues/detail?id=1258686 +- Chromium source comment explaining why WGC only works for interactive sessions: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/desktop_capture/win/wgc_desktop_frame_capturer_win.cc;l=51-60 +- Electron desktopCapturer docs noting RDP limitations and the need to disable WGC: https://www.electronjs.org/docs/latest/api/desktop-capturer#windows-remote-sessions +- Electron issue tracker confirming the recommended switch: https://github.com/electron/electron/issues/27411 +- Chromium code showing both WGC capturer variants: https://source.chromium.org/chromium/chromium/src/+/main:third_party/webrtc/modules/desktop_capture/desktop_capturer.cc + +### Webview Chrome Flags Inheritance + +- Electron webview tag security and architectural considerations: https://github.com/electron/electron/issues/18187 +- Electron webview getUserMedia/getDisplayMedia limitations: https://github.com/electron/electron/issues/27208 +- Electron documentation on webview isolation and separate process model: Webview tags run in separate renderer processes and don't automatically inherit all app-level command-line switches +- Solution: Using `additionalArguments` in parent BrowserWindow's `webPreferences` to ensure flags propagate to webview renderer processes diff --git a/electron-builder.json b/electron-builder.json index bce80f6927..3ebad39579 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -6,11 +6,12 @@ "name": "Rocket.Chat", "schemes": ["rocketchat"] }, + "afterPack": "./build/afterPack.js", "mac": { "category": "public.app-category.productivity", "target": ["dmg", "pkg", "zip", "mas"], "icon": "build/icon.icns", - "bundleVersion": "25041", + "bundleVersion": "26031", "helperBundleId": "chat.rocket.electron.helper", "type": "distribution", "artifactName": "rocketchat-${version}-${os}.${ext}", @@ -22,7 +23,9 @@ }, "hardenedRuntime": true, "gatekeeperAssess": false, - "provisioningProfile": "Desktop.provisionprofile" + "provisioningProfile": "Desktop.provisionprofile", + "entitlements": "build/entitlements.mac.plist", + "entitlementsInherit": "build/entitlements.mac.plist" }, "dmg": { "background": "build/background.png", @@ -56,15 +59,15 @@ "target": [ { "target": "nsis", - "arch": ["x64", "ia32"] + "arch": ["x64", "ia32", "arm64"] }, { "target": "msi", - "arch": ["x64", "ia32"] + "arch": ["x64", "ia32", "arm64"] }, { "target": "zip", - "arch": ["x64", "ia32"] + "arch": ["x64", "ia32", "arm64"] } ], "icon": "build/icon.ico", @@ -72,7 +75,12 @@ "verifyUpdateCodeSignature": true, "requestedExecutionLevel": "asInvoker", "signAndEditExecutable": true, - "artifactName": "rocketchat-${version}-${os}-${arch}.${ext}" + "artifactName": "rocketchat-${version}-${os}-${arch}.${ext}", + "signtoolOptions": { + "sign": "./build/winSignKms.js", + "signingHashAlgorithms": ["sha256"], + "timeStampServer": "http://timestamp.digicert.com" + } }, "nsis": { "oneClick": false, @@ -95,6 +103,10 @@ "createStartMenuShortcut": true, "menuCategory": false }, + "msi": { + "warningsAsErrors": false, + "additionalWixArgs": ["-sval"] + }, "appx": { "backgroundColor": "#2f343d", "languages": ["en-US", "en-GB", "pt-BR"], @@ -113,27 +125,31 @@ "Name": "Rocket.Chat", "Comment": "Official Rocket.Chat Desktop Client", "GenericName": "Rocket.Chat", - "Categories": "GNOME;GTK;Network;InstantMessaging", - "StartupWMClass": "Rocket.Chat", - "MimeType": "x-scheme-handler/rocketchat" + "Categories": "GNOME;GTK;Network;InstantMessaging" } }, "artifactName": "rocketchat-${version}-${os}-${arch}.${ext}" }, "deb": { - "fpm": [ - "--after-install=build/linux/postinst.sh" - ] + "fpm": ["--after-install=build/linux/postinst.sh"], + "recommends": ["xdg-desktop-portal", "xdg-desktop-portal-gtk"] }, "rpm": { - "fpm": ["--rpm-rpmbuild-define=_build_id_links none"] + "fpm": [ + "--rpm-rpmbuild-define=_build_id_links none", + "--rpm-tag=Recommends: xdg-desktop-portal", + "--rpm-tag=Recommends: xdg-desktop-portal-gtk" + ] }, "snap": { + "base": "core22", + "allowNativeWayland": false, "plugs": [ "desktop", "desktop-legacy", "home", "x11", + "wayland", "unity7", "browser-support", "network", @@ -144,7 +160,8 @@ "audio-playback", "audio-record", "screen-inhibit-control", - "upower-observe" + "upower-observe", + "pipewire" ] }, "afterSign": "./build/notarize.js", diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 48821e2f8a..0000000000 --- a/package-lock.json +++ /dev/null @@ -1,22835 +0,0 @@ -{ - "name": "rocketchat", - "version": "3.9.6", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "7zip-bin": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.1.1.tgz", - "integrity": "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@aws-crypto/crc32": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", - "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", - "dev": true, - "optional": true, - "requires": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "optional": true - } - } - }, - "@aws-crypto/ie11-detection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", - "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "optional": true - } - } - }, - "@aws-crypto/sha256-browser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", - "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-crypto/ie11-detection": "^3.0.0", - "@aws-crypto/sha256-js": "^3.0.0", - "@aws-crypto/supports-web-crypto": "^3.0.0", - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "optional": true - } - } - }, - "@aws-crypto/sha256-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", - "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-crypto/util": "^3.0.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "optional": true - } - } - }, - "@aws-crypto/supports-web-crypto": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", - "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "optional": true - } - } - }, - "@aws-crypto/util": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", - "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/client-cognito-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.398.0.tgz", - "integrity": "sha512-Pr/S1f8R2FsJ8DwBC6g0CSdtZNNV5dMHhlIi+t8YAmCJvP4KT+UhzFjbvQRINlBRLFuGUuP7p5vRcGVELD3+wA==", - "dev": true, - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/client-sts": "3.398.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/client-sso": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.398.0.tgz", - "integrity": "sha512-CygL0jhfibw4kmWXG/3sfZMFNjcXo66XUuPC4BqZBk8Rj5vFoxp1vZeMkDLzTIk97Nvo5J5Bh+QnXKhub6AckQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/client-sts": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.398.0.tgz", - "integrity": "sha512-/3Pa9wLMvBZipKraq3AtbmTfXW6q9kyvhwOno64f1Fz7kFb8ijQFMGoATS70B2pGEZTlxkUqJFWDiisT6Q6dFg==", - "dev": true, - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-sdk-sts": "3.398.0", - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "fast-xml-parser": "4.2.5", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/credential-provider-cognito-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.398.0.tgz", - "integrity": "sha512-MFUhy1YayHg5ypRTk4OTfDumQRP+OJBagaGv14kA8DzhKH1sNrU4HV7A7y2J4SvkN5hG/KnLJqxpakCtB2/O2g==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/credential-provider-env": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.398.0.tgz", - "integrity": "sha512-Z8Yj5z7FroAsR6UVML+XUdlpoqEe9Dnle8c2h8/xWwIC2feTfIBhjLhRVxfbpbM1pLgBSNEcZ7U8fwq5l7ESVQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/credential-provider-ini": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.398.0.tgz", - "integrity": "sha512-AsK1lStK3nB9Cn6S6ODb1ktGh7SRejsNVQVKX3t5d3tgOaX+aX1Iwy8FzM/ZEN8uCloeRifUGIY9uQFygg5mSw==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/credential-provider-node": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.398.0.tgz", - "integrity": "sha512-odmI/DSKfuWUYeDnGTCEHBbC8/MwnF6yEq874zl6+owoVv0ZsYP8qBHfiJkYqrwg7wQ7Pi40sSAPC1rhesGwzg==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-ini": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/credential-provider-process": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.398.0.tgz", - "integrity": "sha512-WrkBL1W7TXN508PA9wRXPFtzmGpVSW98gDaHEaa8GolAPHMPa5t2QcC/z/cFpglzrcVv8SA277zu9Z8tELdZhg==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/credential-provider-sso": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.398.0.tgz", - "integrity": "sha512-2Dl35587xbnzR/GGZqA2MnFs8+kS4wbHQO9BioU0okA+8NRueohNMdrdQmQDdSNK4BfIpFspiZmFkXFNyEAfgw==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/client-sso": "3.398.0", - "@aws-sdk/token-providers": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/credential-provider-web-identity": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.398.0.tgz", - "integrity": "sha512-iG3905Alv9pINbQ8/MIsshgqYMbWx+NDQWpxbIW3W0MkSH3iAqdVpSCteYidYX9G/jv2Um1nW3y360ib20bvNg==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/credential-providers": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.398.0.tgz", - "integrity": "sha512-355vXmImn2e85mIWSYDVb101AF2lIVHKNCaH6sV1U/8i0ZOXh2cJYNdkRYrxNt1ezDB0k97lSKvuDx7RDvJyRg==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/client-cognito-identity": "3.398.0", - "@aws-sdk/client-sso": "3.398.0", - "@aws-sdk/client-sts": "3.398.0", - "@aws-sdk/credential-provider-cognito-identity": "3.398.0", - "@aws-sdk/credential-provider-env": "3.398.0", - "@aws-sdk/credential-provider-ini": "3.398.0", - "@aws-sdk/credential-provider-node": "3.398.0", - "@aws-sdk/credential-provider-process": "3.398.0", - "@aws-sdk/credential-provider-sso": "3.398.0", - "@aws-sdk/credential-provider-web-identity": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/credential-provider-imds": "^2.0.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/middleware-host-header": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.398.0.tgz", - "integrity": "sha512-m+5laWdBaxIZK2ko0OwcCHJZJ5V1MgEIt8QVQ3k4/kOkN9ICjevOYmba751pHoTnbOYB7zQd6D2OT3EYEEsUcA==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/middleware-logger": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.398.0.tgz", - "integrity": "sha512-CiJjW+FL12elS6Pn7/UVjVK8HWHhXMfvHZvOwx/Qkpy340sIhkuzOO6fZEruECDTZhl2Wqn81XdJ1ZQ4pRKpCg==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/middleware-recursion-detection": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.398.0.tgz", - "integrity": "sha512-7QpOqPQAZNXDXv6vsRex4R8dLniL0E/80OPK4PPFsrCh9btEyhN9Begh4i1T+5lL28hmYkztLOkTQ2N5J3hgRQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/middleware-sdk-sts": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.398.0.tgz", - "integrity": "sha512-+JH76XHEgfVihkY+GurohOQ5Z83zVN1nYcQzwCFnCDTh4dG4KwhnZKG+WPw6XJECocY0R+H0ivofeALHvVWJtQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/middleware-signing": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/middleware-signing": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.398.0.tgz", - "integrity": "sha512-O0KqXAix1TcvZBFt1qoFkHMUNJOSgjJTYS7lFTRKSwgsD27bdW2TM2r9R8DAccWFt5Amjkdt+eOwQMIXPGTm8w==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/signature-v4": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/middleware-user-agent": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.398.0.tgz", - "integrity": "sha512-nF1jg0L+18b5HvTcYzwyFgfZQQMELJINFqI0mi4yRKaX7T5a3aGp5RVLGGju/6tAGTuFbfBoEhkhU3kkxexPYQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/token-providers": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.398.0.tgz", - "integrity": "sha512-nrYgjzavGCKJL/48Vt0EL+OlIc5UZLfNGpgyUW9cv3XZwl+kXV0QB+HH0rHZZLfpbBgZ2RBIJR9uD5ieu/6hpQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-crypto/sha256-browser": "3.0.0", - "@aws-crypto/sha256-js": "3.0.0", - "@aws-sdk/middleware-host-header": "3.398.0", - "@aws-sdk/middleware-logger": "3.398.0", - "@aws-sdk/middleware-recursion-detection": "3.398.0", - "@aws-sdk/middleware-user-agent": "3.398.0", - "@aws-sdk/types": "3.398.0", - "@aws-sdk/util-endpoints": "3.398.0", - "@aws-sdk/util-user-agent-browser": "3.398.0", - "@aws-sdk/util-user-agent-node": "3.398.0", - "@smithy/config-resolver": "^2.0.5", - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/hash-node": "^2.0.5", - "@smithy/invalid-dependency": "^2.0.5", - "@smithy/middleware-content-length": "^2.0.5", - "@smithy/middleware-endpoint": "^2.0.5", - "@smithy/middleware-retry": "^2.0.5", - "@smithy/middleware-serde": "^2.0.5", - "@smithy/middleware-stack": "^2.0.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/property-provider": "^2.0.0", - "@smithy/protocol-http": "^2.0.5", - "@smithy/shared-ini-file-loader": "^2.0.0", - "@smithy/smithy-client": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-body-length-browser": "^2.0.0", - "@smithy/util-body-length-node": "^2.1.0", - "@smithy/util-defaults-mode-browser": "^2.0.5", - "@smithy/util-defaults-mode-node": "^2.0.5", - "@smithy/util-retry": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/types": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.398.0.tgz", - "integrity": "sha512-r44fkS+vsEgKCuEuTV+TIk0t0m5ZlXHNjSDYEUvzLStbbfUFiNus/YG4UCa0wOk9R7VuQI67badsvvPeVPCGDQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/util-endpoints": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.398.0.tgz", - "integrity": "sha512-Fy0gLYAei/Rd6BrXG4baspCnWTUSd0NdokU1pZh4KlfEAEN1i8SPPgfiO5hLk7+2inqtCmqxVJlfqbMVe9k4bw==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/util-locate-window": { - "version": "3.310.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", - "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/util-user-agent-browser": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.398.0.tgz", - "integrity": "sha512-A3Tzx1tkDHlBT+IgxmsMCHbV8LM7SwwCozq2ZjJRx0nqw3MCrrcxQFXldHeX/gdUMO+0Oocb7HGSnVODTq+0EA==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/types": "^2.2.2", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/util-user-agent-node": { - "version": "3.398.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.398.0.tgz", - "integrity": "sha512-RTVQofdj961ej4//fEkppFf4KXqKGMTCqJYghx3G0C/MYXbg7MGl7LjfNGtJcboRE8pfHHQ/TUWBDA7RIAPPlQ==", - "dev": true, - "optional": true, - "requires": { - "@aws-sdk/types": "3.398.0", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.3.1" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", - "requires": { - "@babel/highlight": "^7.18.6" - } - }, - "@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", - "dev": true - }, - "@babel/core": { - "version": "7.22.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.1.tgz", - "integrity": "sha512-Hkqu7J4ynysSXxmAahpN1jjRwVJ+NdpraFLIWflgjpVob3KNyK3/tIUc7Q7szed8WMp0JNa7Qtd1E9Oo22F9gA==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.22.0", - "@babel/helper-compilation-targets": "^7.22.1", - "@babel/helper-module-transforms": "^7.22.1", - "@babel/helpers": "^7.22.0", - "@babel/parser": "^7.22.0", - "@babel/template": "^7.21.9", - "@babel/traverse": "^7.22.1", - "@babel/types": "^7.22.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/eslint-parser": { - "version": "7.21.8", - "dev": true, - "requires": { - "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", - "eslint-visitor-keys": "^2.1.0", - "semver": "^6.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", - "dev": true, - "requires": { - "@babel/types": "^7.22.10", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, - "requires": { - "@babel/types": "^7.18.6" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz", - "integrity": "sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==", - "dev": true, - "requires": { - "@babel/types": "^7.22.10" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", - "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.11.tgz", - "integrity": "sha512-y1grdYL4WzmUDBRGK0pDbIoFd7UZKoDurDzWEoNMYoj1EL+foGRQNyPWDcC+YyegN5y1DUsFFmzjGijB3nSVAQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", - "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz", - "integrity": "sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", - "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", - "requires": { - "@babel/types": "^7.21.4" - } - }, - "@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" - }, - "dependencies": { - "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-plugin-utils": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", - "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", - "dev": true - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", - "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-wrap-function": "^7.22.9" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-replace-supers": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", - "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-member-expression-to-functions": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" - }, - "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==" - }, - "@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz", - "integrity": "sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.10" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helpers": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz", - "integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.11", - "@babel/types": "^7.22.11" - }, - "dependencies": { - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/highlight": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", - "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", - "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.4.tgz", - "integrity": "sha512-VLLsx06XkEYqBtE5YGPwfSGwfrjnyPP5oiGty3S8pQLFDFLaS8VwWSIxkTXpcvr5zeYLE6+MBNl2npl/YnfofA==", - "dev": true - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", - "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", - "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-function-bind": { - "version": "7.18.9", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-function-bind": "^7.18.6" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-flow": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz", - "integrity": "sha512-9RdCl0i+q0QExayk2nOS7853w08yLucnnPML6EN9S8fgMPVtdLDCdx/cOQ/i44Lb9UeQX9A35yaqBBOMMZxPxQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-syntax-function-bind": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-function-bind/-/plugin-syntax-function-bind-7.18.6.tgz", - "integrity": "sha512-wZN0Aq/AScknI9mKGcR3TpHdASMufFGaeJgc1rhPmLtZ/PniwjePSh8cfh8tXMB3U4kh/3cRKrLjDtedejg8jQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", - "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz", - "integrity": "sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", - "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.11.tgz", - "integrity": "sha512-0pAlmeRJn6wU84zzZsEOx1JV1Jf8fqO9ok7wofIJwUnplYo247dcd24P+cMJht7ts9xkzdtB0EPHmOb7F+KzXw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz", - "integrity": "sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.5" - }, - "dependencies": { - "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", - "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz", - "integrity": "sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-class-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz", - "integrity": "sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-class-static-block": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.11.tgz", - "integrity": "sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.11", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-classes": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", - "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", - "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz", - "integrity": "sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", - "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", - "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.11.tgz", - "integrity": "sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", - "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.11.tgz", - "integrity": "sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-flow-strip-types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz", - "integrity": "sha512-tujNbZdxdG0/54g/oua8ISToaXTFBf8EnSb5PgQSciIXWOWKX3S4+JR7ZE9ol8FZwf9kxitzkGQ+QWeov/mCiA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-flow": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", - "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", - "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", - "dev": true, - "requires": { - "@babel/helper-compilation-targets": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-json-strings": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.11.tgz", - "integrity": "sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", - "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.11.tgz", - "integrity": "sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", - "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", - "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.11.tgz", - "integrity": "sha512-o2+bg7GDS60cJMgz9jWqRUsWkMzLCxp+jFDeDUT5sjRlAxcJWZ2ylNdI7QQ2+CH5hWu7OnN+Cv3htt7AkSf96g==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.11.tgz", - "integrity": "sha512-rIqHmHoMEOhI3VkVf5jQ15l539KrwhzqcBO6wdCNWPWc/JWt9ILNYNUssbRpeq0qWns8svuw8LnMNCvWBIJ8wA==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", - "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", - "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.11.tgz", - "integrity": "sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.11.tgz", - "integrity": "sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.11.tgz", - "integrity": "sha512-nX8cPFa6+UmbepISvlf5jhQyaC7ASs/7UxHmMkuJ/k5xSHvDPPaibMo+v3TXwU/Pjqhep/nFNpd3zn4YR59pnw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.10", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", - "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.11.tgz", - "integrity": "sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.22.12", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.12.tgz", - "integrity": "sha512-7XXCVqZtyFWqjDsYDY4T45w4mlx1rf7aOgkc/Ww76xkgBiOlmjPkx36PBLHa1k1rwWvVgYMPsbuVnIamx2ZQJw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", - "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-private-methods": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz", - "integrity": "sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.11.tgz", - "integrity": "sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", - "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz", - "integrity": "sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.22.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.3.tgz", - "integrity": "sha512-JEulRWG2f04a7L8VWaOngWiK6p+JOSpB+DAtwfJgOaej1qdbNxqtK7MwTBHjUA10NeFcszlFNqCdbRcirzh2uQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/plugin-syntax-jsx": "^7.21.4", - "@babel/types": "^7.22.3" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz", - "integrity": "sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==", - "dev": true, - "requires": { - "@babel/plugin-transform-react-jsx": "^7.18.6" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz", - "integrity": "sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz", - "integrity": "sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", - "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", - "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-spread": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", - "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", - "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", - "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", - "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.11.tgz", - "integrity": "sha512-0E4/L+7gfvHub7wsbTv03oRtD69X31LByy44fGmFzbZScpupFByMcgCJ0VbBTkzyjSJKuRoGN8tcijOWKTmqOA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.11", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.22.5" - }, - "dependencies": { - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz", - "integrity": "sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz", - "integrity": "sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", - "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz", - "integrity": "sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - } - } - }, - "@babel/preset-env": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.22.4.tgz", - "integrity": "sha512-c3lHOjbwBv0TkhYCr+XCR6wKcSZ1QbQTVdSkZUaVpLv8CVWotBMArWUi5UAJrcrQaEnleVkkvaV8F/pmc/STZQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.3", - "@babel/helper-compilation-targets": "^7.22.1", - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.3", - "@babel/plugin-proposal-private-property-in-object": "^7.21.0", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-import-attributes": "^7.22.3", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.21.5", - "@babel/plugin-transform-async-generator-functions": "^7.22.3", - "@babel/plugin-transform-async-to-generator": "^7.20.7", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.21.0", - "@babel/plugin-transform-class-properties": "^7.22.3", - "@babel/plugin-transform-class-static-block": "^7.22.3", - "@babel/plugin-transform-classes": "^7.21.0", - "@babel/plugin-transform-computed-properties": "^7.21.5", - "@babel/plugin-transform-destructuring": "^7.21.3", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-dynamic-import": "^7.22.1", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-export-namespace-from": "^7.22.3", - "@babel/plugin-transform-for-of": "^7.21.5", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-json-strings": "^7.22.3", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.22.3", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.20.11", - "@babel/plugin-transform-modules-commonjs": "^7.21.5", - "@babel/plugin-transform-modules-systemjs": "^7.22.3", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.3", - "@babel/plugin-transform-new-target": "^7.22.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.3", - "@babel/plugin-transform-numeric-separator": "^7.22.3", - "@babel/plugin-transform-object-rest-spread": "^7.22.3", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-optional-catch-binding": "^7.22.3", - "@babel/plugin-transform-optional-chaining": "^7.22.3", - "@babel/plugin-transform-parameters": "^7.22.3", - "@babel/plugin-transform-private-methods": "^7.22.3", - "@babel/plugin-transform-private-property-in-object": "^7.22.3", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.21.5", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.20.7", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.21.5", - "@babel/plugin-transform-unicode-property-regex": "^7.22.3", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/plugin-transform-unicode-sets-regex": "^7.22.3", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.22.4", - "babel-plugin-polyfill-corejs2": "^0.4.3", - "babel-plugin-polyfill-corejs3": "^0.8.1", - "babel-plugin-polyfill-regenerator": "^0.5.0", - "core-js-compat": "^3.30.2", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/preset-flow": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.22.5.tgz", - "integrity": "sha512-ta2qZ+LSiGCrP5pgcGt8xMnnkXQrq8Sa4Ulhy06BOlF5QbLw9q5hIx7bn5MrsvyTGAfh6kTOo07Q+Pfld/8Y5Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-transform-flow-strip-types": "^7.22.5" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true - } - } - }, - "@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.22.3", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "@babel/plugin-transform-react-display-name": "^7.18.6", - "@babel/plugin-transform-react-jsx": "^7.22.3", - "@babel/plugin-transform-react-jsx-development": "^7.18.6", - "@babel/plugin-transform-react-pure-annotations": "^7.18.6" - } - }, - "@babel/preset-typescript": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.22.11.tgz", - "integrity": "sha512-tWY5wyCZYBGY7IlalfKI1rLiGlIfnwsRHZqlky0HVv8qviwQ1Uo/05M6+s+TcTCVa6Bmoo2uJW5TMFX6Wa4qVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.22.11", - "@babel/plugin-transform-typescript": "^7.22.11" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true - } - } - }, - "@babel/register": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.22.5.tgz", - "integrity": "sha512-vV6pm/4CijSQ8Y47RH5SopXzursN35RQINfGJkmOlcpAtGuf94miFvIPhCKGQN7WGIcsgG1BHEX2KVdTYwTwUQ==", - "dev": true, - "requires": { - "clone-deep": "^4.0.1", - "find-cache-dir": "^2.0.0", - "make-dir": "^2.1.0", - "pirates": "^4.0.5", - "source-map-support": "^0.5.16" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, - "@babel/runtime": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz", - "integrity": "sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.10", - "chalk": "^2.4.2" - } - }, - "@babel/parser": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz", - "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==", - "dev": true - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", - "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.10", - "chalk": "^2.4.2" - } - }, - "@babel/parser": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.11.tgz", - "integrity": "sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==", - "dev": true - }, - "@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/types": { - "version": "7.22.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.4.tgz", - "integrity": "sha512-Tx9x3UBHTTsMSW85WB2kphxYQVvrZ/t1FxD88IpSgIjiUJlCm9z+xWIDwyo1vffTwSqteqyznB8ZE9vYYk16zA==", - "requires": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@bugsnag/browser": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@bugsnag/browser/-/browser-7.20.2.tgz", - "integrity": "sha512-4J4s53ZpYr3hHA+QjxUjOI6U+A8+XuUVH45UshE87Jp2Y4mV8ML2DovejqJS8J8yjdbnh2z1Wtg/v3WUNt4ayQ==", - "requires": { - "@bugsnag/core": "^7.19.0" - } - }, - "@bugsnag/core": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@bugsnag/core/-/core-7.19.0.tgz", - "integrity": "sha512-2KGwdaLD9PhR7Wk7xPi3jGuGsKTatc/28U4TOZIDU3CgC2QhGjubwiXSECel5gwxhZ3jACKcMKSV2ovHhv1NrA==", - "requires": { - "@bugsnag/cuid": "^3.0.0", - "@bugsnag/safe-json-stringify": "^6.0.0", - "error-stack-parser": "^2.0.3", - "iserror": "0.0.2", - "stack-generator": "^2.0.3" - } - }, - "@bugsnag/cuid": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@bugsnag/cuid/-/cuid-3.0.2.tgz", - "integrity": "sha512-cIwzC93r3PQ/INeuwtZwkZIG2K8WWN0rRLZQhu+mr48Ay+i6sEki4GYfTsflse7hZ1BeDWrNb/Q9vgY3B31xHQ==" - }, - "@bugsnag/js": { - "version": "7.20.2", - "requires": { - "@bugsnag/browser": "^7.20.2", - "@bugsnag/node": "^7.19.0" - } - }, - "@bugsnag/node": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@bugsnag/node/-/node-7.19.0.tgz", - "integrity": "sha512-c4snyxx5d/fsMogmgehFBGc//daH6+4XCplia4zrEQYltjaQ+l8ud0dPx623DgJl/2j1+2zlRc7y7IHSd7Gm5w==", - "requires": { - "@bugsnag/core": "^7.19.0", - "byline": "^5.0.0", - "error-stack-parser": "^2.0.2", - "iserror": "^0.0.2", - "pump": "^3.0.0", - "stack-generator": "^2.0.3" - } - }, - "@bugsnag/safe-json-stringify": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@bugsnag/safe-json-stringify/-/safe-json-stringify-6.0.0.tgz", - "integrity": "sha512-htzFO1Zc57S8kgdRK9mLcPVTW1BY2ijfH7Dk2CeZmspTWKdKqSo1iwmqrq2WtRjFlo8aRZYgLX0wFrDXF/9DLA==" - }, - "@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@develar/schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", - "dev": true, - "requires": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" - } - }, - "@electron/get": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.2.tgz", - "integrity": "sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "global-agent": "^3.0.0", - "got": "^11.8.5", - "progress": "^2.0.3", - "semver": "^6.2.0", - "sumchecker": "^3.0.1" - }, - "dependencies": { - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - } - } - }, - "@electron/universal": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.2.1.tgz", - "integrity": "sha512-7323HyMh7KBAl/nPDppdLsC87G6RwRU02dy5FPeGB1eS7rUePh55+WNWiDPLhFQqqVPHzh77M69uhmoT8XnwMQ==", - "dev": true, - "requires": { - "@malept/cross-spawn-promise": "^1.1.0", - "asar": "^3.1.0", - "debug": "^4.3.1", - "dir-compare": "^2.4.0", - "fs-extra": "^9.0.1", - "minimatch": "^3.0.4", - "plist": "^3.0.4" - }, - "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } - } - }, - "@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", - "requires": { - "@babel/helper-module-imports": "^7.16.7", - "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", - "babel-plugin-macros": "^3.1.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^4.0.0", - "find-root": "^1.1.0", - "source-map": "^0.5.7", - "stylis": "4.2.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" - }, - "stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" - } - } - }, - "@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "requires": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "stylis": "4.2.0" - }, - "dependencies": { - "stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" - } - } - }, - "@emotion/css": { - "version": "11.11.0", - "requires": { - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1" - } - }, - "@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" - }, - "@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", - "requires": { - "@emotion/memoize": "^0.8.1" - } - }, - "@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" - }, - "@emotion/react": { - "version": "11.11.0", - "requires": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", - "hoist-non-react-statics": "^3.3.1" - } - }, - "@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", - "requires": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", - "csstype": "^3.0.2" - } - }, - "@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" - }, - "@emotion/styled": { - "version": "11.11.0", - "requires": { - "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1" - } - }, - "@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" - }, - "@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==" - }, - "@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" - }, - "@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.0.tgz", - "integrity": "sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "globals": { - "version": "13.21.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", - "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@ewsjs/ntlm-client": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@ewsjs/ntlm-client/-/ntlm-client-1.0.0.tgz", - "integrity": "sha512-UVH0fRaOBO4qiAtJ33pp5aVEf5lIYT0aTCfhy4Rq+1AsCGv7krvx5DTp7dee5dMCvUk4HQXrS946u6RBHZbOkQ==", - "requires": { - "extend": "^3.0.0", - "request": "^2.66.0" - } - }, - "@ewsjs/xhr": { - "version": "1.5.0", - "requires": { - "@ewsjs/ntlm-client": "^1.0.0", - "bluebird": "^3.4.6", - "fetch": "^1.0.1", - "request": "^2.88.0" - } - }, - "@fiahfy/icns": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/@fiahfy/icns/-/icns-0.0.7.tgz", - "integrity": "sha512-0apAtbUXTU3Opy/Z4h69o53voBa+am8FmdZauyagUMskAVYN1a5yIRk48Sf+tEdBLlefbvqLWPJ4pxr/Y/QtTg==", - "dev": true, - "requires": { - "@fiahfy/packbits": "^0.0.6", - "pngjs": "^6.0.0" - }, - "dependencies": { - "pngjs": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", - "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", - "dev": true - } - } - }, - "@fiahfy/icns-convert": { - "version": "0.0.12", - "dev": true, - "requires": { - "@fiahfy/icns": "^0.0.7", - "meow": "^8.1.0", - "sharp": "^0.27.0" - }, - "dependencies": { - "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dev": true, - "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true - }, - "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "dev": true, - "requires": { - "semver": "^5.4.1" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true - }, - "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "dependencies": { - "simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dev": true, - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - } - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "sharp": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.27.2.tgz", - "integrity": "sha512-w3FVoONPG/x5MXCc3wsjOS+b9h3CI60qkus6EPQU4dkT0BDm0PyGhDCK6KhtfT3/vbeOMOXAKFNSw+I3QGWkMA==", - "dev": true, - "requires": { - "array-flatten": "^3.0.0", - "color": "^3.1.3", - "detect-libc": "^1.0.3", - "node-addon-api": "^3.1.0", - "npmlog": "^4.1.2", - "prebuild-install": "^6.0.1", - "semver": "^7.3.4", - "simple-get": "^4.0.0", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" - } - } - } - }, - "@fiahfy/ico": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@fiahfy/ico/-/ico-0.0.5.tgz", - "integrity": "sha512-caB3sEGMcPQUmlid4hAlrDBo82nsXER8xLakPiX6IhoVO9MZg0Sa2y6pQpWPsqVt6tdm8ZE7OhkgcaSTeWFN2Q==", - "dev": true, - "requires": { - "pngjs": "^6.0.0" - }, - "dependencies": { - "pngjs": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-6.0.0.tgz", - "integrity": "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg==", - "dev": true - } - } - }, - "@fiahfy/ico-convert": { - "version": "0.0.12", - "dev": true, - "requires": { - "@fiahfy/ico": "^0.0.5", - "meow": "^8.1.0", - "sharp": "^0.27.0" - }, - "dependencies": { - "color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "dev": true, - "requires": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "decompress-response": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", - "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "dev": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true - }, - "node-abi": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", - "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", - "dev": true, - "requires": { - "semver": "^5.4.1" - }, - "dependencies": { - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true - }, - "prebuild-install": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.1.4.tgz", - "integrity": "sha512-Z4vpywnK1lBg+zdPCVCsKq0xO66eEV9rWo2zrROGGiRS4JtueBOdlB1FnY8lcy7JsUud/Q3ijUxyWN26Ika0vQ==", - "dev": true, - "requires": { - "detect-libc": "^1.0.3", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", - "node-abi": "^2.21.0", - "npmlog": "^4.0.1", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^3.0.3", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "dependencies": { - "simple-get": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", - "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", - "dev": true, - "requires": { - "decompress-response": "^4.2.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - } - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "sharp": { - "version": "0.27.2", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.27.2.tgz", - "integrity": "sha512-w3FVoONPG/x5MXCc3wsjOS+b9h3CI60qkus6EPQU4dkT0BDm0PyGhDCK6KhtfT3/vbeOMOXAKFNSw+I3QGWkMA==", - "dev": true, - "requires": { - "array-flatten": "^3.0.0", - "color": "^3.1.3", - "detect-libc": "^1.0.3", - "node-addon-api": "^3.1.0", - "npmlog": "^4.1.2", - "prebuild-install": "^6.0.1", - "semver": "^7.3.4", - "simple-get": "^4.0.0", - "tar-fs": "^2.1.1", - "tunnel-agent": "^0.6.0" - } - } - } - }, - "@fiahfy/packbits": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@fiahfy/packbits/-/packbits-0.0.6.tgz", - "integrity": "sha512-XuhF/edg+iIvXjkCWgfj6fWtRi/KrEPg2ILXj1l86EN4EssuOiPcLKgkMDr9cL8jTGtVd/MKUWW6Y0/ZVf1PGA==", - "dev": true - }, - "@formatjs/ecma402-abstract": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.0.tgz", - "integrity": "sha512-6ueQTeJZtwKjmh23bdkq/DMqH4l4bmfvtQH98blOSbiXv/OUiyijSW6jU22IT8BNM1ujCaEvJfTtyCYVH38EMQ==", - "requires": { - "@formatjs/intl-localematcher": "0.4.0", - "tslib": "^2.4.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } - } - }, - "@formatjs/fast-memoize": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", - "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", - "requires": { - "tslib": "^2.4.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } - } - }, - "@formatjs/icu-messageformat-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.6.0.tgz", - "integrity": "sha512-yT6at0qc0DANw9qM/TU8RZaCtfDXtj4pZM/IC2WnVU80yAcliS3KVDiuUt4jSQAeFL9JS5bc2hARnFmjPdA6qw==", - "requires": { - "@formatjs/ecma402-abstract": "1.17.0", - "@formatjs/icu-skeleton-parser": "1.6.0", - "tslib": "^2.4.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } - } - }, - "@formatjs/icu-skeleton-parser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.0.tgz", - "integrity": "sha512-eMmxNpoX/J1IPUjPGSZwo0Wh+7CEvdEMddP2Jxg1gQJXfGfht/FdW2D5XDFj3VMbOTUQlDIdZJY7uC6O6gjPoA==", - "requires": { - "@formatjs/ecma402-abstract": "1.17.0", - "tslib": "^2.4.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } - } - }, - "@formatjs/intl-localematcher": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.4.0.tgz", - "integrity": "sha512-bRTd+rKomvfdS4QDlVJ6TA/Jx1F2h/TBVO5LjvhQ7QPPHp19oPNMIum7W2CMEReq/zPxpmCeB31F9+5gl/qtvw==", - "requires": { - "tslib": "^2.4.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } - } - }, - "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@hutson/parse-repository-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", - "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", - "dev": true - }, - "@internationalized/date": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.4.0.tgz", - "integrity": "sha512-QUDSGCsvrEVITVf+kv9VSAraAmCgjQmU5CiXtesUBBhBe374NmnEIIaOFBZ72t29dfGMBP0zF+v6toVnbcc6jg==", - "requires": { - "@swc/helpers": "^0.5.0" - } - }, - "@internationalized/message": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.1.tgz", - "integrity": "sha512-ZgHxf5HAPIaR0th+w0RUD62yF6vxitjlprSxmLJ1tam7FOekqRSDELMg4Cr/DdszG5YLsp5BG3FgHgqquQZbqw==", - "requires": { - "@swc/helpers": "^0.5.0", - "intl-messageformat": "^10.1.0" - } - }, - "@internationalized/number": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.2.1.tgz", - "integrity": "sha512-hK30sfBlmB1aIe3/OwAPg9Ey0DjjXvHEiGVhNaOiBJl31G0B6wMaX8BN3ibzdlpyRNE9p7X+3EBONmxtJO9Yfg==", - "requires": { - "@swc/helpers": "^0.5.0" - } - }, - "@internationalized/string": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.1.1.tgz", - "integrity": "sha512-fvSr6YRoVPgONiVIUhgCmIAlifMVCeej/snPZVzbzRPxGpHl3o1GRe+d/qh92D8KhgOciruDUH8I5mjdfdjzfA==", - "requires": { - "@swc/helpers": "^0.5.0" - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest-runner/core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@jest-runner/core/-/core-3.0.0.tgz", - "integrity": "sha512-frsV83vLDNEYvBcoUa5aizV3JyG8GPTB/eUN9MhX+0rAuP2H0tFceoNj16evufDsNFJY9VKlTkZZUY5tfmiGcw==", - "dev": true, - "requires": { - "jest-message-util": "^24.0.0", - "node-ipc": "^9.1.1" - }, - "dependencies": { - "@jest/console": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", - "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", - "dev": true, - "requires": { - "@jest/source-map": "^24.9.0", - "chalk": "^2.0.1", - "slash": "^2.0.0" - } - }, - "@jest/test-result": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", - "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", - "dev": true, - "requires": { - "@jest/console": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/istanbul-lib-coverage": "^2.0.0" - } - }, - "@jest/types": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", - "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^13.0.0" - } - }, - "@types/yargs": { - "version": "13.0.12", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.12.tgz", - "integrity": "sha512-qCxJE1qgz2y0hA4pIxjBR+PelCH0U5CK1XJXFwCNqfmliatKp47UCXXE9Dyk1OXBDLvsCF57TqQEJaeLfDYEOQ==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "jest-message-util": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", - "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/test-result": "^24.9.0", - "@jest/types": "^24.9.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^2.0.1", - "micromatch": "^3.1.10", - "slash": "^2.0.0", - "stack-utils": "^1.0.1" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "@jest-runner/electron": { - "version": "3.0.1", - "dev": true, - "requires": { - "@jest-runner/core": "^3.0.0", - "@jest-runner/rpc": "^3.0.0", - "jest-haste-map": "^25.0.0", - "jest-mock": "^25.0.0", - "jest-runner": "^25.0.0", - "jest-runtime": "^25.0.0", - "jest-util": "^25.0.0", - "throat": "^4.1.0" - }, - "dependencies": { - "@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", - "slash": "^3.0.0" - }, - "dependencies": { - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "@jest/fake-timers": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.5.0.tgz", - "integrity": "sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-util": "^25.5.0", - "lolex": "^5.0.0" - } - }, - "@jest/source-map": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.5.0.tgz", - "integrity": "sha512-eIGx0xN12yVpMcPaVpjXPnn3N30QGJCJQSkEDUt9x1fI1Gdvb07Ml6K5iN2hG7NmMP6FDmtPEssE3z6doOYUwQ==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "dependencies": { - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - } - } - }, - "@jest/transform": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.5.1.tgz", - "integrity": "sha512-Y8CEoVwXb4QwA6Y/9uDkn0Xfz0finGkieuV0xkdF9UtZGJeLukD5nLkaVrVsODB1ojRWlaoD0AJZpVHCSnJEvg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^25.5.0", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^3.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^25.5.1", - "jest-regex-util": "^25.2.6", - "jest-util": "^25.5.0", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "realpath-native": "^2.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - }, - "dependencies": { - "@types/yargs": { - "version": "15.0.15", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", - "integrity": "sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - } - } - }, - "@types/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", - "dev": true - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "jest-docblock": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.3.0.tgz", - "integrity": "sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-haste-map": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.5.1.tgz", - "integrity": "sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "@types/graceful-fs": "^4.1.2", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-serializer": "^25.5.0", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" - }, - "dependencies": { - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - } - } - }, - "jest-leak-detector": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.5.0.tgz", - "integrity": "sha512-rV7JdLsanS8OkdDpZtgBf61L5xZ4NnYLBq72r6ldxahJWWczZjXawRsoHyXzibM5ed7C2QRjpp6ypgwGdKyoVA==", - "dev": true, - "requires": { - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - }, - "dependencies": { - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true - }, - "jest-resolve": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", - "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "read-pkg-up": "^7.0.1", - "realpath-native": "^2.0.0", - "resolve": "^1.17.0", - "slash": "^3.0.0" - } - }, - "jest-runner": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.5.4.tgz", - "integrity": "sha512-V/2R7fKZo6blP8E9BL9vJ8aTU4TH2beuqGNxHbxi6t14XzTb+x90B3FRgdvuHm41GY8ch4xxvf0ATH4hdpjTqg==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/environment": "^25.5.0", - "@jest/test-result": "^25.5.0", - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^25.5.4", - "jest-docblock": "^25.3.0", - "jest-haste-map": "^25.5.1", - "jest-jasmine2": "^25.5.4", - "jest-leak-detector": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-resolve": "^25.5.1", - "jest-runtime": "^25.5.4", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - }, - "dependencies": { - "@jest/environment": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.5.0.tgz", - "integrity": "sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^25.5.0", - "@jest/types": "^25.5.0", - "jest-mock": "^25.5.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "jest-config": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.5.4.tgz", - "integrity": "sha512-SZwR91SwcdK6bz7Gco8qL7YY2sx8tFJYzvg216DLihTWf+LKY/DoJXpM9nTzYakSyfblbqeU48p/p7Jzy05Atg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^25.5.4", - "@jest/types": "^25.5.0", - "babel-jest": "^25.5.1", - "chalk": "^3.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^25.5.0", - "jest-environment-node": "^25.5.0", - "jest-get-type": "^25.2.6", - "jest-jasmine2": "^25.5.4", - "jest-regex-util": "^25.2.6", - "jest-resolve": "^25.5.1", - "jest-util": "^25.5.0", - "jest-validate": "^25.5.0", - "micromatch": "^4.0.2", - "pretty-format": "^25.5.0", - "realpath-native": "^2.0.0" - } - }, - "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", - "dev": true - }, - "jest-jasmine2": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.5.4.tgz", - "integrity": "sha512-9acbWEfbmS8UpdcfqnDO+uBUgKa/9hcRh983IHdM+pKmJPL77G0sWAAK0V0kr5LK3a8cSBfkFSoncXwQlRZfkQ==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^25.5.0", - "@jest/source-map": "^25.5.0", - "@jest/test-result": "^25.5.0", - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "co": "^4.6.0", - "expect": "^25.5.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^25.5.0", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-runtime": "^25.5.4", - "jest-snapshot": "^25.5.1", - "jest-util": "^25.5.0", - "pretty-format": "^25.5.0", - "throat": "^5.0.0" - } - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "realpath-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", - "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", - "dev": true - }, - "throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "dev": true - } - } - }, - "jest-runtime": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.5.4.tgz", - "integrity": "sha512-RWTt8LeWh3GvjYtASH2eezkc8AehVoWKK20udV6n3/gC87wlTbE1kIA+opCvNWyyPeBs6ptYsc6nyHUb1GlUVQ==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/environment": "^25.5.0", - "@jest/globals": "^25.5.2", - "@jest/source-map": "^25.5.0", - "@jest/test-result": "^25.5.0", - "@jest/transform": "^25.5.1", - "@jest/types": "^25.5.0", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^25.5.4", - "jest-haste-map": "^25.5.1", - "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-regex-util": "^25.2.6", - "jest-resolve": "^25.5.1", - "jest-snapshot": "^25.5.1", - "jest-util": "^25.5.0", - "jest-validate": "^25.5.0", - "realpath-native": "^2.0.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.3.1" - } - }, - "jest-serializer": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.5.0.tgz", - "integrity": "sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4" - } - }, - "jest-snapshot": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.5.1.tgz", - "integrity": "sha512-C02JE1TUe64p2v1auUJ2ze5vcuv32tkv9PyhEb318e8XOKF7MOyXdJ7kdjbvrp3ChPLU2usI7Rjxs97Dj5P0uQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/prettier": "^1.19.0", - "chalk": "^3.0.0", - "expect": "^25.5.0", - "graceful-fs": "^4.2.4", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-resolve": "^25.5.1", - "make-dir": "^3.0.0", - "natural-compare": "^1.4.0", - "pretty-format": "^25.5.0", - "semver": "^6.3.0" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - }, - "dependencies": { - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - } - } - } - }, - "jest-validate": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", - "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "jest-get-type": "^25.2.6", - "leven": "^3.1.0", - "pretty-format": "^25.5.0" - } - }, - "jest-worker": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", - "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "dependencies": { - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "throat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", - "integrity": "sha512-wCVxLDcFxw7ujDxaeJC6nfl2XfHJNYs8yUYJnvMgtPEFlttP9tHSfRUv2vBe6C4hkVFPWoP1P6ZccbYjmSEkKA==", - "dev": true - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "@jest-runner/rpc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@jest-runner/rpc/-/rpc-3.0.0.tgz", - "integrity": "sha512-Q+zMLgG8wWqaBCt5vXE9C/A8L4gWFLlt8w7RW3CmbrBF5S5vVoO/zdGZbrS2nB6xCAey7rMAWsxosDPGmsn/9w==", - "dev": true, - "requires": { - "@jest-runner/core": "^3.0.0", - "glob": "^7.1.3", - "jscodeshift": "^0.6.3", - "node-ipc": "^9.1.1", - "prettier": "^1.14.2", - "uuid": "^3.3.2", - "yargs": "^15.3.1" - }, - "dependencies": { - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.6.3.tgz", - "integrity": "sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/reporters": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.6.2", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-resolve-dependencies": "^26.6.3", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "jest-watcher": "^26.6.2", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/globals": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", - "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/types": "^26.6.2", - "expect": "^26.6.2" - } - }, - "@jest/source-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", - "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-sequencer": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", - "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", - "dev": true, - "requires": { - "@jest/test-result": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "babel-jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", - "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", - "dev": true, - "requires": { - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", - "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", - "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^26.6.2", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "source-map": "~0.6.1" - } - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "jest-config": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", - "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.6.3", - "@jest/types": "^26.6.2", - "babel-jest": "^26.6.3", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.6.2", - "jest-environment-node": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.6.3", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-each": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", - "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2" - } - }, - "jest-environment-jsdom": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", - "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2", - "jsdom": "^16.4.0" - } - }, - "jest-environment-node": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", - "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-jasmine2": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", - "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.6.2", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2", - "throat": "^5.0.0" - } - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-runtime": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", - "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/globals": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "cjs-module-lexer": "^0.6.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.4.1" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - } - } - }, - "@jest/environment": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.5.0.tgz", - "integrity": "sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^25.5.0", - "@jest/types": "^25.5.0", - "jest-mock": "^25.5.0" - }, - "dependencies": { - "@jest/fake-timers": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.5.0.tgz", - "integrity": "sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-util": "^25.5.0", - "lolex": "^5.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - } - } - }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - }, - "dependencies": { - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - } - } - }, - "@jest/globals": { - "version": "25.5.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-25.5.2.tgz", - "integrity": "sha512-AgAS/Ny7Q2RCIj5kZ+0MuKM1wbF0WMLxbCVl/GOMoCNbODRdJ541IxJ98xnZdVSZXivKpJlNPIWa3QmY0l4CXA==", - "dev": true, - "requires": { - "@jest/environment": "^25.5.0", - "@jest/types": "^25.5.0", - "expect": "^25.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "@jest/reporters": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.6.2.tgz", - "integrity": "sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.3", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "node-notifier": "^8.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^7.0.0" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@jest/source-map": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", - "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.1.15", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-25.5.4.tgz", - "integrity": "sha512-pTJGEkSeg1EkCO2YWq6hbFvKNXk8ejqlxiOg1jBNLnWrgXOkdY6UmqZpwGFXNnRt9B8nO1uWMzLLZ4eCmhkPNA==", - "dev": true, - "requires": { - "@jest/test-result": "^25.5.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^25.5.1", - "jest-runner": "^25.5.4", - "jest-runtime": "^25.5.4" - }, - "dependencies": { - "@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", - "slash": "^3.0.0" - } - }, - "@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "jest-docblock": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-25.3.0.tgz", - "integrity": "sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-haste-map": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.5.1.tgz", - "integrity": "sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "@types/graceful-fs": "^4.1.2", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-serializer": "^25.5.0", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" - } - }, - "jest-leak-detector": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-25.5.0.tgz", - "integrity": "sha512-rV7JdLsanS8OkdDpZtgBf61L5xZ4NnYLBq72r6ldxahJWWczZjXawRsoHyXzibM5ed7C2QRjpp6ypgwGdKyoVA==", - "dev": true, - "requires": { - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-resolve": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", - "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "read-pkg-up": "^7.0.1", - "realpath-native": "^2.0.0", - "resolve": "^1.17.0", - "slash": "^3.0.0" - } - }, - "jest-runner": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-25.5.4.tgz", - "integrity": "sha512-V/2R7fKZo6blP8E9BL9vJ8aTU4TH2beuqGNxHbxi6t14XzTb+x90B3FRgdvuHm41GY8ch4xxvf0ATH4hdpjTqg==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/environment": "^25.5.0", - "@jest/test-result": "^25.5.0", - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^25.5.4", - "jest-docblock": "^25.3.0", - "jest-haste-map": "^25.5.1", - "jest-jasmine2": "^25.5.4", - "jest-leak-detector": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-resolve": "^25.5.1", - "jest-runtime": "^25.5.4", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - } - }, - "jest-serializer": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.5.0.tgz", - "integrity": "sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "jest-worker": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", - "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } - } - }, - "@jest/transform": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.6.2.tgz", - "integrity": "sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.6.2", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.6.2", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "dependencies": { - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - } - } - }, - "@jimp/bmp": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.16.13.tgz", - "integrity": "sha512-9edAxu7N2FX7vzkdl5Jo1BbACfycUtBQX+XBMcHA2bk62P8R0otgkHg798frgAk/WxQIzwxqOH6wMiCwrlAzdQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13", - "bmp-js": "^0.1.0" - } - }, - "@jimp/core": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/core/-/core-0.16.13.tgz", - "integrity": "sha512-qXpA1tzTnlkTku9yqtuRtS/wVntvE6f3m3GNxdTdtmc+O+Wcg9Xo2ABPMh7Nc0AHbMKzwvwgB2JnjZmlmJEObg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13", - "any-base": "^1.1.0", - "buffer": "^5.2.0", - "exif-parser": "^0.1.12", - "file-type": "^16.5.4", - "load-bmfont": "^1.3.1", - "mkdirp": "^0.5.1", - "phin": "^2.9.1", - "pixelmatch": "^4.0.2", - "tinycolor2": "^1.4.1" - } - }, - "@jimp/custom": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.16.13.tgz", - "integrity": "sha512-LTATglVUPGkPf15zX1wTMlZ0+AU7cGEGF6ekVF1crA8eHUWsGjrYTB+Ht4E3HTrCok8weQG+K01rJndCp/l4XA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/core": "^0.16.13" - } - }, - "@jimp/gif": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/gif/-/gif-0.16.13.tgz", - "integrity": "sha512-yFAMZGv3o+YcjXilMWWwS/bv1iSqykFahFMSO169uVMtfQVfa90kt4/kDwrXNR6Q9i6VHpFiGZMlF2UnHClBvg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13", - "gifwrap": "^0.9.2", - "omggif": "^1.0.9" - } - }, - "@jimp/jpeg": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/jpeg/-/jpeg-0.16.13.tgz", - "integrity": "sha512-BJHlDxzTlCqP2ThqP8J0eDrbBfod7npWCbJAcfkKqdQuFk0zBPaZ6KKaQKyKxmWJ87Z6ohANZoMKEbtvrwz1AA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13", - "jpeg-js": "^0.4.2" - } - }, - "@jimp/plugin-blit": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blit/-/plugin-blit-0.16.13.tgz", - "integrity": "sha512-8Z1k96ZFxlhK2bgrY1JNWNwvaBeI/bciLM0yDOni2+aZwfIIiC7Y6PeWHTAvjHNjphz+XCt01WQmOYWCn0ML6g==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-blur": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-blur/-/plugin-blur-0.16.13.tgz", - "integrity": "sha512-PvLrfa8vkej3qinlebyhLpksJgCF5aiysDMSVhOZqwH5nQLLtDE9WYbnsofGw4r0VVpyw3H/ANCIzYTyCtP9Cg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-circle": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-circle/-/plugin-circle-0.16.13.tgz", - "integrity": "sha512-RNave7EFgZrb5V5EpdvJGAEHMnDAJuwv05hKscNfIYxf0kR3KhViBTDy+MoTnMlIvaKFULfwIgaZWzyhuINMzA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-color": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-color/-/plugin-color-0.16.13.tgz", - "integrity": "sha512-xW+9BtEvoIkkH/Wde9ql4nAFbYLkVINhpgAE7VcBUsuuB34WUbcBl/taOuUYQrPEFQJ4jfXiAJZ2H/rvKjCVnQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13", - "tinycolor2": "^1.4.1" - } - }, - "@jimp/plugin-contain": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-contain/-/plugin-contain-0.16.13.tgz", - "integrity": "sha512-QayTXw4tXMwU6q6acNTQrTTFTXpNRBe+MgTGMDU0lk+23PjlFCO/9sacflelG8lsp7vNHhAxFeHptDMAksEYzg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-cover": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-cover/-/plugin-cover-0.16.13.tgz", - "integrity": "sha512-BSsP71GTNaqWRcvkbWuIVH+zK7b3TSNebbhDkFK0fVaUTzHuKMS/mgY4hDZIEVt7Rf5FjadAYtsujHN9w0iSYA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-crop": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-crop/-/plugin-crop-0.16.13.tgz", - "integrity": "sha512-WEl2tPVYwzYL8OKme6Go2xqiWgKsgxlMwyHabdAU4tXaRwOCnOI7v4021gCcBb9zn/oWwguHuKHmK30Fw2Z/PA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-displace": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-displace/-/plugin-displace-0.16.13.tgz", - "integrity": "sha512-qt9WKq8vWrcjySa9DyQ0x/RBMHQeiVjdVSY1SJsMjssPUf0pS74qorcuAkGi89biN3YoGUgPkpqECnAWnYwgGA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-dither": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-dither/-/plugin-dither-0.16.13.tgz", - "integrity": "sha512-5/N3yJggbWQTlGZHQYJPmQXEwR52qaXjEzkp1yRBbtdaekXE3BG/suo0fqeoV/csf8ooI78sJzYmIrxNoWVtgQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-fisheye": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-fisheye/-/plugin-fisheye-0.16.13.tgz", - "integrity": "sha512-2rZmTdFbT/cF9lEZIkXCYO0TsT114Q27AX5IAo0Sju6jVQbvIk1dFUTnwLDadTo8wkJlFzGqMQ24Cs8cHWOliA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-flip": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-flip/-/plugin-flip-0.16.13.tgz", - "integrity": "sha512-EmcgAA74FTc5u7Z+hUO/sRjWwfPPLuOQP5O64x5g4j0T12Bd29IgsYZxoutZo/rb3579+JNa/3wsSEmyVv1EpA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-gaussian": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-gaussian/-/plugin-gaussian-0.16.13.tgz", - "integrity": "sha512-A1XKfGQD0iDdIiKqFYi8nZMv4dDVYdxbrmgR7y/CzUHhSYdcmoljLIIsZZM3Iks/Wa353W3vtvkWLuDbQbch1w==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-invert": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-invert/-/plugin-invert-0.16.13.tgz", - "integrity": "sha512-xFMrIn7czEZbdbMzZWuaZFnlLGJDVJ82y5vlsKsXRTG2kcxRsMPXvZRWHV57nSs1YFsNqXSbrC8B98n0E32njQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-mask": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-mask/-/plugin-mask-0.16.13.tgz", - "integrity": "sha512-wLRYKVBXql2GAYgt6FkTnCfE+q5NomM7Dlh0oIPGAoMBWDyTx0eYutRK6PlUrRK2yMHuroAJCglICTbxqGzowQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-normalize": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-normalize/-/plugin-normalize-0.16.13.tgz", - "integrity": "sha512-3tfad0n9soRna4IfW9NzQdQ2Z3ijkmo21DREHbE6CGcMIxOSvfRdSvf1qQPApxjTSo8LTU4MCi/fidx/NZ0GqQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-print": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-print/-/plugin-print-0.16.13.tgz", - "integrity": "sha512-0m6i3p01PGRkGAK9r53hDYrkyMq+tlhLOIbsSTmZyh6HLshUKlTB7eXskF5OpVd5ZUHoltlNc6R+ggvKIzxRFw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13", - "load-bmfont": "^1.4.0" - } - }, - "@jimp/plugin-resize": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-resize/-/plugin-resize-0.16.13.tgz", - "integrity": "sha512-qoqtN8LDknm3fJm9nuPygJv30O3vGhSBD2TxrsCnhtOsxKAqVPJtFVdGd/qVuZ8nqQANQmTlfqTiK9mVWQ7MiQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-rotate": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-rotate/-/plugin-rotate-0.16.13.tgz", - "integrity": "sha512-Ev+Jjmj1nHYw897z9C3R9dYsPv7S2/nxdgfFb/h8hOwK0Ovd1k/+yYS46A0uj/JCKK0pQk8wOslYBkPwdnLorw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-scale": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-scale/-/plugin-scale-0.16.13.tgz", - "integrity": "sha512-05POQaEJVucjTiSGMoH68ZiELc7QqpIpuQlZ2JBbhCV+WCbPFUBcGSmE7w4Jd0E2GvCho/NoMODLwgcVGQA97A==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-shadow": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-shadow/-/plugin-shadow-0.16.13.tgz", - "integrity": "sha512-nmu5VSZ9hsB1JchTKhnnCY+paRBnwzSyK5fhkhtQHHoFD5ArBQ/5wU8y6tCr7k/GQhhGq1OrixsECeMjPoc8Zw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugin-threshold": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugin-threshold/-/plugin-threshold-0.16.13.tgz", - "integrity": "sha512-+3zArBH0OE3Rhjm4HyAokMsZlIq5gpQec33CncyoSwxtRBM2WAhUVmCUKuBo+Lr/2/4ISoY4BWpHKhMLDix6cA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13" - } - }, - "@jimp/plugins": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/plugins/-/plugins-0.16.13.tgz", - "integrity": "sha512-CJLdqODEhEVs4MgWCxpWL5l95sCBlkuSLz65cxEm56X5akIsn4LOlwnKoSEZioYcZUBvHhCheH67AyPTudfnQQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/plugin-blit": "^0.16.13", - "@jimp/plugin-blur": "^0.16.13", - "@jimp/plugin-circle": "^0.16.13", - "@jimp/plugin-color": "^0.16.13", - "@jimp/plugin-contain": "^0.16.13", - "@jimp/plugin-cover": "^0.16.13", - "@jimp/plugin-crop": "^0.16.13", - "@jimp/plugin-displace": "^0.16.13", - "@jimp/plugin-dither": "^0.16.13", - "@jimp/plugin-fisheye": "^0.16.13", - "@jimp/plugin-flip": "^0.16.13", - "@jimp/plugin-gaussian": "^0.16.13", - "@jimp/plugin-invert": "^0.16.13", - "@jimp/plugin-mask": "^0.16.13", - "@jimp/plugin-normalize": "^0.16.13", - "@jimp/plugin-print": "^0.16.13", - "@jimp/plugin-resize": "^0.16.13", - "@jimp/plugin-rotate": "^0.16.13", - "@jimp/plugin-scale": "^0.16.13", - "@jimp/plugin-shadow": "^0.16.13", - "@jimp/plugin-threshold": "^0.16.13", - "timm": "^1.6.1" - } - }, - "@jimp/png": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/png/-/png-0.16.13.tgz", - "integrity": "sha512-8cGqINvbWJf1G0Her9zbq9I80roEX0A+U45xFby3tDWfzn+Zz8XKDF1Nv9VUwVx0N3zpcG1RPs9hfheG4Cq2kg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/utils": "^0.16.13", - "pngjs": "^3.3.3" - } - }, - "@jimp/tiff": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/tiff/-/tiff-0.16.13.tgz", - "integrity": "sha512-oJY8d9u95SwW00VPHuCNxPap6Q1+E/xM5QThb9Hu+P6EGuu6lIeLaNBMmFZyblwFbwrH+WBOZlvIzDhi4Dm/6Q==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "utif": "^2.0.1" - } - }, - "@jimp/types": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/types/-/types-0.16.13.tgz", - "integrity": "sha512-mC0yVNUobFDjoYLg4hoUwzMKgNlxynzwt3cDXzumGvRJ7Kb8qQGOWJQjQFo5OxmGExqzPphkirdbBF88RVLBCg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/bmp": "^0.16.13", - "@jimp/gif": "^0.16.13", - "@jimp/jpeg": "^0.16.13", - "@jimp/png": "^0.16.13", - "@jimp/tiff": "^0.16.13", - "timm": "^1.6.1" - } - }, - "@jimp/utils": { - "version": "0.16.13", - "resolved": "https://registry.npmjs.org/@jimp/utils/-/utils-0.16.13.tgz", - "integrity": "sha512-VyCpkZzFTHXtKgVO35iKN0sYR10psGpV6SkcSeV4oF7eSYlR8Bl6aQLCzVeFjvESF7mxTmIiI3/XrMobVrtxDA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "regenerator-runtime": "^0.13.3" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - } - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@juggle/resize-observer": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==" - }, - "@malept/cross-spawn-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", - "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.1" - } - }, - "@malept/flatpak-bundler": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", - "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "fs-extra": "^9.0.0", - "lodash": "^4.17.15", - "tmp-promise": "^3.0.2" - }, - "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } - } - }, - "@nicolo-ribaudo/eslint-scope-5-internals": { - "version": "5.1.1-v1", - "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", - "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", - "dev": true, - "requires": { - "eslint-scope": "5.1.1" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - } - } - }, - "@react-aria/breadcrumbs": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@react-aria/breadcrumbs/-/breadcrumbs-3.5.4.tgz", - "integrity": "sha512-CtBAL7xDDHXpZvmglhEYbNAXeoXNl4Ke+Rwn2WTHVr9blry3P17IL4Elou5QAkyzI2GNHnXUs9K6lzX/uLv+kQ==", - "requires": { - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/link": "^3.5.3", - "@react-aria/utils": "^3.19.0", - "@react-types/breadcrumbs": "^3.6.1", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/button": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@react-aria/button/-/button-3.8.1.tgz", - "integrity": "sha512-igxZ871An3Clpmpw+beN8F792NfEnEaLRAZ4jITtC/FdzwQwRM7eCu/ZEaqpNtbUtruAmYhafnG/2uCkKhTpTw==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/interactions": "^3.17.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/toggle": "^3.6.1", - "@react-types/button": "^3.7.4", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/calendar": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@react-aria/calendar/-/calendar-3.4.1.tgz", - "integrity": "sha512-mXz4v0iSPtPX9SR6LaIzSAE5n2blCujaQ+EiM6G91TM3S7BsHqyELiJcV/ucDD7ncr6ovJpm1JsLFOOAn44YTQ==", - "requires": { - "@internationalized/date": "^3.4.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/live-announcer": "^3.3.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/calendar": "^3.3.1", - "@react-types/button": "^3.7.4", - "@react-types/calendar": "^3.3.1", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/checkbox": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@react-aria/checkbox/-/checkbox-3.10.0.tgz", - "integrity": "sha512-1s5jkmag+41Fa2BwoOoM5cRRadDh3N8khgsziuGzD0NqvZLRCtHgDetNlileezFHwOeOWK6zCqDOrYLJhcMi8g==", - "requires": { - "@react-aria/label": "^3.6.1", - "@react-aria/toggle": "^3.7.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/checkbox": "^3.4.4", - "@react-stately/toggle": "^3.6.1", - "@react-types/checkbox": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/combobox": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/@react-aria/combobox/-/combobox-3.6.3.tgz", - "integrity": "sha512-zry8Jh//BrGZ7+qJP3iiFZeb3+EuOjjy6MTmDT3zg60YwGgDArsaSA5s0gopF0fuiOKqlDRCDZ+T3CLyoeOomA==", - "requires": { - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/listbox": "^3.10.1", - "@react-aria/live-announcer": "^3.3.1", - "@react-aria/menu": "^3.10.1", - "@react-aria/overlays": "^3.16.0", - "@react-aria/selection": "^3.16.1", - "@react-aria/textfield": "^3.11.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/collections": "^3.10.0", - "@react-stately/combobox": "^3.6.0", - "@react-stately/layout": "^3.13.0", - "@react-types/button": "^3.7.4", - "@react-types/combobox": "^3.7.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/datepicker": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-aria/datepicker/-/datepicker-3.6.0.tgz", - "integrity": "sha512-b6LThZJSF9mboFeATUMboTIxSGgW7MjH2vnDZ7UdRQ/ZHZVNX+fjzQ5uOQQ30wJRP44t273jfvxc9OXEMD9CPQ==", - "requires": { - "@internationalized/date": "^3.4.0", - "@internationalized/number": "^3.2.1", - "@internationalized/string": "^3.1.1", - "@react-aria/focus": "^3.14.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/label": "^3.6.1", - "@react-aria/spinbutton": "^3.5.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/datepicker": "^3.6.0", - "@react-types/button": "^3.7.4", - "@react-types/calendar": "^3.3.1", - "@react-types/datepicker": "^3.5.0", - "@react-types/dialog": "^3.5.4", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/dialog": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@react-aria/dialog/-/dialog-3.5.4.tgz", - "integrity": "sha512-+YGjX5ygYvFvnRGDy7LVTL2uRCH5VYosMNKn0vyel99SiwHH9d8fdnnJjVvSJ3u8kvoXk22+OnRE2/vEX+G1EA==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/overlays": "^3.16.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/overlays": "^3.6.1", - "@react-types/dialog": "^3.5.4", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/dnd": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@react-aria/dnd/-/dnd-3.4.0.tgz", - "integrity": "sha512-4KxdC2FXPL/+ZAsv7RVrZ+kC35dxU4yBowdtmZuagTasLSgfuS3SSyY/VRVgQ+Uq8lUgb55u62+km6xc47n7zA==", - "requires": { - "@internationalized/string": "^3.1.1", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/live-announcer": "^3.3.1", - "@react-aria/overlays": "^3.16.0", - "@react-aria/utils": "^3.19.0", - "@react-aria/visually-hidden": "^3.8.3", - "@react-stately/dnd": "^3.2.3", - "@react-types/button": "^3.7.4", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/focus": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.14.0.tgz", - "integrity": "sha512-Xw7PxLT0Cqcz22OVtTZ8+HvurDogn9/xntzoIbVjpRFWzhlYe5WHnZL+2+gIiKf7EZ18Ma9/QsCnrVnvrky/Kw==", - "requires": { - "@react-aria/interactions": "^3.17.0", - "@react-aria/utils": "^3.19.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0", - "clsx": "^1.1.1" - } - }, - "@react-aria/grid": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@react-aria/grid/-/grid-3.8.1.tgz", - "integrity": "sha512-J/k7i2ZnMgTv3csMIQrIanbb0mWzlokT86QfKDgQpKxIvrPGbdrVJTx99tzJxEzYeXN9w11Jjwjal65rZCs4rQ==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/live-announcer": "^3.3.1", - "@react-aria/selection": "^3.16.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/collections": "^3.10.0", - "@react-stately/grid": "^3.8.0", - "@react-stately/selection": "^3.13.3", - "@react-stately/virtualizer": "^3.6.1", - "@react-types/checkbox": "^3.5.0", - "@react-types/grid": "^3.2.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/gridlist": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@react-aria/gridlist/-/gridlist-3.5.1.tgz", - "integrity": "sha512-VEyEgOKov3lKizoqHpEUIZD+JzyyH8TK0WzWFo/f6lNvmzbYhnW2ciFmqD5DS3bHxLkoXMFdaiA0/MLofRYbHQ==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/grid": "^3.8.1", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/selection": "^3.16.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/list": "^3.9.1", - "@react-types/checkbox": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/i18n": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.8.1.tgz", - "integrity": "sha512-ftH3saJlhWaHoHEDb/YjYqP8I4/9t4Ksf0D0kvPDRfRcL98DKUSHZD77+EmbjsmzJInzm76qDeEV0FYl4oj7gg==", - "requires": { - "@internationalized/date": "^3.4.0", - "@internationalized/message": "^3.1.1", - "@internationalized/number": "^3.2.1", - "@internationalized/string": "^3.1.1", - "@react-aria/ssr": "^3.7.1", - "@react-aria/utils": "^3.19.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/interactions": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.17.0.tgz", - "integrity": "sha512-v4BI5Nd8gi8s297fHpgjDDXOyufX+FPHJ31rkMwY6X1nR5gtI0+2jNOL4lh7s+cWzszpA0wpwIrKUPGhhLyUjQ==", - "requires": { - "@react-aria/ssr": "^3.7.1", - "@react-aria/utils": "^3.19.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/label": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@react-aria/label/-/label-3.6.1.tgz", - "integrity": "sha512-hR7Qx6q0BjOJi/YG5pI13QTQA/2oaXMYdzDCx4Faz8qaY9CCsLjFpo5pUUwRhNieGmf/nHJq6jiYbJqfaONuTQ==", - "requires": { - "@react-aria/utils": "^3.19.0", - "@react-types/label": "^3.7.5", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/link": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@react-aria/link/-/link-3.5.3.tgz", - "integrity": "sha512-WGz/s/czlb/+wJUnBfnfaRuvOSiNTaQDTk9QsEEwrTkkYbWo7fMlH5Tc7c0Uxem4UuUblYXKth5SskiKQNWc0w==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/interactions": "^3.17.0", - "@react-aria/utils": "^3.19.0", - "@react-types/link": "^3.4.4", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/listbox": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@react-aria/listbox/-/listbox-3.10.1.tgz", - "integrity": "sha512-hG+f7URcVk7saRG6bemCRaZSNMCg5U51ol/EuoKyHyvd0Vfq/AcsLYrg8vOyRWTsPwjxFtMLItNOZo36KIDs5w==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/interactions": "^3.17.0", - "@react-aria/label": "^3.6.1", - "@react-aria/selection": "^3.16.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/collections": "^3.10.0", - "@react-stately/list": "^3.9.1", - "@react-types/listbox": "^3.4.3", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/live-announcer": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.3.1.tgz", - "integrity": "sha512-hsc77U7S16trM86d+peqJCOCQ7/smO1cybgdpOuzXyiwcHQw8RQ4GrXrS37P4Ux/44E9nMZkOwATQRT2aK8+Ew==", - "requires": { - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/menu": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@react-aria/menu/-/menu-3.10.1.tgz", - "integrity": "sha512-FOb16XVejZgl4sFpclLvGd2RCvUBwl2bzFdAnss8Nd6Mx+h4m0bPeDT102k9v1Vjo7OGeqzvMyNU/KM4FwUGGA==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/overlays": "^3.16.0", - "@react-aria/selection": "^3.16.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/collections": "^3.10.0", - "@react-stately/menu": "^3.5.4", - "@react-stately/tree": "^3.7.1", - "@react-types/button": "^3.7.4", - "@react-types/menu": "^3.9.3", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/meter": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/@react-aria/meter/-/meter-3.4.4.tgz", - "integrity": "sha512-dbn4Ur/w2PzqO8ChrVfkr+GHqaqbMElQlx0HVVbrHhOS1fCx1CC86bn8h767lhFMvh54Kv9MY2cYuygmVBxP1w==", - "requires": { - "@react-aria/progress": "^3.4.4", - "@react-types/meter": "^3.3.3", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/numberfield": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@react-aria/numberfield/-/numberfield-3.7.0.tgz", - "integrity": "sha512-vXerG2mCdAM82AHc7ZiMhKxpWHgjnG+YXkBu5wGRYunmg5exj4n5QVFFIAQgCiToCoJp7nhY9d34BclJbmHwrQ==", - "requires": { - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/live-announcer": "^3.3.1", - "@react-aria/spinbutton": "^3.5.1", - "@react-aria/textfield": "^3.11.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/numberfield": "^3.6.0", - "@react-types/button": "^3.7.4", - "@react-types/numberfield": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@react-types/textfield": "^3.7.3", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/overlays": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.16.0.tgz", - "integrity": "sha512-jclyCqs1U4XqDA1DAdZaiijKtHLVZ78FV0+IzL4QQfrvzCPC+ba+MC8pe/tw8dMQzXBSnTx/IEqOHu07IwrESQ==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/ssr": "^3.7.1", - "@react-aria/utils": "^3.19.0", - "@react-aria/visually-hidden": "^3.8.3", - "@react-stately/overlays": "^3.6.1", - "@react-types/button": "^3.7.4", - "@react-types/overlays": "^3.8.1", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/progress": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/@react-aria/progress/-/progress-3.4.4.tgz", - "integrity": "sha512-k4EBtYcmqw3j/JYJtn+xKPM8/P1uPcFGSBqvwmVdwDknuT/hR1os3wIKm712N/Ubde8hTeeLcaa38HYezSF8BA==", - "requires": { - "@react-aria/i18n": "^3.8.1", - "@react-aria/label": "^3.6.1", - "@react-aria/utils": "^3.19.0", - "@react-types/progress": "^3.4.2", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/radio": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@react-aria/radio/-/radio-3.7.0.tgz", - "integrity": "sha512-ygSr3ow9avO5BNNwm4aL70EwvLHrBbhSVfG1lmP2k5u/2dxn+Pnm3BGMaEriOFiAyAV4nLGUZAjER6GWXfu5cA==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/label": "^3.6.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/radio": "^3.8.3", - "@react-types/radio": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/searchfield": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@react-aria/searchfield/-/searchfield-3.5.4.tgz", - "integrity": "sha512-0jHQYoqT4OutAXNAsWjVJPwzTgZg5wAXIEuQlJuhdfBrjisbgGrYlSHN3Si7x2quXzvdExVL7e0aWRuu6bjjYg==", - "requires": { - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/textfield": "^3.11.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/searchfield": "^3.4.4", - "@react-types/button": "^3.7.4", - "@react-types/searchfield": "^3.4.3", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/select": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/@react-aria/select/-/select-3.12.0.tgz", - "integrity": "sha512-2n7NezoR6xfrcfCAmg8hz8+4i4Sci/F5LGoqa6/KlESrMSIRI7FLHNsZV+4qE4dWLvDwtnxG2itIfQad1iAqUQ==", - "requires": { - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/label": "^3.6.1", - "@react-aria/listbox": "^3.10.1", - "@react-aria/menu": "^3.10.1", - "@react-aria/selection": "^3.16.1", - "@react-aria/utils": "^3.19.0", - "@react-aria/visually-hidden": "^3.8.3", - "@react-stately/select": "^3.5.3", - "@react-types/button": "^3.7.4", - "@react-types/select": "^3.8.2", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/selection": { - "version": "3.16.1", - "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.16.1.tgz", - "integrity": "sha512-mOoAeNjq23H5p6IaeoyLHavYHRXOuNUlv8xO4OzYxIEnxmAvk4PCgidGLFYrr4sloftUMgTTL3LpCj21ylBS9A==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/collections": "^3.10.0", - "@react-stately/selection": "^3.13.3", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/separator": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@react-aria/separator/-/separator-3.3.4.tgz", - "integrity": "sha512-Wb4TJ/PF6Q1yMIKfPM5z+SYwvNRW4RKBzB4oTNAWpSnj8pFimRNXYtyqIowZa67HOPgqzLptqxx6+mAsffCiuQ==", - "requires": { - "@react-aria/utils": "^3.19.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/slider": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-aria/slider/-/slider-3.6.0.tgz", - "integrity": "sha512-jfFv5q8wX4aAPxoxLcMmBFBUnAdjsryMNLgwN0fosKBLZzshyH9d4WT+Vc4TfVjs5+HHPbGQXeRLo3pgvIJkGQ==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/label": "^3.6.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/radio": "^3.8.3", - "@react-stately/slider": "^3.4.1", - "@react-types/radio": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@react-types/slider": "^3.6.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/spinbutton": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.5.1.tgz", - "integrity": "sha512-VUMPxjt7TEw38kSyqE3A20UlQ5/0GvkeV/Q61tcjdef9vcf9Z+EJ7AKCcqbVLd9wIKYlPaJQ0JMHJrFJ9Mc91g==", - "requires": { - "@react-aria/i18n": "^3.8.1", - "@react-aria/live-announcer": "^3.3.1", - "@react-aria/utils": "^3.19.0", - "@react-types/button": "^3.7.4", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/ssr": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.7.1.tgz", - "integrity": "sha512-ovVPSD1WlRpZHt7GI9DqJrWG3OIYS+NXQ9y5HIewMJpSe+jPQmMQfyRmgX4EnvmxSlp0u04Wg/7oItcoSIb/RA==", - "requires": { - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/switch": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@react-aria/switch/-/switch-3.5.3.tgz", - "integrity": "sha512-3sV78Oa12/aU+M9P7BqUDdp/zm2zZA2QvtLLdxykrH04AJp0hLNBnmaTDXJVaGPPiU0umOB0LWDquA3apkBiBA==", - "requires": { - "@react-aria/toggle": "^3.7.0", - "@react-stately/toggle": "^3.6.1", - "@react-types/switch": "^3.4.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/table": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@react-aria/table/-/table-3.11.0.tgz", - "integrity": "sha512-kPIQWh1dIHFAzl+rzfUGgbpAZGerMwwW0zNvRwcLpBOl/nrOwV5Zg/wuCC5cSdkwgo3SghYbcUaM19teve0UcQ==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/grid": "^3.8.1", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/live-announcer": "^3.3.1", - "@react-aria/selection": "^3.16.1", - "@react-aria/utils": "^3.19.0", - "@react-aria/visually-hidden": "^3.8.3", - "@react-stately/collections": "^3.10.0", - "@react-stately/flags": "^3.0.0", - "@react-stately/table": "^3.11.0", - "@react-stately/virtualizer": "^3.6.1", - "@react-types/checkbox": "^3.5.0", - "@react-types/grid": "^3.2.0", - "@react-types/shared": "^3.19.0", - "@react-types/table": "^3.8.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/tabs": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@react-aria/tabs/-/tabs-3.6.2.tgz", - "integrity": "sha512-FjI0h1Z4TsLOvIODhdDrVLz0O8RAqxDi58DO88CwkdUrWwZspNEpSpHhDarzUT7MlX3X72lsAUwvQLqY1OmaBQ==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/i18n": "^3.8.1", - "@react-aria/interactions": "^3.17.0", - "@react-aria/selection": "^3.16.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/list": "^3.9.1", - "@react-stately/tabs": "^3.5.1", - "@react-types/shared": "^3.19.0", - "@react-types/tabs": "^3.3.1", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/textfield": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@react-aria/textfield/-/textfield-3.11.0.tgz", - "integrity": "sha512-07pHRuWeLmsmciWL8y9azUwcBYi1IBmOT9KxBgLdLK5NLejd7q2uqd0WEEgZkOc48i2KEtMDgBslc4hA+cmHow==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/label": "^3.6.1", - "@react-aria/utils": "^3.19.0", - "@react-types/shared": "^3.19.0", - "@react-types/textfield": "^3.7.3", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/toggle": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.7.0.tgz", - "integrity": "sha512-8Rpqolm8dxesyHi03RSmX2MjfHO/YwdhyEpAMMO0nsajjdtZneGzIOXzyjdWCPWwwzahcpwRHOA4qfMiRz+axA==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/interactions": "^3.17.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/toggle": "^3.6.1", - "@react-types/checkbox": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@react-types/switch": "^3.4.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/tooltip": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@react-aria/tooltip/-/tooltip-3.6.1.tgz", - "integrity": "sha512-CVSmndGXhC5EkkGrKcC8EVdAKCbSLTyJibpojC/8uOCbGIQglq3xCAr68PElNNO8+sFDJ4fp9ZzEeDi0Qyxf0w==", - "requires": { - "@react-aria/focus": "^3.14.0", - "@react-aria/interactions": "^3.17.0", - "@react-aria/utils": "^3.19.0", - "@react-stately/tooltip": "^3.4.3", - "@react-types/shared": "^3.19.0", - "@react-types/tooltip": "^3.4.3", - "@swc/helpers": "^0.5.0" - } - }, - "@react-aria/utils": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.19.0.tgz", - "integrity": "sha512-5GXqTCrUQtr78aiLVHZoeeGPuAxO4lCM+udWbKpSCh5xLfCZ7zFlZV9Q9FS0ea+IQypUcY8ngXCLsf22nSu/yg==", - "requires": { - "@react-aria/ssr": "^3.7.1", - "@react-stately/utils": "^3.7.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0", - "clsx": "^1.1.1" - } - }, - "@react-aria/visually-hidden": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.3.tgz", - "integrity": "sha512-Ln3rqUnPF/UiiPjj8Xjc5FIagwNvG16qtAR2Diwnsju+X9o2xeDEZhN/5fg98PxH2JBS3IvtsmMZRzPT9mhpmg==", - "requires": { - "@react-aria/interactions": "^3.17.0", - "@react-aria/utils": "^3.19.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0", - "clsx": "^1.1.1" - } - }, - "@react-stately/calendar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.3.1.tgz", - "integrity": "sha512-wD5hvdL6Bs8fL2oYkGB/7jGR5Z4ARrrd5uK7T2RwthYguvw95og99A6uUti8ssPGzEkPmJvokds59ov6UmBDdA==", - "requires": { - "@internationalized/date": "^3.4.0", - "@react-stately/utils": "^3.7.0", - "@react-types/calendar": "^3.3.1", - "@react-types/datepicker": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/checkbox": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/@react-stately/checkbox/-/checkbox-3.4.4.tgz", - "integrity": "sha512-TYNod4+4TmS73F+sbKXAMoBH810ZEBdpMfXlNttUCXfVkDXc38W7ucvpQxXPwF+d+ZhGk4DJZsUYqfVPyXXSGg==", - "requires": { - "@react-stately/toggle": "^3.6.1", - "@react-stately/utils": "^3.7.0", - "@react-types/checkbox": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/collections": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.10.0.tgz", - "integrity": "sha512-PyJEFmt9X0kDMF7D4StGnTdXX1hgyUcTXvvXU2fEw6OyXLtmfWFHmFARRtYbuelGKk6clmJojYmIEds0k8jdww==", - "requires": { - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/combobox": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-stately/combobox/-/combobox-3.6.0.tgz", - "integrity": "sha512-TguTMh9hr5GjtT4sKragsiKqer2PXSa2cA/8bPGCox0E9VGNPnYWOYMZ5FXS3FO2OotHxOlbH1LNNKwiE255KQ==", - "requires": { - "@react-stately/collections": "^3.10.0", - "@react-stately/list": "^3.9.1", - "@react-stately/menu": "^3.5.4", - "@react-stately/select": "^3.5.3", - "@react-stately/utils": "^3.7.0", - "@react-types/combobox": "^3.7.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/data": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/@react-stately/data/-/data-3.10.1.tgz", - "integrity": "sha512-7RBVr5NMGwruZkxuWZtGrZydPlfoZ2VNxzUkc9VXF1gAWbGP7l0t2MoxDgigznUHNS/iYBJ4Y/iYWx3GXtDsrQ==", - "requires": { - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/datepicker": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-stately/datepicker/-/datepicker-3.6.0.tgz", - "integrity": "sha512-NlaZNknzIXj8zjmwtyMaXIWAyCRIk2g6xQVqHuxZKjx8ZA44IEXiHqhqCmJH3KNjhrP1hvNPsE2Jl+kSbYZj/A==", - "requires": { - "@internationalized/date": "^3.4.0", - "@internationalized/string": "^3.1.1", - "@react-stately/overlays": "^3.6.1", - "@react-stately/utils": "^3.7.0", - "@react-types/datepicker": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/dnd": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@react-stately/dnd/-/dnd-3.2.3.tgz", - "integrity": "sha512-gE0bfKr2CY2LIWpVSee/+Xq74gaquQ5WIhMNDPPjRDuWiIvhAd1vCwqfqVKXGZbn3G97Ak/BIpwhvBvVQVD/8g==", - "requires": { - "@react-stately/selection": "^3.13.3", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/flags": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.0.0.tgz", - "integrity": "sha512-e3i2ItHbIa0eEwmSXAnPdD7K8syW76JjGe8ENxwFJPW/H1Pu9RJfjkCb/Mq0WSPN/TpxBb54+I9TgrGhbCoZ9w==", - "requires": { - "@swc/helpers": "^0.4.14" - }, - "dependencies": { - "@swc/helpers": { - "version": "0.4.36", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.36.tgz", - "integrity": "sha512-5lxnyLEYFskErRPenYItLRSge5DjrJngYKdVjRSrWfza9G6KkgHEXi0vUZiyUeMU5JfXH1YnvXZzSp8ul88o2Q==", - "requires": { - "legacy-swc-helpers": "npm:@swc/helpers@=0.4.14", - "tslib": "^2.4.0" - }, - "dependencies": { - "legacy-swc-helpers": { - "version": "npm:@swc/helpers@0.4.14", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", - "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", - "requires": { - "tslib": "^2.4.0" - } - } - } - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } - } - }, - "@react-stately/grid": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.8.0.tgz", - "integrity": "sha512-+3Q6D3W5FTc9/t1Gz35sH0NRiJ2u95aDls9ogBNulC/kQvYaF31NT34QdvpstcfrcCFtF+D49+TkesklZRHJlw==", - "requires": { - "@react-stately/collections": "^3.10.0", - "@react-stately/selection": "^3.13.3", - "@react-types/grid": "^3.2.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/layout": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/@react-stately/layout/-/layout-3.13.0.tgz", - "integrity": "sha512-ktTbD4IP82+4JilJ2iua3qmAeLDhsGUlY8fdYCEvs2BIhr87Hyalk7kMegPoU7bgo9kV9NS4BEf3ZH7DoaxLoQ==", - "requires": { - "@react-stately/collections": "^3.10.0", - "@react-stately/table": "^3.11.0", - "@react-stately/virtualizer": "^3.6.1", - "@react-types/grid": "^3.2.0", - "@react-types/shared": "^3.19.0", - "@react-types/table": "^3.8.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/list": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.9.1.tgz", - "integrity": "sha512-GiKrxGakzMTZKe3mp410l4xKiHbZplJCGrtqlxq/+YRD0uCQwWGYpRG+z9A7tTCusruRD3m91/OjWsbfbGdiEw==", - "requires": { - "@react-stately/collections": "^3.10.0", - "@react-stately/selection": "^3.13.3", - "@react-stately/utils": "^3.7.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/menu": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@react-stately/menu/-/menu-3.5.4.tgz", - "integrity": "sha512-+Q71fMDhMM1iARPFtwqpXY/8qkb0dN4PBJbcjwjGCumGs+ja2YbZxLBHCP0DYBElS9l6m3ssF47RKNMtF/Oi5w==", - "requires": { - "@react-stately/overlays": "^3.6.1", - "@react-stately/utils": "^3.7.0", - "@react-types/menu": "^3.9.3", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/numberfield": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-stately/numberfield/-/numberfield-3.6.0.tgz", - "integrity": "sha512-4spLEPuYeYQrzs/r13tv/ti4szkJz+6VfVhFNdYwNiW41flUPDpFtGziIqbe2myoEudC+P5WWzryfHkl79tIbQ==", - "requires": { - "@internationalized/number": "^3.2.1", - "@react-stately/utils": "^3.7.0", - "@react-types/numberfield": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/overlays": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.1.tgz", - "integrity": "sha512-c/Mda4ZZmFO4e3XZFd7kqt5wuh6Q/7wYJ+0oG59MfDoQstFwGcJTUnx7S8EUMujbocIOCeOmVPA1eE3DNPC2/A==", - "requires": { - "@react-stately/utils": "^3.7.0", - "@react-types/overlays": "^3.8.1", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/radio": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/@react-stately/radio/-/radio-3.8.3.tgz", - "integrity": "sha512-3ovJ6tDWzl/Qap8065GZS9mQM7LbQwLc7EhhmQ3dn5+pH4pUCHo8Gb0TIcYFsvFMyHrNMg/r8+N3ICq/WDj5NQ==", - "requires": { - "@react-stately/utils": "^3.7.0", - "@react-types/radio": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/searchfield": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/@react-stately/searchfield/-/searchfield-3.4.4.tgz", - "integrity": "sha512-GhgisSXbz18MjGrvLpXXBkb8HeYPCxlrAGp+tq1dCMhAkmgZI9ZqQZB8EFzS7EoXQ/gCb87sIT0vhiy257lxSA==", - "requires": { - "@react-stately/utils": "^3.7.0", - "@react-types/searchfield": "^3.4.3", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/select": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/@react-stately/select/-/select-3.5.3.tgz", - "integrity": "sha512-bzHcCyp2nka6+Gy/YIDM2eWhk+Dz6KP+l2XnGeM62LhbQ7OWdZW/cEjqhCw0MXZFIC+TDMQcLsX4GRkiRDmL7g==", - "requires": { - "@react-stately/collections": "^3.10.0", - "@react-stately/list": "^3.9.1", - "@react-stately/menu": "^3.5.4", - "@react-stately/selection": "^3.13.3", - "@react-stately/utils": "^3.7.0", - "@react-types/select": "^3.8.2", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/selection": { - "version": "3.13.3", - "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.13.3.tgz", - "integrity": "sha512-+CmpZpyIXfbxEwd9eBvo5Jatc2MNX7HinBcW3X8GfvqNzkbgOXETsmXaW6jlKJekvLLE13Is78Ob8NNzZVxQYg==", - "requires": { - "@react-stately/collections": "^3.10.0", - "@react-stately/utils": "^3.7.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/slider": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@react-stately/slider/-/slider-3.4.1.tgz", - "integrity": "sha512-mWnOMTRWO2QHSoH2plQe0yDmjqOHAqHkdGKwPI/vTXiqFVLlFhy5RNz8OkB91PBljIzbHh752W+9Cbi6u2K0yA==", - "requires": { - "@react-aria/i18n": "^3.8.1", - "@react-aria/utils": "^3.19.0", - "@react-stately/utils": "^3.7.0", - "@react-types/shared": "^3.19.0", - "@react-types/slider": "^3.6.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/table": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@react-stately/table/-/table-3.11.0.tgz", - "integrity": "sha512-mHv8KgNHm6scO0gntQc1ZVbQaAqLiNzYi4hxksz2lY+HN2CJbJkYGl/aRt4jmnfpi1xWpwYP5najXdncMAKpGA==", - "requires": { - "@react-stately/collections": "^3.10.0", - "@react-stately/flags": "^3.0.0", - "@react-stately/grid": "^3.8.0", - "@react-stately/selection": "^3.13.3", - "@react-stately/utils": "^3.7.0", - "@react-types/grid": "^3.2.0", - "@react-types/shared": "^3.19.0", - "@react-types/table": "^3.8.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/tabs": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@react-stately/tabs/-/tabs-3.5.1.tgz", - "integrity": "sha512-p1vZOuIS98GMF9jfEHQA6Pir1wYY6j+Gni6DcluNnWj90rLEubuwARNw7uscoOaXKlK/DiZIhkLKSDsA5tbadQ==", - "requires": { - "@react-stately/list": "^3.9.1", - "@react-stately/utils": "^3.7.0", - "@react-types/shared": "^3.19.0", - "@react-types/tabs": "^3.3.1", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/toggle": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.6.1.tgz", - "integrity": "sha512-UUWtuI6gZlX6wpF9/bxBikjyAW1yQojRPCJ4MPkjMMBQL0iveAm3WEQkXRLNycEiOCeoaVFBwAd1L9h9+fuCFg==", - "requires": { - "@react-stately/utils": "^3.7.0", - "@react-types/checkbox": "^3.5.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/tooltip": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@react-stately/tooltip/-/tooltip-3.4.3.tgz", - "integrity": "sha512-IX/XlLdwSQWy75TAOARm6hxajRWV0x/C7vGA54O+JNvvfZ212+nxVyTSduM+zjULzhOPICSSUFKmX4ZCV/aHSg==", - "requires": { - "@react-stately/overlays": "^3.6.1", - "@react-stately/utils": "^3.7.0", - "@react-types/tooltip": "^3.4.3", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/tree": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/@react-stately/tree/-/tree-3.7.1.tgz", - "integrity": "sha512-D0BWcLTRx7EOTdAJCgYV6zm18xpNDxmv4meKJ/WmYSFq1bkHPN75NLv7VPf5Uvsm66xshbO/B3A4HB2/ag1yPA==", - "requires": { - "@react-stately/collections": "^3.10.0", - "@react-stately/selection": "^3.13.3", - "@react-stately/utils": "^3.7.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/utils": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.7.0.tgz", - "integrity": "sha512-VbApRiUV2rhozOfk0Qj9xt0qjVbQfLTgAzXLdrfeZSBnyIgo1bFRnjDpnDZKZUUCeGQcJJI03I9niaUtY+kwJQ==", - "requires": { - "@swc/helpers": "^0.5.0" - } - }, - "@react-stately/virtualizer": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@react-stately/virtualizer/-/virtualizer-3.6.1.tgz", - "integrity": "sha512-Gq5gQ1YPgTakPCkWnmp9P6p5uGoVS+phm6Ie34lmZQ+E62lrkHK0XG0bkOuvMSdWwzql0oLg03E/SMOahI9vNA==", - "requires": { - "@react-aria/utils": "^3.19.0", - "@react-types/shared": "^3.19.0", - "@swc/helpers": "^0.5.0" - } - }, - "@react-types/breadcrumbs": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@react-types/breadcrumbs/-/breadcrumbs-3.6.1.tgz", - "integrity": "sha512-O4Jeh2DdYqqbG9tFDkcMEBZ+MId/vouy0gSuRf7Q9HWnT3E68GE1LM8yj2z58XIYOecDeWhlbzvPMfXztouYzg==", - "requires": { - "@react-types/link": "^3.4.4", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/button": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.7.4.tgz", - "integrity": "sha512-y1JOnJ3pqg2ezZz/fdwMMToPj+8fgj/He7z1NRWtIy1/I7HP+ilSK6S/MLO2jRsM2QfCq8KSw5MQEZBPiPWsjw==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/calendar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@react-types/calendar/-/calendar-3.3.1.tgz", - "integrity": "sha512-9pn4M8GK6dCMyCN5oilsGYnphe+tSU5zfHucdiVCOyss3HrOBVxLQnr9eZfDxN/nEqz7fCu8QPIIMFFgOi/YCA==", - "requires": { - "@internationalized/date": "^3.4.0", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/checkbox": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.5.0.tgz", - "integrity": "sha512-fCisTdqFKkz7FvxNoexXIiVsTBt0ZwIyeIZz/S41M6hzIZM38nKbh6yS/lveQ+/877Dn7+ngvbpJ8QYnXYVrIQ==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/combobox": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@react-types/combobox/-/combobox-3.7.0.tgz", - "integrity": "sha512-w9LSAq/DR1mM8lwHk7cGbIGGm75yg+A2pdnLaViFNEVqv7nBUuhHUBzIihnCQ2k/4piWxa5Ih5gcggDFv2yE4g==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/datepicker": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.5.0.tgz", - "integrity": "sha512-PQSfLR0CgSaD3T70enZQZH/L4s1+KPAJLRxwtyy8toDekKfrkoIjrnUOP91e0rkajeHCSG9T1kL6w8FtaUvbmg==", - "requires": { - "@internationalized/date": "^3.4.0", - "@react-types/calendar": "^3.3.1", - "@react-types/overlays": "^3.8.1", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/dialog": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@react-types/dialog/-/dialog-3.5.4.tgz", - "integrity": "sha512-WCEkUf93XauGaPaF1efTJ8u04Z5iUgmmzRbFnGLrske7rQJYfryP3+26zCxtKKlOTgeFORq5AHeH6vqaMKOhhg==", - "requires": { - "@react-types/overlays": "^3.8.1", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/grid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.2.0.tgz", - "integrity": "sha512-ZIzFDbuBgqaPNvZ18/fOdm9Ol0m5rFPlhSxQfyAgUOXFaQhl/1+BsG8FsHla/Y6tTmxDt5cVrF5PX2CWzZmtOw==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/label": { - "version": "3.7.5", - "resolved": "https://registry.npmjs.org/@react-types/label/-/label-3.7.5.tgz", - "integrity": "sha512-iNO5T1UYK7FPF23cwRLQJ4zth2rqoJWbz27Wikwt8Cw8VbVVzfLBPUBZoUyeBVZ0/zzTvEgZUW75OrmKb4gqhw==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/link": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/@react-types/link/-/link-3.4.4.tgz", - "integrity": "sha512-/FnKf7W6nCNZ2E96Yo1gaX63eSxERmtovQbkRRdsgPLfgRcqzQIVzQtNJThIbVNncOnAw3qvIyhrS0weUTFacQ==", - "requires": { - "@react-aria/interactions": "^3.17.0", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/listbox": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.4.3.tgz", - "integrity": "sha512-AHOnx5z+q/uIsBnGqrNJ25OSTbOe2/kWXWUcPDdfZ29OBqoDZu86psAOA97glYod97w/KzU5xq8EaxDrWupKuQ==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/menu": { - "version": "3.9.3", - "resolved": "https://registry.npmjs.org/@react-types/menu/-/menu-3.9.3.tgz", - "integrity": "sha512-0dgIIM9z3hzjFltT+1/L8Hj3oDEcdYkexQhaA+jv6xBHUI5Bqs4SaJAeSGrGz5u6tsrHBPEgf/TLk9Dg9c7XMA==", - "requires": { - "@react-types/overlays": "^3.8.1", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/meter": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@react-types/meter/-/meter-3.3.3.tgz", - "integrity": "sha512-cuNMHAG9SF/QjM0bjukC1ezjWxp0KRInmEQN3kQuQt+eAVC2GLCJjDRfRSLgf5jld8S68xOVw8fEAWY+VK/NHg==", - "requires": { - "@react-types/progress": "^3.4.2", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/numberfield": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@react-types/numberfield/-/numberfield-3.5.0.tgz", - "integrity": "sha512-uKN6uJCJICIvngk3d2AzD/XU+LZHSriALpsM58l6Zy7xmVu3Wdb11WeWL9z/cwJ+KAdt4tcD+rCE/Y2rcfjWDA==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/overlays": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.1.tgz", - "integrity": "sha512-aDI/K3E2XACkey8SCBmAerLhYSUFa8g8tML4SoQbfEJPRj+jJztbHbg9F7b3HKDUk4ZOjcUdQRfz1nFHORdbtQ==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/progress": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/@react-types/progress/-/progress-3.4.2.tgz", - "integrity": "sha512-UvnBt1OtjgQgOM3556KpuAXSdvSIVGSeD4+otTfkl05ieTcy6Lx7ef3TFI2KfQP45a9JeRBstTNpThBmuRe03A==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/radio": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@react-types/radio/-/radio-3.5.0.tgz", - "integrity": "sha512-jpAG03eYxLvD1+zLoHXVUR7BCXfzbaQnOv5vu2R4EXhBA7t1/HBOAY/WHbUEgrnyDYa2na7dr/RbY81H9JqR0g==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/searchfield": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@react-types/searchfield/-/searchfield-3.4.3.tgz", - "integrity": "sha512-gnOKM2r5GuRspe+8gmKZxuiPYUlzxge9r1SADWgCCrF9091Aq6uEL+oXT4nAIMlRCwxxKXjAa8KlGeqz3dEgxw==", - "requires": { - "@react-types/shared": "^3.19.0", - "@react-types/textfield": "^3.7.3" - } - }, - "@react-types/select": { - "version": "3.8.2", - "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.8.2.tgz", - "integrity": "sha512-m11J/xBR8yFwPLuueoFHzr4DiLyY7nKLCbZCz1W2lwIyd8Tl2iJwcLcuJiyUTJwdSTcCDgvbkY4vdTfLOIktYQ==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/shared": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.19.0.tgz", - "integrity": "sha512-h852l8bWhqUxbXIG8vH3ab7gE19nnP3U1kuWf6SNSMvgmqjiRN9jXKPIFxF/PbfdvnXXm0yZSgSMWfUCARF0Cg==" - }, - "@react-types/slider": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@react-types/slider/-/slider-3.6.0.tgz", - "integrity": "sha512-X9h7g1eoYx5+Xts0qCfLd7Qje8NknK3AWq9BZKul2KSZ/5VJeFhIsRjN5MzaUNngO1aYOvSPlPn1oaAWx/ZXHw==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/switch": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.4.0.tgz", - "integrity": "sha512-vUA4Etm7ZiThYN3IotPXl99gHYZNJlc/f9o/SgAUSxtk5pBv5unOSmXLdrvk01Kd6TJ/MjL42IxRShygyr8mTQ==", - "requires": { - "@react-types/checkbox": "^3.5.0", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/table": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@react-types/table/-/table-3.8.0.tgz", - "integrity": "sha512-/7IBG4ZlJHvEPQwND/q6ZFzfXq0Bc1ohaocDFzEOeNtVUrgQ2rFS64EY2p8G7BL9XDJFTY2R5dLYqjyGFojUvQ==", - "requires": { - "@react-types/grid": "^3.2.0", - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/tabs": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@react-types/tabs/-/tabs-3.3.1.tgz", - "integrity": "sha512-vPxSbLCU7RT+Rupvu/1uOAesxlR/53GD5ZbgLuQRr/oEZRbsjY8Cs3CE3LGv49VdvBWivXUvHiF5wSE7CdWs1w==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/textfield": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@react-types/textfield/-/textfield-3.7.3.tgz", - "integrity": "sha512-M2u9NK3iqQEmTp4G1Dk36pCleyH/w1n+N52u5n0fRlxvucY/Od8W1zvk3w9uqJLFHSlzleHsfSvkaETDJn7FYw==", - "requires": { - "@react-types/shared": "^3.19.0" - } - }, - "@react-types/tooltip": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@react-types/tooltip/-/tooltip-3.4.3.tgz", - "integrity": "sha512-ne1SVhgofHRZNhoQM4iMCSjCstpdPBpM81B4KDJ7XmWax0+dP4qmdxMc7qvEm7GjuZLfYx5f44fWytKm1BkZmg==", - "requires": { - "@react-types/overlays": "^3.8.1", - "@react-types/shared": "^3.19.0" - } - }, - "@rocket.chat/css-in-js": { - "version": "0.31.23-dev.165", - "requires": { - "@emotion/hash": "^0.9.0", - "@rocket.chat/css-supports": "~0.31.23-dev.165", - "@rocket.chat/memo": "~0.31.23-dev.165", - "@rocket.chat/stylis-logical-props-middleware": "~0.31.23-dev.165", - "stylis": "~4.1.3" - } - }, - "@rocket.chat/css-supports": { - "version": "0.31.23", - "resolved": "https://registry.npmjs.org/@rocket.chat/css-supports/-/css-supports-0.31.23.tgz", - "integrity": "sha512-UEHtO3ClWobFJx3SJn+zIfKK2nb0KXO1O7J085eltOHoJcTDQDgbcemVfuGKLeID1R6bOKA9QvxglaG426q2AQ==", - "requires": { - "@rocket.chat/memo": "^0.31.23" - } - }, - "@rocket.chat/eslint-config": { - "version": "0.4.0", - "dev": true, - "requires": { - "eslint-plugin-import": "^2.17.2" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - }, - "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - } - }, - "eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "requires": { - "debug": "^3.2.7" - } - }, - "object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - } - } - }, - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@rocket.chat/eslint-config-alt": { - "version": "0.32.0-dev.143", - "dev": true, - "requires": { - "@typescript-eslint/eslint-plugin": "~5.58.0", - "@typescript-eslint/parser": "~5.58.0", - "eslint-config-prettier": "~8.8.0", - "eslint-import-resolver-typescript": "~3.5.5", - "eslint-plugin-import": "~2.26.0", - "eslint-plugin-prettier": "~4.2.1", - "eslint-plugin-react": "~7.32.2", - "eslint-plugin-react-hooks": "~4.6.0" - }, - "dependencies": { - "@typescript-eslint/parser": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.58.0.tgz", - "integrity": "sha512-ixaM3gRtlfrKzP8N6lRhBbjTow1t6ztfBvQNGuRM8qH1bjFFXIJ35XY+FC0RRBKn3C6cT+7VW1y8tNm7DwPHDQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.58.0", - "@typescript-eslint/types": "5.58.0", - "@typescript-eslint/typescript-estree": "5.58.0", - "debug": "^4.3.4" - } - } - } - }, - "@rocket.chat/fuselage": { - "version": "0.32.0-dev.391", - "requires": { - "@rocket.chat/css-in-js": "~0.31.23-dev.165", - "@rocket.chat/css-supports": "~0.31.23-dev.165", - "@rocket.chat/fuselage-tokens": "~0.32.0-dev.341", - "@rocket.chat/memo": "~0.31.23-dev.165", - "@rocket.chat/styled": "~0.31.23-dev.165", - "invariant": "^2.2.4", - "react-aria": "~3.23.1", - "react-keyed-flatten-children": "^1.3.0", - "react-stately": "~3.17.0" - }, - "dependencies": { - "@rocket.chat/css-in-js": { - "version": "0.31.23", - "resolved": "https://registry.npmjs.org/@rocket.chat/css-in-js/-/css-in-js-0.31.23.tgz", - "integrity": "sha512-SKwJPBVAn/zkly29TghuYKxehkVEG/20jIaOUH4sgwBSztlA655zkjegLTTNhN93+nDecxNeHcZQaqldbQ/Q9A==", - "requires": { - "@emotion/hash": "^0.9.0", - "@rocket.chat/css-supports": "^0.31.23", - "@rocket.chat/memo": "^0.31.23", - "@rocket.chat/stylis-logical-props-middleware": "^0.31.23", - "stylis": "~4.1.3" - } - }, - "react-keyed-flatten-children": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/react-keyed-flatten-children/-/react-keyed-flatten-children-1.3.0.tgz", - "integrity": "sha512-qB7A6n+NHU0x88qTZGAJw6dsqwI941jcRPBB640c/CyWqjPQQ+YUmXOuzPziuHb7iqplM3xksWAbGYwkQT0tXA==", - "requires": { - "react-is": "^16.8.6" - } - } - } - }, - "@rocket.chat/fuselage-hooks": { - "version": "0.32.0-dev.304", - "requires": { - "use-sync-external-store": "~1.2.0" - } - }, - "@rocket.chat/fuselage-polyfills": { - "version": "0.31.23-dev.165", - "requires": { - "@juggle/resize-observer": "^3.4.0", - "clipboard-polyfill": "^3.0.3", - "element-closest-polyfill": "^1.0.6", - "focus-visible": "^5.2.0", - "focus-within-polyfill": "^5.2.1", - "new-event-polyfill": "^1.0.1" - } - }, - "@rocket.chat/fuselage-tokens": { - "version": "0.32.0-dev.341", - "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-tokens/-/fuselage-tokens-0.32.0-dev.341.tgz", - "integrity": "sha512-U0NTrW42liOrHDIzzAT00LwyZVAr4joQCt/oK3x3IPnyW7wvp0F9Fw98knLJ+pPUHZ84+BRAryq0PgtgMrAWeA==" - }, - "@rocket.chat/icons": { - "version": "0.32.0-dev.373" - }, - "@rocket.chat/memo": { - "version": "0.31.23", - "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.31.23.tgz", - "integrity": "sha512-XnzJNldmOYJrcTKtqNfuSdTFzECYxA/1oGLfWJKY+p7XOxM7f4BaE11P81ha0fpCTGnxn9BPAFklTXCEbqmTKQ==" - }, - "@rocket.chat/prettier-config": { - "version": "0.31.23-dev.165", - "dev": true - }, - "@rocket.chat/styled": { - "version": "0.31.23", - "resolved": "https://registry.npmjs.org/@rocket.chat/styled/-/styled-0.31.23.tgz", - "integrity": "sha512-6yKvqDjt1m5aYm5HWgv4Nttk0KIvjfCaBEDpd3hMCw0W8sGVVabMPcAQI1S+JKRbZPvhKlq1gKdwMJM2x3wblQ==", - "requires": { - "@rocket.chat/css-in-js": "^0.31.23" - }, - "dependencies": { - "@rocket.chat/css-in-js": { - "version": "0.31.25", - "resolved": "https://registry.npmjs.org/@rocket.chat/css-in-js/-/css-in-js-0.31.25.tgz", - "integrity": "sha512-H5echHYGHGYFBIdxRihM+6fIm19emdYpHn5e0cLmxCTDooEK0NRmjz8VIqDh4oxFdJlFxMQ5dgsOM8viNnnLwA==", - "requires": { - "@emotion/hash": "^0.9.0", - "@rocket.chat/css-supports": "^0.31.25", - "@rocket.chat/memo": "^0.31.25", - "@rocket.chat/stylis-logical-props-middleware": "^0.31.25", - "stylis": "~4.1.3" - } - }, - "@rocket.chat/css-supports": { - "version": "0.31.25", - "resolved": "https://registry.npmjs.org/@rocket.chat/css-supports/-/css-supports-0.31.25.tgz", - "integrity": "sha512-hr3qLNgxuRVEUv02rze9IaM0QG9FHG8l2u+lLcGelfdU7V/AMBmDnFznJgDjE8QJYwdDL9EbUjabxLRRd09Whg==", - "requires": { - "@rocket.chat/memo": "^0.31.25" - } - }, - "@rocket.chat/memo": { - "version": "0.31.25", - "resolved": "https://registry.npmjs.org/@rocket.chat/memo/-/memo-0.31.25.tgz", - "integrity": "sha512-d5pbOMzFCOhW/+ju5lXc+2fnDrtk7v5asic8h8aAyGIoycWMy/YmDVmF7TK9zMs5iJ+cPf86KWkRI+ANl02KfA==" - }, - "@rocket.chat/stylis-logical-props-middleware": { - "version": "0.31.25", - "resolved": "https://registry.npmjs.org/@rocket.chat/stylis-logical-props-middleware/-/stylis-logical-props-middleware-0.31.25.tgz", - "integrity": "sha512-sgqpKA4nuqD8fZsabebIU4G0+0qiduaG4TLKbgKHQe0FpuYQwuJ+LnmAqJuM9odtnN+CH6Q7ghzkTxiBiS5eIw==", - "requires": { - "@rocket.chat/css-supports": "^0.31.25" - } - } - } - }, - "@rocket.chat/stylis-logical-props-middleware": { - "version": "0.31.23", - "resolved": "https://registry.npmjs.org/@rocket.chat/stylis-logical-props-middleware/-/stylis-logical-props-middleware-0.31.23.tgz", - "integrity": "sha512-2a02RgsZe17P/FHGpb9Z7ixRT9a3CKQ/FUYTHjqFB5V7BiOLDycvGu5E2KykRbbLo2s0HiBYJICzn3HqH86OnA==", - "requires": { - "@rocket.chat/css-supports": "^0.31.23" - } - }, - "@rollup/plugin-babel": { - "version": "5.3.1", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@rollup/pluginutils": "^3.1.0" - } - }, - "@rollup/plugin-commonjs": { - "version": "21.1.0", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "commondir": "^1.0.1", - "estree-walker": "^2.0.1", - "glob": "^7.1.6", - "is-reference": "^1.2.1", - "magic-string": "^0.25.7", - "resolve": "^1.17.0" - } - }, - "@rollup/plugin-json": { - "version": "4.1.0", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.0.8" - } - }, - "@rollup/plugin-node-resolve": { - "version": "13.3.0", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.1.0", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - } - }, - "@rollup/plugin-replace": { - "version": "3.1.0", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "magic-string": "^0.25.7" - } - }, - "@rollup/plugin-typescript": { - "version": "8.5.0", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.1.0", - "resolve": "^1.17.0" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - }, - "dependencies": { - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - } - } - }, - "@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@smithy/abort-controller": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.5.tgz", - "integrity": "sha512-byVZ2KWLMPYAZGKjRpniAzLcygJO4ruClZKdJTuB0eCB76ONFTdptBHlviHpAZXknRz7skYWPfcgO9v30A1SyA==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/config-resolver": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.5.tgz", - "integrity": "sha512-n0c2AXz+kjALY2FQr7Zy9zhYigXzboIh1AuUUVCqFBKFtdEvTwnwPXrTDoEehLiRTUHNL+4yzZ3s+D0kKYSLSg==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "@smithy/util-config-provider": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/credential-provider-imds": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.5.tgz", - "integrity": "sha512-KFcf/e0meFkQNyteJ65f1G19sgUEY1e5zL7hyAEUPz2SEfBmC9B37WyRq87G3MEEsvmAWwCRu7nFFYUKtR3svQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/node-config-provider": "^2.0.5", - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/eventstream-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.5.tgz", - "integrity": "sha512-iqR6OuOV3zbQK8uVs9o+9AxhVk8kW9NAxA71nugwUB+kTY9C35pUd0A5/m4PRT0Y0oIW7W4kgnSR3fdYXQjECw==", - "dev": true, - "optional": true, - "requires": { - "@aws-crypto/crc32": "3.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-hex-encoding": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/fetch-http-handler": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.0.5.tgz", - "integrity": "sha512-EzFoMowdBNy1VqtvkiXgPFEdosIAt4/4bgZ8uiDiUyfhmNXq/3bV+CagPFFBsgFOR/X2XK4zFZHRsoa7PNHVVg==", - "dev": true, - "optional": true, - "requires": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/querystring-builder": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/util-base64": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/hash-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.5.tgz", - "integrity": "sha512-mk551hIywBITT+kXruRNXk7f8Fy7DTzBjZJSr/V6nolYKmUHIG3w5QU6nO9qPYEQGKc/yEPtkpdS28ndeG93lA==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/invalid-dependency": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.5.tgz", - "integrity": "sha512-0wEi+JT0hM+UUwrJVYbqjuGFhy5agY/zXyiN7BNAJ1XoCDjU5uaNSj8ekPWsXd/d4yM6NSe8UbPd8cOc1+3oBQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/is-array-buffer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", - "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/middleware-content-length": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.5.tgz", - "integrity": "sha512-E7VwV5H02fgZIUGRli4GevBCAPvkyEI/fgl9SU47nPPi3DAAX3nEtUb8xfGbXjOcJ5BdSUoWWZn42tEd/blOqA==", - "dev": true, - "optional": true, - "requires": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/middleware-endpoint": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.5.tgz", - "integrity": "sha512-tyzDuoNTbsMQCq5Xkc4QOt6e2GACUllQIV8SQ5fc59FtOIV9/vbf58/GxVjZm2o8+MMbdDBANjTDZe/ijZKfyA==", - "dev": true, - "optional": true, - "requires": { - "@smithy/middleware-serde": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/url-parser": "^2.0.5", - "@smithy/util-middleware": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/middleware-retry": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.5.tgz", - "integrity": "sha512-ulIfbFyzQTVnJbLjUl1CTSi0etg6tej/ekwaLp0Gn8ybUkDkKYa+uB6CF/m2J5B6meRwyJlsryR+DjaOVyiicg==", - "dev": true, - "optional": true, - "requires": { - "@smithy/protocol-http": "^2.0.5", - "@smithy/service-error-classification": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-retry": "^2.0.0", - "tslib": "^2.5.0", - "uuid": "^8.3.2" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/middleware-serde": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.5.tgz", - "integrity": "sha512-in0AA5sous74dOfTGU9rMJBXJ0bDVNxwdXtEt5lh3FVd2sEyjhI+rqpLLRF1E4ixbw3RSEf80hfRpcPdjg4vvQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/middleware-stack": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", - "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/node-config-provider": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.5.tgz", - "integrity": "sha512-LRtjV9WkhONe2lVy+ipB/l1GX60ybzBmFyeRUoLUXWKdnZ3o81jsnbKzMK8hKq8eFSWPk+Lmyx6ZzCQabGeLxg==", - "dev": true, - "optional": true, - "requires": { - "@smithy/property-provider": "^2.0.5", - "@smithy/shared-ini-file-loader": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/node-http-handler": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.0.5.tgz", - "integrity": "sha512-lZm5DZf4b3V0saUw9WTC4/du887P6cy2fUyQgQQKRRV6OseButyD5yTzeMmXE53CaXJBMBsUvvIQ0hRVxIq56w==", - "dev": true, - "optional": true, - "requires": { - "@smithy/abort-controller": "^2.0.5", - "@smithy/protocol-http": "^2.0.5", - "@smithy/querystring-builder": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/property-provider": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.5.tgz", - "integrity": "sha512-cAFSUhX6aiHcmpWfrCLKvwBtgN1F6A0N8qY/8yeSi0LRLmhGqsY1/YTxFE185MCVzYbqBGXVr9TBv4RUcIV4rA==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/protocol-http": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-2.0.5.tgz", - "integrity": "sha512-d2hhHj34mA2V86doiDfrsy2fNTnUOowGaf9hKb0hIPHqvcnShU4/OSc4Uf1FwHkAdYF3cFXTrj5VGUYbEuvMdw==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/querystring-builder": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.5.tgz", - "integrity": "sha512-4DCX9krxLzATj+HdFPC3i8pb7XTAWzzKqSw8aTZMjXjtQY+vhe4azMAqIvbb6g7JKwIkmkRAjK6EXO3YWSnJVQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "@smithy/util-uri-escape": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/querystring-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.5.tgz", - "integrity": "sha512-C2stCULH0r54KBksv3AWcN8CLS3u9+WsEW8nBrvctrJ5rQTNa1waHkffpVaiKvcW2nP0aIMBPCobD/kYf/q9mA==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/service-error-classification": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", - "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", - "dev": true, - "optional": true - }, - "@smithy/shared-ini-file-loader": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.5.tgz", - "integrity": "sha512-Mvtk6FwMtfbKRC4YuSsIqRYp9WTxsSUJVVo2djgyhcacKGMqicHDWSAmgy3sDrKv+G/G6xTZCPwm6pJARtdxVg==", - "dev": true, - "optional": true, - "requires": { - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/signature-v4": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.5.tgz", - "integrity": "sha512-ABIzXmUDXK4n2c9cXjQLELgH2RdtABpYKT+U131e2I6RbCypFZmxIHmIBufJzU2kdMCQ3+thBGDWorAITFW04A==", - "dev": true, - "optional": true, - "requires": { - "@smithy/eventstream-codec": "^2.0.5", - "@smithy/is-array-buffer": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-middleware": "^2.0.0", - "@smithy/util-uri-escape": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/smithy-client": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.0.5.tgz", - "integrity": "sha512-kCTFr8wfOAWKDzGvfBElc6shHigWtHNhMQ1IbosjC4jOlayFyZMSs2PysKB+Ox/dhQ41KqOzgVjgiQ+PyWqHMQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/middleware-stack": "^2.0.0", - "@smithy/types": "^2.2.2", - "@smithy/util-stream": "^2.0.5", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/types": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.2.2.tgz", - "integrity": "sha512-4PS0y1VxDnELGHGgBWlDksB2LJK8TG8lcvlWxIsgR+8vROI7Ms8h1P4FQUx+ftAX2QZv5g1CJCdhdRmQKyonyw==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/url-parser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.5.tgz", - "integrity": "sha512-OdMBvZhpckQSkugCXNJQCvqJ71wE7Ftxce92UOQLQ9pwF6hoS5PLL7wEfpnuEXtStzBqJYkzu1C1ZfjuFGOXAA==", - "dev": true, - "optional": true, - "requires": { - "@smithy/querystring-parser": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-base64": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", - "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", - "dev": true, - "optional": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-body-length-browser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", - "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-body-length-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", - "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-buffer-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", - "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", - "dev": true, - "optional": true, - "requires": { - "@smithy/is-array-buffer": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-config-provider": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", - "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-defaults-mode-browser": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.5.tgz", - "integrity": "sha512-yciP6TPttLsj731aHTvekgyuCGXQrEAJibEwEWAh3kzaDsfGAVCuZSBlyvC2Dl3TZmHKCOQwHV8mIE7KQCTPuQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "bowser": "^2.11.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-defaults-mode-node": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.5.tgz", - "integrity": "sha512-M07t99rWasXt+IaDZDyP3BkcoEm/mgIE1RIMASrE49LKSNxaVN7PVcgGc77+4uu2kzBAyqJKy79pgtezuknyjQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/config-resolver": "^2.0.5", - "@smithy/credential-provider-imds": "^2.0.5", - "@smithy/node-config-provider": "^2.0.5", - "@smithy/property-provider": "^2.0.5", - "@smithy/types": "^2.2.2", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-hex-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", - "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-middleware": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", - "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-retry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", - "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", - "dev": true, - "optional": true, - "requires": { - "@smithy/service-error-classification": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-stream": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.5.tgz", - "integrity": "sha512-ylx27GwI05xLpYQ4hDIfS15vm+wYjNN0Sc2P0FxuzgRe8v0BOLHppGIQ+Bezcynk8C9nUzsUue3TmtRhjut43g==", - "dev": true, - "optional": true, - "requires": { - "@smithy/fetch-http-handler": "^2.0.5", - "@smithy/node-http-handler": "^2.0.5", - "@smithy/types": "^2.2.2", - "@smithy/util-base64": "^2.0.0", - "@smithy/util-buffer-from": "^2.0.0", - "@smithy/util-hex-encoding": "^2.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-uri-escape": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", - "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", - "dev": true, - "optional": true, - "requires": { - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@smithy/util-utf8": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", - "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", - "dev": true, - "optional": true, - "requires": { - "@smithy/util-buffer-from": "^2.0.0", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true, - "optional": true - } - } - }, - "@swc/helpers": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", - "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", - "requires": { - "tslib": "^2.4.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } - } - }, - "@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "dev": true - }, - "@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.0.tgz", - "integrity": "sha512-TBOjqAGf0hmaqRwpii5LLkJLg7c6OMm4nHLmpsUxwk9bBHtoTC6dAHdVWdGv4TBxj2CZOZY8Xfq8WmfoVi7n4Q==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" - } - }, - "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/debug": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.8.tgz", - "integrity": "sha512-/vPO1EPOs306Cvhwv7KfVfYvOJqA/S/AXjaHQiJboCZzcNDb+TIJFN9/2C9DZ//ijSKWioNyUxD792QmDJ+HKQ==", - "dev": true, - "requires": { - "@types/ms": "*" - } - }, - "@types/electron-devtools-installer": { - "version": "2.2.2", - "dev": true - }, - "@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", - "dev": true - }, - "@types/fs-extra": { - "version": "9.0.13", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", - "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", - "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/hoist-non-react-statics": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", - "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", - "requires": { - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "27.5.2", - "dev": true, - "requires": { - "jest-matcher-utils": "^27.0.0", - "pretty-format": "^27.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - } - } - }, - "@types/jquery": { - "version": "3.5.16", - "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.16.tgz", - "integrity": "sha512-bsI7y4ZgeMkmpG9OM710RRzDFp+w4P1RGiIt30C1mSBT+ExCleeh4HObwgArnDFELmRrOpXgSYN9VF1hj+f1lw==", - "dev": true, - "requires": { - "@types/sizzle": "*" - } - }, - "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "@types/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-drE6uz7QBKq1fYqqoFKTDRdFCPHd5TCub75BM+D+cMx7NU9hUz7SESLfC2fSCXVFMO5Yj8sOWHuGqPgjc+fz0Q==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/meteor": { - "version": "2.9.2", - "dev": true, - "requires": { - "@types/connect": "*", - "@types/jquery": "*", - "@types/nodemailer": "*", - "@types/react": "*", - "@types/underscore": "*", - "mongodb": "^4.3.1" - }, - "dependencies": { - "@types/react": { - "version": "18.2.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.7.tgz", - "integrity": "sha512-ojrXpSH2XFCmHm7Jy3q44nXDyN54+EYKP2lBhJ2bqfyPj6cIUW/FZW/Csdia34NQgq7KYcAlHi5184m4X88+yw==", - "dev": true, - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - }, - "dependencies": { - "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", - "dev": true - }, - "@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", - "dev": true - } - } - } - } - }, - "@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "@types/ms": { - "version": "0.7.31", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", - "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==", - "dev": true - }, - "@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "dev": true - }, - "@types/node-fetch": { - "version": "3.0.3", - "dev": true, - "requires": { - "node-fetch": "*" - } - }, - "@types/nodemailer": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.8.tgz", - "integrity": "sha512-oVsJSCkqViCn8/pEu2hfjwVO+Gb3e+eTWjg3PcjeFKRItfKpKwHphQqbYmPQrlMk+op7pNNWPbsJIEthpFN/OQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" - }, - "@types/plist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.2.tgz", - "integrity": "sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==", - "dev": true, - "optional": true, - "requires": { - "@types/node": "*", - "xmlbuilder": ">=11.0.1" - } - }, - "@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, - "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" - }, - "@types/react": { - "version": "17.0.60", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.60.tgz", - "integrity": "sha512-pCH7bqWIfzHs3D+PDs3O/COCQJka+Kcw3RnO9rFA2zalqoXg7cNjJDh6mZ7oRtY1wmY4LVwDdAbA1F7Z8tv3BQ==", - "requires": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-dom": { - "version": "17.0.20", - "dev": true, - "requires": { - "@types/react": "^17" - } - }, - "@types/react-redux": { - "version": "7.1.25", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz", - "integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==", - "requires": { - "@types/hoist-non-react-statics": "^3.3.0", - "@types/react": "*", - "hoist-non-react-statics": "^3.3.0", - "redux": "^4.0.0" - }, - "dependencies": { - "redux": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", - "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", - "requires": { - "@babel/runtime": "^7.9.2" - } - } - } - }, - "@types/resize-observer-browser": { - "version": "0.1.7", - "dev": true - }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/rimraf": { - "version": "3.0.2", - "dev": true, - "requires": { - "@types/glob": "*", - "@types/node": "*" - }, - "dependencies": { - "@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "dev": true, - "requires": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, - "@types/node": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", - "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", - "dev": true - } - } - }, - "@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, - "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==" - }, - "@types/sizzle": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", - "dev": true - }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "@types/underscore": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.5.tgz", - "integrity": "sha512-b8e//LrIlhoXaaBcMC0J/s2/lIF9y5VJYKqbW4nA+tW/nqqDk1Dacd1ULLT7zgGsKs7PGbSnqCPzqEniZ0RxYg==", - "dev": true - }, - "@types/verror": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.6.tgz", - "integrity": "sha512-NNm+gdePAX1VGvPcGZCDKQZKYSiAWigKhKaz5KF94hG6f2s8de9Ow5+7AbXoeKxL8gavZfk4UquSAygOF2duEQ==", - "dev": true, - "optional": true - }, - "@types/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==", - "dev": true - }, - "@types/whatwg-url": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", - "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", - "dev": true, - "requires": { - "@types/node": "*", - "@types/webidl-conversions": "*" - } - }, - "@types/yargs": { - "version": "15.0.15", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.15.tgz", - "integrity": "sha512-IziEYMU9XoVj8hWg7k+UJrXALkGFjWJhn5QFEv9q4p+v40oZhSuC135M38st8XPjICL7Ey4TV64ferBGUoJhBg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@types/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", - "dev": true, - "optional": true, - "requires": { - "@types/node": "*" - } - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.58.0.tgz", - "integrity": "sha512-vxHvLhH0qgBd3/tW6/VccptSfc8FxPQIkmNTVLWcCOVqSBvqpnKkBTYrhcGlXfSnd78azwe+PsjYFj0X34/njA==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.58.0", - "@typescript-eslint/type-utils": "5.58.0", - "@typescript-eslint/utils": "5.58.0", - "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/parser": { - "version": "5.59.8", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.59.8", - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/typescript-estree": "5.59.8", - "debug": "^4.3.4" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.8.tgz", - "integrity": "sha512-/w08ndCYI8gxGf+9zKf1vtx/16y8MHrZs5/tnjHhMLNSixuNcJavSX4wAiPf4aS5x41Es9YPCn44MIe4cxIlig==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/visitor-keys": "5.59.8" - } - }, - "@typescript-eslint/types": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.8.tgz", - "integrity": "sha512-+uWuOhBTj/L6awoWIg0BlWy0u9TyFpCHrAuQ5bNfxDaZ1Ppb3mx6tUigc74LHcbHpOHuOTOJrBoAnhdHdaea1w==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.8.tgz", - "integrity": "sha512-Jy/lPSDJGNow14vYu6IrW790p7HIf/SOV1Bb6lZ7NUkLc2iB2Z9elESmsaUtLw8kVqogSbtLH9tut5GCX1RLDg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.8", - "@typescript-eslint/visitor-keys": "5.59.8", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.59.8", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.8.tgz", - "integrity": "sha512-pJhi2ms0x0xgloT7xYabil3SGGlojNNKjK/q6dB3Ey0uJLMjK2UDGJvHieiyJVW/7C3KI+Z4Q3pEHkm4ejA+xQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.59.8", - "eslint-visitor-keys": "^3.3.0" - } - } - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.58.0.tgz", - "integrity": "sha512-b+w8ypN5CFvrXWQb9Ow9T4/6LC2MikNf1viLkYTiTbkQl46CnR69w7lajz1icW0TBsYmlpg+mRzFJ4LEJ8X9NA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.58.0", - "@typescript-eslint/visitor-keys": "5.58.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.58.0.tgz", - "integrity": "sha512-FF5vP/SKAFJ+LmR9PENql7fQVVgGDOS+dq3j+cKl9iW/9VuZC/8CFmzIP0DLKXfWKpRHawJiG70rVH+xZZbp8w==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.58.0", - "@typescript-eslint/utils": "5.58.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.58.0.tgz", - "integrity": "sha512-JYV4eITHPzVQMnHZcYJXl2ZloC7thuUHrcUmxtzvItyKPvQ50kb9QXBkgNAt90OYMqwaodQh2kHutWZl1fc+1g==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.58.0.tgz", - "integrity": "sha512-cRACvGTodA+UxnYM2uwA2KCwRL7VAzo45syNysqlMyNyjw0Z35Icc9ihPJZjIYuA5bXJYiJ2YGUB59BqlOZT1Q==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.58.0", - "@typescript-eslint/visitor-keys": "5.58.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/utils": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.58.0.tgz", - "integrity": "sha512-gAmLOTFXMXOC+zP1fsqm3VceKSBQJNzV385Ok3+yzlavNHZoedajjS4UyS21gabJYcobuigQPs/z71A9MdJFqQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.58.0", - "@typescript-eslint/types": "5.58.0", - "@typescript-eslint/typescript-estree": "5.58.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.58.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.58.0.tgz", - "integrity": "sha512-/fBraTlPj0jwdyTwLyrRTxv/3lnU2H96pNTVM6z3esTWLtA5MZ9ghSMJ7Rb+TtUAdtEw9EyJzJ0EydIMKxQ9gA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.58.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@xmldom/xmldom": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.7.tgz", - "integrity": "sha512-sI1Ly2cODlWStkINzqGrZ8K6n+MTSbAeQnAipGyL+KZCXuHaRlj2gyyy8B/9MvsFFqN7XHryQnB2QwhzvJXovg==" - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "dev": true, - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, - "abort-controller": { - "version": "3.0.0", - "requires": { - "event-target-shim": "^5.0.0" - } - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-globals": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", - "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", - "dev": true, - "requires": { - "acorn": "^6.0.1", - "acorn-walk": "^6.0.1" - }, - "dependencies": { - "acorn": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", - "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", - "dev": true - }, - "acorn-walk": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", - "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", - "dev": true - } - } - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true - }, - "acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", - "dev": true - }, - "add-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", - "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "requires": { - "ajv": "^8.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - } - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true - }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "any-base": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/any-base/-/any-base-1.1.0.tgz", - "integrity": "sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==", - "dev": true - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "app-builder-bin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", - "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", - "dev": true - }, - "app-builder-lib": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-23.6.0.tgz", - "integrity": "sha512-dQYDuqm/rmy8GSCE6Xl/3ShJg6Ab4bZJMT8KaTKGzT436gl1DN4REP3FCWfXoh75qGTJ+u+WsdnnpO9Jl8nyMA==", - "dev": true, - "requires": { - "7zip-bin": "~5.1.1", - "@develar/schema-utils": "~2.6.5", - "@electron/universal": "1.2.1", - "@malept/flatpak-bundler": "^0.4.0", - "async-exit-hook": "^2.0.1", - "bluebird-lst": "^1.0.9", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chromium-pickle-js": "^0.2.0", - "debug": "^4.3.4", - "ejs": "^3.1.7", - "electron-osx-sign": "^0.6.0", - "electron-publish": "23.6.0", - "form-data": "^4.0.0", - "fs-extra": "^10.1.0", - "hosted-git-info": "^4.1.0", - "is-ci": "^3.0.0", - "isbinaryfile": "^4.0.10", - "js-yaml": "^4.1.0", - "lazy-val": "^1.0.5", - "minimatch": "^3.1.2", - "read-config-file": "6.2.0", - "sanitize-filename": "^1.6.3", - "semver": "^7.3.7", - "tar": "^6.1.11", - "temp-file": "^3.4.0" - }, - "dependencies": { - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "requires": { - "ci-info": "^3.2.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "are-we-there-yet": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", - "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", - "dev": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "dev": true - }, - "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "array-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==", - "dev": true - }, - "array-flatten": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-3.0.0.tgz", - "integrity": "sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==", - "dev": true - }, - "array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true - }, - "array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "dev": true - }, - "array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" - } - }, - "arraybuffer.prototype.slice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz", - "integrity": "sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - } - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true - }, - "asar": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", - "integrity": "sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "chromium-pickle-js": "^0.2.0", - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "dependencies": { - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true - } - } - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "dev": true - }, - "ast-types": { - "version": "0.11.7", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.7.tgz", - "integrity": "sha512-2mP3TwtkY/aTv5X3ZsMpNAbOnyoC/aMJwJSoaELPkHId0nSQgFcnU4dRW3isxiz7+zBexk0ym3WNVjMiQBnJSw==", - "dev": true - }, - "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 - }, - "async": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true - }, - "async-exit-hook": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", - "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", - "dev": true - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "atomically": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", - "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==" - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" - }, - "aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" - }, - "axios": { - "version": "1.4.0", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "babel-core": { - "version": "7.0.0-bridge.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", - "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", - "dev": true - }, - "babel-jest": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-25.5.1.tgz", - "integrity": "sha512-9dA9+GmMjIzgPnYtkhBg73gOo/RHqPmLruP3BaGL4KEX3Dwz6pI8auSN8G8+iuEG90+GSswyKvslN+JYSaacaQ==", - "dev": true, - "requires": { - "@jest/transform": "^25.5.1", - "@jest/types": "^25.5.0", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/transform": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.5.1.tgz", - "integrity": "sha512-Y8CEoVwXb4QwA6Y/9uDkn0Xfz0finGkieuV0xkdF9UtZGJeLukD5nLkaVrVsODB1ojRWlaoD0AJZpVHCSnJEvg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^25.5.0", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^3.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^25.5.1", - "jest-regex-util": "^25.2.6", - "jest-util": "^25.5.0", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "realpath-native": "^2.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "jest-haste-map": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.5.1.tgz", - "integrity": "sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "@types/graceful-fs": "^4.1.2", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-serializer": "^25.5.0", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" - } - }, - "jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true - }, - "jest-serializer": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.5.0.tgz", - "integrity": "sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "jest-worker": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", - "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - } - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.5.0.tgz", - "integrity": "sha512-u+/W+WAjMlvoocYGTwthAiQSxDcJAyHpQ6oWlHdFZaaN+Rlk8Q7iiwDPg2lN/FyJtAYnKjFxbn7xus4HCFkg5g==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-plugin-macros": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", - "requires": { - "@babel/runtime": "^7.12.5", - "cosmiconfig": "^7.0.0", - "resolve": "^1.19.0" - } - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz", - "integrity": "sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.4.2", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz", - "integrity": "sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.2", - "core-js-compat": "^3.31.0" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz", - "integrity": "sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA==", - "dev": true, - "requires": { - "@babel/helper-define-polyfill-provider": "^0.4.2" - } - }, - "babel-preset-current-node-syntax": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.4.tgz", - "integrity": "sha512-5/INNCYhUGqw7VbVjT/hb3ucjgkVHKXY7lX3ZjlN4gm565VyFmJUrJ/h+h16ECVB38R/9SF6aACydpKMLZ/c9w==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-25.5.0.tgz", - "integrity": "sha512-8ZczygctQkBU+63DtSOKGh7tFL0CeCuz+1ieud9lJ1WPQ9O6A1a/r+LGn6Y705PA6whHQ3T1XuB/PmpfNYf8Fw==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^25.5.0", - "babel-preset-current-node-syntax": "^0.1.2" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "biskviit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/biskviit/-/biskviit-1.0.1.tgz", - "integrity": "sha512-VGCXdHbdbpEkFgtjkeoBN8vRlbj1ZRX2/mxhE8asCCRalUx2nBzOomLJv8Aw/nRt5+ccDb+tPKidg4XxcfGW4w==", - "requires": { - "psl": "^1.1.7" - } - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" - }, - "bluebird-lst": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", - "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", - "dev": true, - "requires": { - "bluebird": "^3.5.5" - } - }, - "bmp-js": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", - "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", - "dev": true - }, - "boolean": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", - "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", - "dev": true, - "optional": true - }, - "bowser": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", - "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", - "dev": true, - "optional": true - }, - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "requires": { - "big-integer": "^1.6.44" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", - "dev": true - } - } - }, - "browserslist": { - "version": "4.21.10", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", - "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001517", - "electron-to-chromium": "^1.4.477", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.11" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "bson": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", - "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", - "dev": true, - "requires": { - "buffer": "^5.6.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "dev": true, - "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "dev": true - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true - }, - "buffer-equal": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", - "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", - "dev": true - }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" - }, - "buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "dev": true - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "builder-util": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-23.6.0.tgz", - "integrity": "sha512-QiQHweYsh8o+U/KNCZFSvISRnvRctb8m/2rB2I1JdByzvNKxPeFLlHFRPQRXab6aYeXc18j9LpsDLJ3sGQmWTQ==", - "dev": true, - "requires": { - "7zip-bin": "~5.1.1", - "@types/debug": "^4.1.6", - "@types/fs-extra": "^9.0.11", - "app-builder-bin": "4.0.0", - "bluebird-lst": "^1.0.9", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "cross-spawn": "^7.0.3", - "debug": "^4.3.4", - "fs-extra": "^10.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-ci": "^3.0.0", - "js-yaml": "^4.1.0", - "source-map-support": "^0.5.19", - "stat-mode": "^1.0.0", - "temp-file": "^3.4.0" - }, - "dependencies": { - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "requires": { - "ci-info": "^3.2.0" - } - } - } - }, - "builder-util-runtime": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.1.1.tgz", - "integrity": "sha512-azRhYLEoDvRDR8Dhis4JatELC/jUvYjm4cVSj7n9dauGTOM2eeNn9KS0z6YA6oDsjI1xphjNbY6PZZeHPzzqaw==", - "requires": { - "debug": "^4.3.4", - "sax": "^1.2.4" - } - }, - "builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true - }, - "bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "requires": { - "run-applescript": "^5.0.0" - } - }, - "byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", - "dev": true - }, - "cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", - "dev": true, - "requires": { - "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" - } - }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - } - }, - "caniuse-lite": { - "version": "1.0.30001524", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz", - "integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - } - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "chromium-pickle-js": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", - "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cjs-module-lexer": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz", - "integrity": "sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "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, - "optional": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "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, - "optional": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - } - } - }, - "clipboard-polyfill": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/clipboard-polyfill/-/clipboard-polyfill-3.0.3.tgz", - "integrity": "sha512-hts0o01ZkwjA1qHA5gFePzAj/780W7v+eyN3GdaCRyDnapzcPsKRV5aodv77gcr40NDIcyNjNmc+HvfKV+jD0g==" - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - } - }, - "clone-response": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", - "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - } - }, - "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dev": true, - "requires": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "requires": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - }, - "dependencies": { - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - } - } - }, - "compare-version": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", - "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", - "dev": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "conf": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", - "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", - "requires": { - "ajv": "^8.6.3", - "ajv-formats": "^2.1.1", - "atomically": "^1.7.0", - "debounce-fn": "^4.0.0", - "dot-prop": "^6.0.1", - "env-paths": "^2.2.1", - "json-schema-typed": "^7.0.3", - "onetime": "^5.1.2", - "pkg-up": "^3.1.0", - "semver": "^7.3.5" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, - "conventional-changelog": { - "version": "3.1.25", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz", - "integrity": "sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==", - "dev": true, - "requires": { - "conventional-changelog-angular": "^5.0.12", - "conventional-changelog-atom": "^2.0.8", - "conventional-changelog-codemirror": "^2.0.8", - "conventional-changelog-conventionalcommits": "^4.5.0", - "conventional-changelog-core": "^4.2.1", - "conventional-changelog-ember": "^2.0.9", - "conventional-changelog-eslint": "^3.0.9", - "conventional-changelog-express": "^2.0.6", - "conventional-changelog-jquery": "^3.0.11", - "conventional-changelog-jshint": "^2.0.9", - "conventional-changelog-preset-loader": "^2.3.4" - } - }, - "conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - } - }, - "conventional-changelog-atom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", - "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-cli": { - "version": "2.2.2", - "dev": true, - "requires": { - "add-stream": "^1.0.0", - "conventional-changelog": "^3.1.24", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "tempfile": "^3.0.0" - } - }, - "conventional-changelog-codemirror": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", - "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-conventionalcommits": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", - "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" - } - }, - "conventional-changelog-core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz", - "integrity": "sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==", - "dev": true, - "requires": { - "add-stream": "^1.0.0", - "conventional-changelog-writer": "^5.0.0", - "conventional-commits-parser": "^3.2.0", - "dateformat": "^3.0.0", - "get-pkg-repo": "^4.0.0", - "git-raw-commits": "^2.0.8", - "git-remote-origin-url": "^2.0.0", - "git-semver-tags": "^4.1.1", - "lodash": "^4.17.15", - "normalize-package-data": "^3.0.0", - "q": "^1.5.1", - "read-pkg": "^3.0.0", - "read-pkg-up": "^3.0.0", - "through2": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true - }, - "read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "conventional-changelog-ember": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", - "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-eslint": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", - "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-express": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", - "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-jquery": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", - "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", - "dev": true, - "requires": { - "q": "^1.5.1" - } - }, - "conventional-changelog-jshint": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", - "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", - "dev": true, - "requires": { - "compare-func": "^2.0.0", - "q": "^1.5.1" - } - }, - "conventional-changelog-preset-loader": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", - "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", - "dev": true - }, - "conventional-changelog-writer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", - "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", - "dev": true, - "requires": { - "conventional-commits-filter": "^2.0.7", - "dateformat": "^3.0.0", - "handlebars": "^4.7.7", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "semver": "^6.0.0", - "split": "^1.0.0", - "through2": "^4.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "conventional-commits-filter": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", - "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", - "dev": true, - "requires": { - "lodash.ismatch": "^4.4.0", - "modify-values": "^1.0.0" - } - }, - "conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", - "dev": true, - "requires": { - "JSONStream": "^1.0.4", - "is-text-path": "^1.0.1", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - } - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" - }, - "convert-svg-core": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/convert-svg-core/-/convert-svg-core-0.5.0.tgz", - "integrity": "sha512-V30vm5h4sHjmjyAr7o/gYAEmdEIsi0sLKKbDigSxplovCzMHTERXSikIOgA8xSllHh0c4gHYP55Pxwgtu9O+3w==", - "dev": true, - "requires": { - "chalk": "^2.4.1", - "commander": "^2.19.0", - "file-url": "^2.0.2", - "get-stdin": "^6.0.0", - "glob": "^7.1.3", - "lodash.omit": "^4.5.0", - "lodash.pick": "^4.4.0", - "pollock": "^0.2.0", - "puppeteer": "^1.10.0", - "tmp": "0.0.33" - }, - "dependencies": { - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "dev": true, - "requires": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "puppeteer": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-1.20.0.tgz", - "integrity": "sha512-bt48RDBy2eIwZPrkgbcwHtb51mj2nKvHOPMaSH2IsWiv7lOG9k9zhaRzpDZafrk05ajMc3cu+lSQYYOfH2DkVQ==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^2.2.1", - "mime": "^2.0.3", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^6.1.0" - } - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", - "dev": true, - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "convert-svg-to-png": { - "version": "0.5.0", - "dev": true, - "requires": { - "convert-svg-core": "^0.5.0" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", - "dev": true - }, - "core-js-compat": { - "version": "3.32.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.32.1.tgz", - "integrity": "sha512-GSvKDv4wE0bPnQtjklV101juQ85g6H3rm5PDP20mqlS5j0kXF3pP97YvAu5hl+uFHqMictp3b2VxOHljWMAtuA==", - "dev": true, - "requires": { - "browserslist": "^4.21.10" - } - }, - "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==" - }, - "cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - } - }, - "crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "optional": true, - "requires": { - "buffer": "^5.1.0" - } - }, - "create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "cross-fetch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz", - "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==", - "dev": true, - "requires": { - "node-fetch": "2.6.7" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - } - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "dependencies": { - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - } - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" - }, - "dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "dev": true - }, - "data-urls": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", - "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "whatwg-mimetype": "^2.2.0", - "whatwg-url": "^7.0.0" - }, - "dependencies": { - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } - } - }, - "dateformat": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", - "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", - "dev": true - }, - "debounce-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", - "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", - "requires": { - "mimic-fn": "^3.0.0" - }, - "dependencies": { - "mimic-fn": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz", - "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==" - } - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true - } - } - }, - "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", - "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", - "dev": true - }, - "decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, - "requires": { - "mimic-response": "^3.1.0" - }, - "dependencies": { - "mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true - } - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "requires": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "dependencies": { - "execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true - }, - "npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true - } - } - }, - "default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "requires": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - } - }, - "defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true - }, - "define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true - }, - "define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "optional": true - }, - "devtools-protocol": { - "version": "0.0.981744", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.981744.tgz", - "integrity": "sha512-0cuGS8+jhR67Fy7qG3i3Pc7Aw494sb9yG9QgpG97SFVWwolgYjlhJg7n+UaHxOQT30d1TYu/EYe9k01ivLErIg==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", - "dev": true - }, - "dir-compare": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-2.4.0.tgz", - "integrity": "sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA==", - "dev": true, - "requires": { - "buffer-equal": "1.0.0", - "colors": "1.0.3", - "commander": "2.9.0", - "minimatch": "3.0.4" - }, - "dependencies": { - "buffer-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", - "integrity": "sha512-tcBWO2Dl4e7Asr9hTGcpVrCe+F7DubpmqWCTbj4FHLmjqO2hIaC383acQubWtRJhdceqs5uBHs6Es+Sk//RKiQ==", - "dev": true - }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", - "dev": true - }, - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha512-bmkUukX8wAOjHdN26xj5c4ctEV22TQ7dQYhSmuckKhToXrkUn0iIaolHdIxYYqD55nhpSPA9zPQ1yP57GdXP2A==", - "dev": true, - "requires": { - "graceful-readlink": ">= 1.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "dmg-builder": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-23.6.0.tgz", - "integrity": "sha512-jFZvY1JohyHarIAlTbfQOk+HnceGjjAdFjVn3n8xlDWKsYNqbO4muca6qXEZTfGXeQMG7TYim6CeS5XKSfSsGA==", - "dev": true, - "requires": { - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "dmg-license": "^1.0.11", - "fs-extra": "^10.0.0", - "iconv-lite": "^0.6.2", - "js-yaml": "^4.1.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, - "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, - "optional": true, - "requires": { - "@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" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-walk": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", - "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", - "dev": true - }, - "domexception": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", - "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, - "requires": { - "webidl-conversions": "^4.0.2" - } - }, - "dot-prop": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", - "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", - "requires": { - "is-obj": "^2.0.0" - } - }, - "dotenv": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", - "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", - "dev": true - }, - "dotenv-expand": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", - "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", - "dev": true - }, - "easy-stack": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", - "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", - "dev": true - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", - "dev": true, - "requires": { - "jake": "^10.8.5" - } - }, - "electron": { - "version": "24.7.0", - "dev": true, - "requires": { - "@electron/get": "^2.0.0", - "@types/node": "^18.11.18", - "extract-zip": "^2.0.1" - }, - "dependencies": { - "@types/node": { - "version": "18.16.16", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.16.tgz", - "integrity": "sha512-NpaM49IGQQAUlBhHMF82QH80J08os4ZmyF9MkpCzWAGuOHqE4gTEbhzd7L3l5LmWuZ6E0OiC1FweQ4tsiW35+g==", - "dev": true - } - } - }, - "electron-builder": { - "version": "23.6.0", - "dev": true, - "requires": { - "@types/yargs": "^17.0.1", - "app-builder-lib": "23.6.0", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "dmg-builder": "23.6.0", - "fs-extra": "^10.0.0", - "is-ci": "^3.0.0", - "lazy-val": "^1.0.5", - "read-config-file": "6.2.0", - "simple-update-notifier": "^1.0.7", - "yargs": "^17.5.1" - }, - "dependencies": { - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", - "dev": true, - "requires": { - "ci-info": "^3.2.0" - } - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "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" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } - } - }, - "electron-devtools-installer": { - "version": "3.2.0", - "dev": true, - "requires": { - "rimraf": "^3.0.2", - "semver": "^7.2.1", - "tslib": "^2.1.0", - "unzip-crx-3": "^0.2.0" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - } - } - }, - "electron-dl": { - "version": "3.5.0", - "requires": { - "ext-name": "^5.0.0", - "pupa": "^2.0.1", - "unused-filename": "^2.1.0" - } - }, - "electron-notarize": { - "version": "1.2.2", - "dev": true, - "requires": { - "debug": "^4.1.1", - "fs-extra": "^9.0.1" - }, - "dependencies": { - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true - } - } - } - } - }, - "electron-osx-sign": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.6.0.tgz", - "integrity": "sha512-+hiIEb2Xxk6eDKJ2FFlpofCnemCbjbT5jz+BKGpVBrRNT3kWTGs4DfNX6IzGwgi33hUcXF+kFs9JW+r6Wc1LRg==", - "dev": true, - "requires": { - "bluebird": "^3.5.0", - "compare-version": "^0.1.2", - "debug": "^2.6.8", - "isbinaryfile": "^3.0.2", - "minimist": "^1.2.0", - "plist": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "isbinaryfile": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", - "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", - "dev": true, - "requires": { - "buffer-alloc": "^1.2.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "electron-publish": { - "version": "23.6.0", - "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-23.6.0.tgz", - "integrity": "sha512-jPj3y+eIZQJF/+t5SLvsI5eS4mazCbNYqatv5JihbqOstIM13k0d1Z3vAWntvtt13Itl61SO6seicWdioOU5dg==", - "dev": true, - "requires": { - "@types/fs-extra": "^9.0.11", - "builder-util": "23.6.0", - "builder-util-runtime": "9.1.1", - "chalk": "^4.1.1", - "fs-extra": "^10.0.0", - "lazy-val": "^1.0.5", - "mime": "^2.5.2" - } - }, - "electron-store": { - "version": "8.1.0", - "requires": { - "conf": "^10.2.0", - "type-fest": "^2.17.0" - }, - "dependencies": { - "type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==" - } - } - }, - "electron-to-chromium": { - "version": "1.4.503", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.503.tgz", - "integrity": "sha512-LF2IQit4B0VrUHFeQkWhZm97KuJSGF2WJqq1InpY+ECpFRkXd8yTIaTtJxsO0OKDmiBYwWqcrNaXOurn2T2wiA==", - "dev": true - }, - "electron-updater": { - "version": "5.3.0", - "requires": { - "@types/semver": "^7.3.6", - "builder-util-runtime": "9.1.1", - "fs-extra": "^10.0.0", - "js-yaml": "^4.1.0", - "lazy-val": "^1.0.5", - "lodash.escaperegexp": "^4.1.2", - "lodash.isequal": "^4.5.0", - "semver": "^7.3.5", - "typed-emitter": "^2.1.0" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "element-closest-polyfill": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/element-closest-polyfill/-/element-closest-polyfill-1.0.6.tgz", - "integrity": "sha512-rnCCQ89MO1D00I+zOCjuVBl9QyF1pXs5ei+7/3it43/mXrcVHG1GQJaMatfOwPCuI/d3ucE4djNr23r0KxsIzA==" - }, - "emittery": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.2.tgz", - "integrity": "sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha512-bl1LAgiQc4ZWr++pNYUdRe/alecaHFeHxIJ/pNciqGdKXghaTCOwKkbKp6ye7pKZGu/GcaSXFk8PBVhgs+dJdA==", - "requires": { - "iconv-lite": "~0.4.13" - } - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "requires": { - "stackframe": "^1.3.4" - } - }, - "es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - } - }, - "es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - } - }, - "es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-error": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", - "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", - "dev": true, - "optional": true - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "escodegen": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", - "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "eslint": { - "version": "7.32.0", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-import-resolver-typescript": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz", - "integrity": "sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==", - "dev": true, - "requires": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.12.0", - "eslint-module-utils": "^2.7.4", - "get-tsconfig": "^4.5.0", - "globby": "^13.1.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "synckit": "^0.8.5" - }, - "dependencies": { - "globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-import": { - "version": "2.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", - "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", - "dev": true, - "requires": { - "array-includes": "^3.1.4", - "array.prototype.flat": "^1.2.5", - "debug": "^2.6.9", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.3", - "has": "^1.0.3", - "is-core-module": "^2.8.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.5", - "resolve": "^1.22.0", - "tsconfig-paths": "^3.14.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "eslint-plugin-prettier": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", - "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-plugin-react": { - "version": "7.32.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", - "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" - }, - "dependencies": { - "resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", - "dev": true, - "requires": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "dev": true - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "dependencies": { - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - } - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "event-pubsub": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz", - "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", - "dev": true - }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, - "ews-javascript-api": { - "version": "0.12.0", - "requires": { - "@xmldom/xmldom": "^0.8.1", - "base64-js": "^1.5.1", - "bluebird": "^3.7.2", - "fetch": "^1.1.0", - "moment": "^2.29.4", - "moment-timezone": "^0.5.41", - "uuid": "^8.3.2" - } - }, - "exec-sh": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.6.tgz", - "integrity": "sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "exif-parser": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", - "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", - "dev": true - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - } - } - }, - "expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "dev": true - }, - "expect": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-25.5.0.tgz", - "integrity": "sha512-w7KAXo0+6qqZZhovCaBVPSIqQp7/UTcx4M9uKt2m6pd2VB1voyC8JizLRqeEqud3AAVP02g+hbErDu5gu64tlA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-styles": "^4.0.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-regex-util": "^25.2.6" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true - } - } - }, - "ext-list": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz", - "integrity": "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==", - "requires": { - "mime-db": "^1.28.0" - } - }, - "ext-name": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz", - "integrity": "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==", - "requires": { - "ext-list": "^2.0.0", - "sort-keys-length": "^1.0.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "dev": true, - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - } - }, - "extsprintf": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", - "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", - "dev": true, - "optional": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true - }, - "fast-glob": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", - "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fast-xml-parser": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", - "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", - "dev": true, - "optional": true, - "requires": { - "strnum": "^1.0.5" - } - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, - "fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-5O8TwrGzoNblBG/jtK4NFuZwNCkZX6s5GfRNOaGtm+QGJEuNakSC/i2RW0R93KX6E0jVjNXm6O3CRN4Ql3K+yA==", - "requires": { - "biskviit": "1.0.1", - "encoding": "0.1.12" - } - }, - "fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "dev": true, - "requires": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", - "dev": true, - "requires": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" - } - }, - "file-url": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/file-url/-/file-url-2.0.2.tgz", - "integrity": "sha512-x3989K8a1jM6vulMigE8VngH7C5nci0Ks5d9kVjUXmNF28gmiZUNujk5HjwaS8dAzN2QmUfX56riJKgN00dNRw==", - "dev": true - }, - "filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "requires": { - "minimatch": "^5.0.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "dependencies": { - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } - } - }, - "flat-cache": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", - "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", - "dev": true, - "requires": { - "flatted": "^3.2.7", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "flow-parser": { - "version": "0.215.1", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.215.1.tgz", - "integrity": "sha512-qq3rdRToqwesrddyXf+Ml8Tuf7TdoJS+EMbJgC6fHAVoBCXjb4mHelNd3J+jD8ts0bSHX81FG3LN7Qn/dcl6pA==", - "dev": true - }, - "focus-visible": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.0.tgz", - "integrity": "sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ==" - }, - "focus-within-polyfill": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/focus-within-polyfill/-/focus-within-polyfill-5.2.1.tgz", - "integrity": "sha512-24dSGm2euJ0V+blff5Yj0IXtemjjGIoT6/OjmvICZ80NIgnyXDpthfxdkJAJWCDs0BaUvdB+2RParJ9b7X4EuQ==" - }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" - }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dev": true, - "requires": { - "fetch-blob": "^3.1.2" - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - } - } - }, - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "requires": { - "minipass": "^3.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - } - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", - "dev": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - } - } - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", - "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-pkg-repo": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", - "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", - "dev": true, - "requires": { - "@hutson/parse-repository-url": "^3.0.0", - "hosted-git-info": "^4.0.0", - "through2": "^2.0.0", - "yargs": "^16.2.0" - }, - "dependencies": { - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - } - } - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "get-tsconfig": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.0.tgz", - "integrity": "sha512-pmjiZ7xtB8URYm74PlGJozDNyhvsVLUcpBa8DZBG3bWHwaHa9bPiRpiSfovw+fjhwONSCWKRyk+JQHEGZmMrzw==", - "dev": true, - "requires": { - "resolve-pkg-maps": "^1.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "gifwrap": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.9.4.tgz", - "integrity": "sha512-MDMwbhASQuVeD4JKd1fKgNgCRL3fGqMM4WaqpNhWO0JiMOAjbQdumbs4BbBZEy9/M00EHEjKN3HieVhCUlwjeQ==", - "dev": true, - "requires": { - "image-q": "^4.0.0", - "omggif": "^1.0.10" - } - }, - "git-raw-commits": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", - "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", - "dev": true, - "requires": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - } - }, - "git-remote-origin-url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", - "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", - "dev": true, - "requires": { - "gitconfiglocal": "^1.0.0", - "pify": "^2.3.0" - } - }, - "git-semver-tags": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", - "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", - "dev": true, - "requires": { - "meow": "^8.0.0", - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "gitconfiglocal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", - "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", - "dev": true, - "requires": { - "ini": "^1.3.2" - } - }, - "github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "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" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "global": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", - "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", - "dev": true, - "requires": { - "min-document": "^2.19.0", - "process": "^0.11.10" - } - }, - "global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "dev": true, - "optional": true, - "requires": { - "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" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", - "dev": true, - "requires": { - "@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" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "graceful-readlink": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", - "integrity": "sha512-8tLu60LgxF6XpdbK8OW3FA+IfTNBn1ZHGHKF4KQbEeSkajYw5PlYJcKluntgegDPTg8UkHjpet1T82vk6TQ68w==", - "dev": true - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", - "dev": true, - "optional": true - }, - "handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" - } - }, - "hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "html-encoding-sniffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", - "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.1" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "requires": { - "void-elements": "3.1.0" - } - }, - "http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, - "http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "requires": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", - "dev": true, - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" - }, - "dependencies": { - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true - } - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, - "i18next": { - "version": "21.10.0", - "requires": { - "@babel/runtime": "^7.17.2" - } - }, - "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, - "optional": true, - "requires": { - "cli-truncate": "^2.1.0", - "node-addon-api": "^1.6.3" - }, - "dependencies": { - "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, - "optional": true - } - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "image-q": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/image-q/-/image-q-4.0.0.tgz", - "integrity": "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw==", - "dev": true, - "requires": { - "@types/node": "16.9.1" - }, - "dependencies": { - "@types/node": { - "version": "16.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.1.tgz", - "integrity": "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g==", - "dev": true - } - } - }, - "immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - } - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - } - }, - "intl-messageformat": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.0.tgz", - "integrity": "sha512-AvojYuOaRb6r2veOKfTVpxH9TrmjSdc5iR9R5RgBwrDZYSmAAFVT+QLbW3C4V7Qsg0OguMp67Q/EoUkxZzXRGw==", - "requires": { - "@formatjs/ecma402-abstract": "1.17.0", - "@formatjs/fast-memoize": "2.2.0", - "@formatjs/icu-messageformat-parser": "2.6.0", - "tslib": "^2.4.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - } - } - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "requires": { - "builtin-modules": "^3.3.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", - "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "requires": { - "is-docker": "^3.0.0" - }, - "dependencies": { - "is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true - } - } - }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==" - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "requires": { - "@types/estree": "*" - } - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-text-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", - "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", - "dev": true, - "requires": { - "text-extensions": "^1.0.0" - } - }, - "is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "requires": { - "which-typed-array": "^1.1.11" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", - "dev": true - }, - "iserror": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/iserror/-/iserror-0.0.2.tgz", - "integrity": "sha512-oKGGrFVaWwETimP3SiWwjDeY27ovZoyZPHtxblC4hCq9fXxed/jasx+ATWFFjCVSRZng8VTMsN1nDnGo6zMBSw==" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", - "dev": true, - "requires": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - } - }, - "jest": { - "version": "26.6.3", - "dev": true, - "requires": { - "@jest/core": "^26.6.3", - "import-local": "^3.0.2", - "jest-cli": "^26.6.3" - }, - "dependencies": { - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/globals": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", - "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/types": "^26.6.2", - "expect": "^26.6.2" - } - }, - "@jest/source-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", - "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-sequencer": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", - "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", - "dev": true, - "requires": { - "@jest/test-result": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "babel-jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", - "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", - "dev": true, - "requires": { - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", - "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", - "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^26.6.2", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "source-map": "~0.6.1" - } - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "jest-cli": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz", - "integrity": "sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==", - "dev": true, - "requires": { - "@jest/core": "^26.6.3", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.6.3", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "prompts": "^2.0.1", - "yargs": "^15.4.1" - } - }, - "jest-config": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", - "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.6.3", - "@jest/types": "^26.6.2", - "babel-jest": "^26.6.3", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.6.2", - "jest-environment-node": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.6.3", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-each": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", - "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2" - } - }, - "jest-environment-jsdom": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", - "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2", - "jsdom": "^16.4.0" - } - }, - "jest-environment-node": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", - "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-jasmine2": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", - "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.6.2", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2", - "throat": "^5.0.0" - } - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-runtime": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", - "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/globals": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "cjs-module-lexer": "^0.6.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.4.1" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - } - } - }, - "jest-changed-files": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.6.2.tgz", - "integrity": "sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "execa": "^4.0.0", - "throat": "^5.0.0" - }, - "dependencies": { - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - } - } - }, - "jest-config": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-25.5.4.tgz", - "integrity": "sha512-SZwR91SwcdK6bz7Gco8qL7YY2sx8tFJYzvg216DLihTWf+LKY/DoJXpM9nTzYakSyfblbqeU48p/p7Jzy05Atg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^25.5.4", - "@jest/types": "^25.5.0", - "babel-jest": "^25.5.1", - "chalk": "^3.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^25.5.0", - "jest-environment-node": "^25.5.0", - "jest-get-type": "^25.2.6", - "jest-jasmine2": "^25.5.4", - "jest-regex-util": "^25.2.6", - "jest-resolve": "^25.5.1", - "jest-util": "^25.5.0", - "jest-validate": "^25.5.0", - "micromatch": "^4.0.2", - "pretty-format": "^25.5.0", - "realpath-native": "^2.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true - }, - "jest-resolve": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", - "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "read-pkg-up": "^7.0.1", - "realpath-native": "^2.0.0", - "resolve": "^1.17.0", - "slash": "^3.0.0" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "jest-validate": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", - "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "jest-get-type": "^25.2.6", - "leven": "^3.1.0", - "pretty-format": "^25.5.0" - } - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } - } - }, - "jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } - } - }, - "jest-docblock": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", - "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.5.0.tgz", - "integrity": "sha512-QBogUxna3D8vtiItvn54xXde7+vuzqRrEeaw8r1s+1TG9eZLVJE5ZkKoSUlqFwRjnlaA4hyKGiu9OlkFIuKnjA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-get-type": "^25.2.6", - "jest-util": "^25.5.0", - "pretty-format": "^25.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } - } - }, - "jest-environment-jsdom": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-25.5.0.tgz", - "integrity": "sha512-7Jr02ydaq4jaWMZLY+Skn8wL5nVIYpWvmeatOHL3tOcV3Zw8sjnPpx+ZdeBfc457p8jCR9J6YCc+Lga0oIy62A==", - "dev": true, - "requires": { - "@jest/environment": "^25.5.0", - "@jest/fake-timers": "^25.5.0", - "@jest/types": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-util": "^25.5.0", - "jsdom": "^15.2.1" - }, - "dependencies": { - "@jest/fake-timers": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.5.0.tgz", - "integrity": "sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-util": "^25.5.0", - "lolex": "^5.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - } - } - }, - "jest-environment-node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-25.5.0.tgz", - "integrity": "sha512-iuxK6rQR2En9EID+2k+IBs5fCFd919gVVK5BeND82fYeLWPqvRcFNPKu9+gxTwfB5XwBGBvZ0HFQa+cHtIoslA==", - "dev": true, - "requires": { - "@jest/environment": "^25.5.0", - "@jest/fake-timers": "^25.5.0", - "@jest/types": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-util": "^25.5.0", - "semver": "^6.3.0" - }, - "dependencies": { - "@jest/fake-timers": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.5.0.tgz", - "integrity": "sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-util": "^25.5.0", - "lolex": "^5.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", - "dev": true - }, - "jest-haste-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", - "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "dependencies": { - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - } - } - }, - "jest-jasmine2": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-25.5.4.tgz", - "integrity": "sha512-9acbWEfbmS8UpdcfqnDO+uBUgKa/9hcRh983IHdM+pKmJPL77G0sWAAK0V0kr5LK3a8cSBfkFSoncXwQlRZfkQ==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^25.5.0", - "@jest/source-map": "^25.5.0", - "@jest/test-result": "^25.5.0", - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "co": "^4.6.0", - "expect": "^25.5.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^25.5.0", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-runtime": "^25.5.4", - "jest-snapshot": "^25.5.1", - "jest-util": "^25.5.0", - "pretty-format": "^25.5.0", - "throat": "^5.0.0" - }, - "dependencies": { - "@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", - "slash": "^3.0.0" - } - }, - "@jest/source-map": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.5.0.tgz", - "integrity": "sha512-eIGx0xN12yVpMcPaVpjXPnn3N30QGJCJQSkEDUt9x1fI1Gdvb07Ml6K5iN2hG7NmMP6FDmtPEssE3z6doOYUwQ==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "@types/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", - "dev": true - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-resolve": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", - "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "read-pkg-up": "^7.0.1", - "realpath-native": "^2.0.0", - "resolve": "^1.17.0", - "slash": "^3.0.0" - } - }, - "jest-snapshot": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.5.1.tgz", - "integrity": "sha512-C02JE1TUe64p2v1auUJ2ze5vcuv32tkv9PyhEb318e8XOKF7MOyXdJ7kdjbvrp3ChPLU2usI7Rjxs97Dj5P0uQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/prettier": "^1.19.0", - "chalk": "^3.0.0", - "expect": "^25.5.0", - "graceful-fs": "^4.2.4", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-resolve": "^25.5.1", - "make-dir": "^3.0.0", - "natural-compare": "^1.4.0", - "pretty-format": "^25.5.0", - "semver": "^6.3.0" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "jest-leak-detector": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.6.2.tgz", - "integrity": "sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==", - "dev": true, - "requires": { - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "dependencies": { - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - } - } - }, - "jest-matcher-utils": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz", - "integrity": "sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - } - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "dependencies": { - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-mock": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.5.0.tgz", - "integrity": "sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0" - }, - "dependencies": { - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.6.3.tgz", - "integrity": "sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.6.2" - } - }, - "jest-runner": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.6.3.tgz", - "integrity": "sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.7.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.6.2", - "jest-leak-detector": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - }, - "dependencies": { - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/globals": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.6.2.tgz", - "integrity": "sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/types": "^26.6.2", - "expect": "^26.6.2" - } - }, - "@jest/source-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz", - "integrity": "sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-sequencer": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.6.3.tgz", - "integrity": "sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==", - "dev": true, - "requires": { - "@jest/test-result": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.6.2", - "jest-runner": "^26.6.3", - "jest-runtime": "^26.6.3" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "babel-jest": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.6.3.tgz", - "integrity": "sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==", - "dev": true, - "requires": { - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.6.2.tgz", - "integrity": "sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.6.2.tgz", - "integrity": "sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^26.6.2", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "source-map": "~0.6.1" - } - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "jest-config": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.6.3.tgz", - "integrity": "sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.6.3", - "@jest/types": "^26.6.2", - "babel-jest": "^26.6.3", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.6.2", - "jest-environment-node": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-jasmine2": "^26.6.3", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-each": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", - "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2" - } - }, - "jest-environment-jsdom": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz", - "integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2", - "jsdom": "^16.4.0" - } - }, - "jest-environment-node": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz", - "integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==", - "dev": true, - "requires": { - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-jasmine2": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz", - "integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.6.2", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-runtime": "^26.6.3", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2", - "throat": "^5.0.0" - } - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-runtime": { - "version": "26.6.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.6.3.tgz", - "integrity": "sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/environment": "^26.6.2", - "@jest/fake-timers": "^26.6.2", - "@jest/globals": "^26.6.2", - "@jest/source-map": "^26.6.2", - "@jest/test-result": "^26.6.2", - "@jest/transform": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "cjs-module-lexer": "^0.6.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.6.3", - "jest-haste-map": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.6.2", - "jest-snapshot": "^26.6.2", - "jest-util": "^26.6.2", - "jest-validate": "^26.6.2", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.4.1" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - } - } - }, - "jest-runtime": { - "version": "25.5.4", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-25.5.4.tgz", - "integrity": "sha512-RWTt8LeWh3GvjYtASH2eezkc8AehVoWKK20udV6n3/gC87wlTbE1kIA+opCvNWyyPeBs6ptYsc6nyHUb1GlUVQ==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/environment": "^25.5.0", - "@jest/globals": "^25.5.2", - "@jest/source-map": "^25.5.0", - "@jest/test-result": "^25.5.0", - "@jest/transform": "^25.5.1", - "@jest/types": "^25.5.0", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^25.5.4", - "jest-haste-map": "^25.5.1", - "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-regex-util": "^25.2.6", - "jest-resolve": "^25.5.1", - "jest-snapshot": "^25.5.1", - "jest-util": "^25.5.0", - "jest-validate": "^25.5.0", - "realpath-native": "^2.0.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.3.1" - }, - "dependencies": { - "@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", - "slash": "^3.0.0" - } - }, - "@jest/source-map": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-25.5.0.tgz", - "integrity": "sha512-eIGx0xN12yVpMcPaVpjXPnn3N30QGJCJQSkEDUt9x1fI1Gdvb07Ml6K5iN2hG7NmMP6FDmtPEssE3z6doOYUwQ==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/transform": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-25.5.1.tgz", - "integrity": "sha512-Y8CEoVwXb4QwA6Y/9uDkn0Xfz0finGkieuV0xkdF9UtZGJeLukD5nLkaVrVsODB1ojRWlaoD0AJZpVHCSnJEvg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^25.5.0", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^3.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^25.5.1", - "jest-regex-util": "^25.2.6", - "jest-util": "^25.5.0", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "realpath-native": "^2.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "@types/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", - "dev": true - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "jest-haste-map": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-25.5.1.tgz", - "integrity": "sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "@types/graceful-fs": "^4.1.2", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-serializer": "^25.5.0", - "jest-util": "^25.5.0", - "jest-worker": "^25.5.0", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true - }, - "jest-resolve": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", - "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "read-pkg-up": "^7.0.1", - "realpath-native": "^2.0.0", - "resolve": "^1.17.0", - "slash": "^3.0.0" - } - }, - "jest-serializer": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-25.5.0.tgz", - "integrity": "sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4" - } - }, - "jest-snapshot": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.5.1.tgz", - "integrity": "sha512-C02JE1TUe64p2v1auUJ2ze5vcuv32tkv9PyhEb318e8XOKF7MOyXdJ7kdjbvrp3ChPLU2usI7Rjxs97Dj5P0uQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/prettier": "^1.19.0", - "chalk": "^3.0.0", - "expect": "^25.5.0", - "graceful-fs": "^4.2.4", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-resolve": "^25.5.1", - "make-dir": "^3.0.0", - "natural-compare": "^1.4.0", - "pretty-format": "^25.5.0", - "semver": "^6.3.0" - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "jest-validate": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-25.5.0.tgz", - "integrity": "sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "jest-get-type": "^25.2.6", - "leven": "^3.1.0", - "pretty-format": "^25.5.0" - } - }, - "jest-worker": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-25.5.0.tgz", - "integrity": "sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==", - "dev": true, - "requires": { - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - } - } - }, - "jest-serializer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", - "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - } - }, - "jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - }, - "dependencies": { - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "jest-validate": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.6.2.tgz", - "integrity": "sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "leven": "^3.1.0", - "pretty-format": "^26.6.2" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.6.2.tgz", - "integrity": "sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==", - "dev": true, - "requires": { - "@jest/test-result": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^26.6.2", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "jimp": { - "version": "0.16.13", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.2", - "@jimp/custom": "^0.16.13", - "@jimp/plugins": "^0.16.13", - "@jimp/types": "^0.16.13", - "regenerator-runtime": "^0.13.3" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - } - } - }, - "jpeg-js": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", - "integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==", - "dev": true - }, - "js-message": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", - "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", - "dev": true - }, - "js-queue": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz", - "integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==", - "dev": true, - "requires": { - "easy-stack": "^1.0.1" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" - }, - "jscodeshift": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.6.4.tgz", - "integrity": "sha512-+NF/tlNbc2WEhXUuc4WEJLsJumF84tnaMUZW2hyJw3jThKKRvsPX4sPJVgO1lPE28z0gNL+gwniLG9d8mYvQCQ==", - "dev": true, - "requires": { - "@babel/core": "^7.1.6", - "@babel/parser": "^7.1.6", - "@babel/plugin-proposal-class-properties": "^7.1.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/preset-env": "^7.1.6", - "@babel/preset-flow": "^7.0.0", - "@babel/preset-typescript": "^7.1.0", - "@babel/register": "^7.0.0", - "babel-core": "^7.0.0-bridge.0", - "colors": "^1.1.2", - "flow-parser": "0.*", - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "neo-async": "^2.5.0", - "node-dir": "^0.1.17", - "recast": "^0.16.1", - "temp": "^0.8.1", - "write-file-atomic": "^2.3.0" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - } - } - }, - "jsdom": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz", - "integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==", - "dev": true, - "requires": { - "abab": "^2.0.0", - "acorn": "^7.1.0", - "acorn-globals": "^4.3.2", - "array-equal": "^1.0.0", - "cssom": "^0.4.1", - "cssstyle": "^2.0.0", - "data-urls": "^1.1.0", - "domexception": "^1.0.1", - "escodegen": "^1.11.1", - "html-encoding-sniffer": "^1.0.2", - "nwsapi": "^2.2.0", - "parse5": "5.1.0", - "pn": "^1.1.0", - "request": "^2.88.0", - "request-promise-native": "^1.0.7", - "saxes": "^3.1.9", - "symbol-tree": "^3.2.2", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.1", - "w3c-xmlserializer": "^1.1.2", - "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^7.0.0", - "ws": "^7.0.0", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-schema-typed": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz", - "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - }, - "dependencies": { - "universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" - } - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true - }, - "jsonwebtoken": { - "version": "9.0.1", - "requires": { - "jws": "^3.2.2", - "lodash": "^4.17.21", - "ms": "^2.1.1", - "semver": "^7.3.8" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "dependencies": { - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - } - } - }, - "jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - } - }, - "jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dev": true, - "requires": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "keyv": { - "version": "4.5.3", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", - "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "lazy-val": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", - "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "requires": { - "immediate": "~3.0.5" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" - }, - "load-bmfont": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.1.tgz", - "integrity": "sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==", - "dev": true, - "requires": { - "buffer-equal": "0.0.1", - "mime": "^1.3.4", - "parse-bmfont-ascii": "^1.0.3", - "parse-bmfont-binary": "^1.0.5", - "parse-bmfont-xml": "^1.1.4", - "phin": "^2.9.1", - "xhr": "^2.0.1", - "xtend": "^4.0.0" - }, - "dependencies": { - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true - } - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - } - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "lodash.escaperegexp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" - }, - "lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" - }, - "lodash.ismatch": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.omit": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==", - "dev": true - }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, - "lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.8" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true - }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "dev": true, - "optional": true, - "requires": { - "escape-string-regexp": "^4.0.0" - } - }, - "memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "dev": true, - "optional": true - }, - "memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true - }, - "meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "dependencies": { - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - }, - "min-document": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", - "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==", - "dev": true, - "requires": { - "dom-walk": "^0.1.0" - } - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true - }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - } - }, - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "requires": { - "minimist": "^1.2.6" - } - }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, - "modify-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/modify-filename/-/modify-filename-1.1.0.tgz", - "integrity": "sha512-EickqnKq3kVVaZisYuCxhtKbZjInCuwgwZWyAmRIp1NTMhri7r3380/uqwrUHfaDiPzLVTuoNy4whX66bxPVog==" - }, - "modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true - }, - "moment": { - "version": "2.29.4", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", - "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" - }, - "moment-timezone": { - "version": "0.5.43", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", - "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", - "requires": { - "moment": "^2.29.4" - } - }, - "mongodb": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.16.0.tgz", - "integrity": "sha512-0EB113Fsucaq1wsY0dOhi1fmZOwFtLOtteQkiqOXGklvWMnSH3g2QS53f0KTP+/6qOkuoXE2JksubSZNmxeI+g==", - "dev": true, - "requires": { - "@aws-sdk/credential-providers": "^3.186.0", - "bson": "^4.7.2", - "mongodb-connection-string-url": "^2.5.4", - "saslprep": "^1.0.3", - "socks": "^2.7.1" - } - }, - "mongodb-connection-string-url": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", - "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", - "dev": true, - "requires": { - "@types/whatwg-url": "^8.2.1", - "whatwg-url": "^11.0.0" - }, - "dependencies": { - "tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true - }, - "whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, - "requires": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - } - } - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "new-event-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/new-event-polyfill/-/new-event-polyfill-1.0.1.tgz", - "integrity": "sha512-cgNgv9yb3C8xIz9mn9dp98bHVcBv1uxyCq/7R2ZiRC+joyLpgBWvLNJMDzoECnGUC7EAxLEnXh1SxHE0nAQ6gA==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-dir": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", - "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", - "dev": true, - "requires": { - "minimatch": "^3.0.2" - } - }, - "node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "dev": true - }, - "node-fetch": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz", - "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==", - "dev": true, - "requires": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - } - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-ipc": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz", - "integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==", - "dev": true, - "requires": { - "event-pubsub": "4.3.0", - "js-message": "1.0.7", - "js-queue": "2.0.2" - } - }, - "node-notifier": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-8.0.2.tgz", - "integrity": "sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==", - "dev": true, - "optional": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^2.2.0", - "semver": "^7.3.2", - "shellwords": "^0.1.1", - "uuid": "^8.3.0", - "which": "^2.0.2" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "dev": true - }, - "npm-run-all": { - "version": "4.1.5", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - }, - "dependencies": { - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - } - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", - "dev": true - }, - "nwsapi": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", - "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" - } - } - } - }, - "object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", - "dev": true, - "requires": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.1.tgz", - "integrity": "sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.1", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.1", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "safe-array-concat": "^1.0.0", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.10" - } - } - } - }, - "omggif": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz", - "integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "requires": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "dependencies": { - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - } - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true - }, - "p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", - "dev": true - }, - "p-each-series": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", - "integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-bmfont-ascii": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", - "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", - "dev": true - }, - "parse-bmfont-binary": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", - "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", - "dev": true - }, - "parse-bmfont-xml": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.4.tgz", - "integrity": "sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==", - "dev": true, - "requires": { - "xml-parse-from-string": "^1.0.0", - "xml2js": "^0.4.5" - } - }, - "parse-headers": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", - "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==", - "dev": true - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", - "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" - }, - "peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "dev": true - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "phin": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/phin/-/phin-2.9.3.tgz", - "integrity": "sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true - }, - "pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true - }, - "pixelmatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", - "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==", - "dev": true, - "requires": { - "pngjs": "^3.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "pkg-up": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", - "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", - "requires": { - "find-up": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - } - } - }, - "plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", - "dev": true, - "requires": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" - }, - "dependencies": { - "@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", - "dev": true - } - } - }, - "pn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true - }, - "pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", - "dev": true - }, - "pollock": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/pollock/-/pollock-0.2.1.tgz", - "integrity": "sha512-2Xy6LImSXm0ANKv9BKSVuCa6Z4ACbK7oUrl9gtUgqLkekL7n9C0mlWsOGYYuGbCG8xT0x3Q4F31C3ZMyVQjwsg==", - "dev": true - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", - "dev": true - }, - "prettier": { - "version": "2.8.8", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - } - } - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" - }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "requires": { - "escape-goat": "^2.0.0" - } - }, - "puppeteer": { - "version": "13.7.0", - "dev": true, - "requires": { - "cross-fetch": "3.1.5", - "debug": "4.3.4", - "devtools-protocol": "0.0.981744", - "extract-zip": "2.0.1", - "https-proxy-agent": "5.0.1", - "pkg-dir": "4.2.0", - "progress": "2.0.3", - "proxy-from-env": "1.1.0", - "rimraf": "3.0.2", - "tar-fs": "2.1.1", - "unbzip2-stream": "1.4.3", - "ws": "8.5.0" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", - "dev": true - } - } - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true - }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "dev": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "dev": true - } - } - }, - "react": { - "version": "17.0.2", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "react-aria": { - "version": "3.23.1", - "resolved": "https://registry.npmjs.org/react-aria/-/react-aria-3.23.1.tgz", - "integrity": "sha512-fptvkglL/HiX/llx2QCCt4WUJzuj6SLoyUJzF50kOLnX+sz5QhWQ+6dCEN/d3TWCRaXudC1nREUu8MgoV+lg7g==", - "requires": { - "@react-aria/breadcrumbs": "^3.5.0", - "@react-aria/button": "^3.7.0", - "@react-aria/calendar": "^3.1.0", - "@react-aria/checkbox": "^3.8.0", - "@react-aria/combobox": "^3.5.1", - "@react-aria/datepicker": "^3.3.0", - "@react-aria/dialog": "^3.5.0", - "@react-aria/dnd": "^3.1.0", - "@react-aria/focus": "^3.11.0", - "@react-aria/gridlist": "^3.2.1", - "@react-aria/i18n": "^3.7.0", - "@react-aria/interactions": "^3.14.0", - "@react-aria/label": "^3.5.0", - "@react-aria/link": "^3.4.0", - "@react-aria/listbox": "^3.8.1", - "@react-aria/menu": "^3.8.1", - "@react-aria/meter": "^3.4.0", - "@react-aria/numberfield": "^3.4.0", - "@react-aria/overlays": "^3.13.0", - "@react-aria/progress": "^3.4.0", - "@react-aria/radio": "^3.5.0", - "@react-aria/searchfield": "^3.5.0", - "@react-aria/select": "^3.9.1", - "@react-aria/selection": "^3.13.1", - "@react-aria/separator": "^3.3.0", - "@react-aria/slider": "^3.3.0", - "@react-aria/ssr": "^3.5.0", - "@react-aria/switch": "^3.4.0", - "@react-aria/table": "^3.8.1", - "@react-aria/tabs": "^3.4.1", - "@react-aria/textfield": "^3.9.0", - "@react-aria/tooltip": "^3.4.0", - "@react-aria/utils": "^3.15.0", - "@react-aria/visually-hidden": "^3.7.0" - } - }, - "react-dom": { - "version": "17.0.2", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - } - }, - "react-hook-form": { - "version": "7.43.9" - }, - "react-i18next": { - "version": "11.18.6", - "requires": { - "@babel/runtime": "^7.14.5", - "html-parse-stringify": "^3.0.1" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "react-redux": { - "version": "7.2.9", - "requires": { - "@babel/runtime": "^7.15.4", - "@types/react-redux": "^7.1.20", - "hoist-non-react-statics": "^3.3.2", - "loose-envify": "^1.4.0", - "prop-types": "^15.7.2", - "react-is": "^17.0.2" - }, - "dependencies": { - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - } - } - }, - "react-stately": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/react-stately/-/react-stately-3.17.0.tgz", - "integrity": "sha512-wAclTwKXt2BoK1OLajxkW+Zl0gQer3qL/eDaNd98xvK4IYlEBxcCVXzvvzkMcOzPai2iqPFAk0w6PzMjlhUy1g==", - "requires": { - "@react-stately/calendar": "^3.0.2", - "@react-stately/checkbox": "^3.2.1", - "@react-stately/collections": "^3.4.3", - "@react-stately/combobox": "^3.2.1", - "@react-stately/data": "^3.6.1", - "@react-stately/datepicker": "^3.0.2", - "@react-stately/list": "^3.5.3", - "@react-stately/menu": "^3.4.1", - "@react-stately/numberfield": "^3.2.1", - "@react-stately/overlays": "^3.4.1", - "@react-stately/radio": "^3.5.1", - "@react-stately/searchfield": "^3.3.1", - "@react-stately/select": "^3.3.1", - "@react-stately/selection": "^3.10.3", - "@react-stately/slider": "^3.2.1", - "@react-stately/table": "^3.4.0", - "@react-stately/tabs": "^3.2.1", - "@react-stately/toggle": "^3.4.1", - "@react-stately/tooltip": "^3.2.1", - "@react-stately/tree": "^3.3.3", - "@react-types/shared": "^3.14.1" - } - }, - "read-config-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.2.0.tgz", - "integrity": "sha512-gx7Pgr5I56JtYz+WuqEbQHj/xWo+5Vwua2jhb1VwM4Wid5PqYmZ4i00ZB0YEGIfkVBsCv9UrjgyqCiQfS/Oosg==", - "dev": true, - "requires": { - "dotenv": "^9.0.2", - "dotenv-expand": "^5.1.0", - "js-yaml": "^4.1.0", - "json5": "^2.2.0", - "lazy-val": "^1.0.4" - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - }, - "dependencies": { - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "dependencies": { - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - } - } - }, - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", - "dev": true, - "requires": { - "readable-stream": "^3.6.0" - } - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } - }, - "realpath-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", - "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", - "dev": true - }, - "recast": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.16.2.tgz", - "integrity": "sha512-O/7qXi51DPjRVdbrpNzoBQH5dnAPQNbfoOFyRiUwreTMJfIHYOEBzwuH+c0+/BTSJ3CQyKs6ILSWXhESH6Op3A==", - "dev": true, - "requires": { - "ast-types": "0.11.7", - "esprima": "~4.0.0", - "private": "~0.1.5", - "source-map": "~0.6.1" - } - }, - "redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "requires": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - } - }, - "regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", - "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.2" - } - }, - "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, - "regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - } - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dev": true, - "requires": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - } - }, - "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true - }, - "repeat-element": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", - "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - } - } - }, - "request-promise-core": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz", - "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "request-promise-native": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz", - "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==", - "dev": true, - "requires": { - "request-promise-core": "1.1.4", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "reselect": { - "version": "4.1.6" - }, - "resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "dev": true - }, - "responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", - "dev": true, - "requires": { - "lowercase-keys": "^2.0.0" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "roarr": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", - "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", - "dev": true, - "optional": true, - "requires": { - "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" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", - "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", - "dev": true, - "optional": true - } - } - }, - "rollup": { - "version": "2.79.1", - "dev": true, - "requires": { - "fsevents": "~2.3.2" - }, - "dependencies": { - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - } - } - }, - "rollup-plugin-copy": { - "version": "3.4.0", - "dev": true, - "requires": { - "@types/fs-extra": "^8.0.1", - "colorette": "^1.1.0", - "fs-extra": "^8.1.0", - "globby": "10.0.1", - "is-plain-object": "^3.0.0" - }, - "dependencies": { - "@types/fs-extra": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz", - "integrity": "sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "globby": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", - "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" - } - }, - "is-plain-object": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", - "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", - "dev": true - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - } - } - }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true - }, - "run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "requires": { - "execa": "^5.0.0" - }, - "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - } - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "optional": true, - "requires": { - "tslib": "^2.1.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "optional": true - } - } - }, - "safe-array-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.0.tgz", - "integrity": "sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "dependencies": { - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - } - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "sanitize-filename": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", - "dev": true, - "requires": { - "truncate-utf8-bytes": "^1.0.0" - } - }, - "saslprep": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", - "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", - "dev": true, - "optional": true, - "requires": { - "sparse-bitfield": "^3.0.3" - } - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "saxes": { - "version": "3.1.11", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz", - "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==", - "dev": true, - "requires": { - "xmlchars": "^2.1.1" - } - }, - "scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", - "dev": true, - "optional": true - }, - "serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "optional": true, - "requires": { - "type-fest": "^0.13.1" - }, - "dependencies": { - "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "optional": true - } - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, - "shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true, - "optional": true - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "dev": true - }, - "simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "dev": true, - "requires": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dev": true, - "requires": { - "is-arrayish": "^0.3.1" - }, - "dependencies": { - "is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "dev": true - } - } - }, - "simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", - "dev": true, - "requires": { - "semver": "~7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dev": true, - "requires": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - } - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", - "requires": { - "is-plain-obj": "^1.0.0" - } - }, - "sort-keys-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz", - "integrity": "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==", - "requires": { - "sort-keys": "^1.0.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", - "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", - "dev": true - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", - "dev": true, - "optional": true, - "requires": { - "memory-pager": "^1.0.2" - } - }, - "spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", - "dev": true - }, - "split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, - "requires": { - "through": "2" - } - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dev": true, - "requires": { - "readable-stream": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, - "stack-generator": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz", - "integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==", - "requires": { - "stackframe": "^1.3.4" - } - }, - "stack-utils": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", - "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==" - }, - "stat-mode": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", - "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==", - "dev": true - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - } - }, - "string.prototype.padend": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz", - "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "requires": { - "min-indent": "^1.0.0" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", - "dev": true, - "optional": true - }, - "strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", - "dev": true, - "requires": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" - } - }, - "stylis": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.4.tgz", - "integrity": "sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ==" - }, - "sumchecker": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", - "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", - "dev": true, - "requires": { - "debug": "^4.1.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", - "dev": true, - "requires": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" - }, - "dependencies": { - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - } - } - }, - "table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "tar": { - "version": "6.1.15", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", - "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "dev": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - } - } - }, - "tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "requires": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "requires": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "temp": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz", - "integrity": "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==", - "dev": true, - "requires": { - "rimraf": "~2.6.2" - }, - "dependencies": { - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "dev": true - }, - "temp-file": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", - "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", - "dev": true, - "requires": { - "async-exit-hook": "^2.0.1", - "fs-extra": "^10.0.0" - } - }, - "tempfile": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-3.0.0.tgz", - "integrity": "sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw==", - "dev": true, - "requires": { - "temp-dir": "^2.0.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "dev": true, - "requires": { - "readable-stream": "3" - } - }, - "timm": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz", - "integrity": "sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==", - "dev": true - }, - "tinycolor2": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", - "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", - "dev": true - }, - "titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tmp-promise": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", - "dev": true, - "requires": { - "tmp": "^0.2.0" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "requires": { - "rimraf": "^3.0.0" - } - } - } - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - }, - "dependencies": { - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - } - } - }, - "token-types": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.1.tgz", - "integrity": "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==", - "dev": true, - "requires": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true - }, - "truncate-utf8-bytes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", - "dev": true, - "requires": { - "utf8-byte-length": "^1.0.1" - } - }, - "ts-jest": { - "version": "26.5.6", - "dev": true, - "requires": { - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^26.1.0", - "json5": "2.x", - "lodash": "4.x", - "make-error": "1.x", - "mkdirp": "1.x", - "semver": "7.x", - "yargs-parser": "20.x" - }, - "dependencies": { - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "ts-node": { - "version": "10.9.1", - "dev": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "dependencies": { - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - } - } - }, - "tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true - }, - "typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typed-emitter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", - "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", - "requires": { - "rxjs": "*" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typescript": { - "version": "4.9.5", - "dev": true - }, - "uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "dev": true, - "optional": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "requires": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", - "dev": true - } - } - }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true - }, - "unused-filename": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unused-filename/-/unused-filename-2.1.0.tgz", - "integrity": "sha512-BMiNwJbuWmqCpAM1FqxCTD7lXF97AvfQC8Kr/DIeA6VtvhJaMDupZ82+inbjl5yVP44PcxOuCSxye1QMS0wZyg==", - "requires": { - "modify-filename": "^1.1.0", - "path-exists": "^4.0.0" - }, - "dependencies": { - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - } - } - }, - "unzip-crx-3": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz", - "integrity": "sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==", - "dev": true, - "requires": { - "jszip": "^3.1.0", - "mkdirp": "^0.5.1", - "yaku": "^0.16.6" - } - }, - "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "dev": true - }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==" - }, - "utf8-byte-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz", - "integrity": "sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==", - "dev": true - }, - "utif": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz", - "integrity": "sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==", - "dev": true, - "requires": { - "pako": "^1.0.5" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "v8-to-istanbul": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-7.1.2.tgz", - "integrity": "sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true - } - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", - "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", - "dev": true, - "requires": { - "domexception": "^1.0.1", - "webidl-conversions": "^4.0.2", - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "dev": true - }, - "webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - } - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "which-typed-array": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", - "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "requires": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true - }, - "xhr": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", - "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", - "dev": true, - "requires": { - "global": "~4.4.0", - "is-function": "^1.0.1", - "parse-headers": "^2.0.0", - "xtend": "^4.0.0" - } - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xml-parse-from-string": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", - "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", - "dev": true - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "dependencies": { - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true - } - } - }, - "xmlbuilder": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", - "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "dev": true - }, - "xvfb-maybe": { - "version": "0.2.1", - "dev": true, - "requires": { - "debug": "^2.2.0", - "which": "^1.2.4" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yaku": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/yaku/-/yaku-0.16.7.tgz", - "integrity": "sha512-Syu3IB3rZvKvYk7yTiyl1bo/jiEFaaStrgv1V2TIJTqYPStSMQVO8EQjg/z+DRzLq/4LIIharNT3iH1hylEIRw==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "dependencies": { - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true - } - } -} diff --git a/package.json b/package.json index ff6a16b01f..d5950f3f41 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,9 @@ "productName": "Rocket.Chat", "name": "rocketchat", "description": "Official OSX, Windows, and Linux Desktop Clients for Rocket.Chat", - "version": "4.3.2", + "version": "4.13.0", "author": "Rocket.Chat Support ", - "copyright": "© 2016-2025, Rocket.Chat", + "copyright": "© 2016-2026, Rocket.Chat", "homepage": "https://rocket.chat", "license": "MIT", "goUrlShortener": "go.rocket.chat", @@ -33,6 +33,7 @@ "build": "rollup -c", "build:watch": "rollup -c -w", "build-mac": "yarn build && yarn electron-builder --publish never --mac --universal", + "build-mas": "yarn build && yarn electron-builder --publish never --mac mas --universal", "build-win": "yarn build && yarn electron-builder --publish never --win", "build-linux": "yarn build && yarn electron-builder --publish never --linux", "build-assets": "ts-node -O '{\"module\":\"commonjs\"}' src/buildAssets.ts", @@ -46,24 +47,29 @@ ".:lint:tsc": "tsc --noEmit", "lint-fix": "run-s .:lint-fix:eslint .:lint:tsc", ".:lint-fix:eslint": "eslint --fix .", - "workspaces:build": "yarn workspaces foreach -At run build" + "workspaces:build": "yarn workspaces foreach -At run build", + "release:tag": "ts-node -O '{\"module\":\"commonjs\"}' scripts/release-tag.ts", + "release:tag:win": "ts-node -O \"{\\\"module\\\":\\\"commonjs\\\"}\" scripts/release-tag.ts" }, "dependencies": { - "@bugsnag/js": "~7.22.3", + "@bugsnag/js": "~8.8.1", "@emotion/css": "~11.11.2", "@emotion/react": "~11.11.3", "@emotion/styled": "~11.11.0", - "@ewsjs/xhr": "~2.0.2", + "@ewsjs/xhr": "patch:@ewsjs/xhr@npm%3A2.0.2#~/.yarn/patches/@ewsjs-xhr-npm-2.0.2-77506b0a6c.patch", "@rocket.chat/css-in-js": "~0.31.25", + "@rocket.chat/desktop-api": "~1.1.0", "@rocket.chat/fuselage": "0.58.0", "@rocket.chat/fuselage-hooks": "~0.33.1", "@rocket.chat/fuselage-polyfills": "~0.31.25", "@rocket.chat/icons": "0.37.0", - "axios": "~1.6.4", + "archiver": "~7.0.1", + "axios": "~1.13.2", "detect-browsers": "~6.1.0", - "electron-dl": "3.5.2", + "electron-dl": "4.0.0", + "electron-log": "~5.4.3", "electron-store": "~8.1.0", - "electron-updater": "^5.3.0", + "electron-updater": "^6.3.9", "ews-javascript-api": "~0.13.2", "i18next": "~23.7.16", "jsonwebtoken": "~9.0.2", @@ -88,6 +94,7 @@ "@babel/preset-env": "~7.23.7", "@babel/preset-react": "~7.23.3", "@babel/preset-typescript": "~7.23.3", + "@electron/fuses": "~1.8.0", "@fiahfy/icns-convert": "~0.0.12", "@fiahfy/ico-convert": "~0.0.12", "@kayahr/jest-electron-runner": "29.14.0", @@ -98,10 +105,11 @@ "@rollup/plugin-json": "~6.1.0", "@rollup/plugin-node-resolve": "~15.2.3", "@rollup/plugin-replace": "~5.0.5", + "@types/archiver": "~7.0.0", "@types/electron-devtools-installer": "~2.2.5", "@types/jest": "~29.5.11", "@types/jsonwebtoken": "~9.0.5", - "@types/node": "~16.18.69", + "@types/node": "~25.0.10", "@types/react": "~18.3.18", "@types/react-dom": "~18.3.5", "@typescript-eslint/eslint-plugin": "~6.17.0", @@ -110,8 +118,8 @@ "chokidar": "~3.5.3", "conventional-changelog-cli": "~4.1.0", "convert-svg-to-png": "~0.6.4", - "electron": "~34.0.2", - "electron-builder": "25.1.8", + "electron": "40.0.0", + "electron-builder": "26.0.3", "electron-devtools-installer": "^3.2.0", "electron-notarize": "^1.2.2", "eslint": "~8.56.0", @@ -124,11 +132,12 @@ "jest": "~29.7.0", "jest-environment-jsdom": "~29.7.0", "jimp": "~0.22.10", + "nock": "~14.0.0", "npm-run-all": "~4.1.5", "patch-package": "~8.0.0", "prettier": "~3.2.5", "puppeteer": "23.1.1", - "rollup": "~4.9.6", + "rollup": "~4.32.0", "rollup-plugin-copy": "~3.5.0", "ts-jest": "~29.1.4", "ts-node": "~10.9.2", @@ -139,19 +148,27 @@ "fsevents": "2.3.3" }, "engines": { - "node": ">=22.13.1" + "node": ">=24.11.1" }, "devEngines": { - "node": ">=22.13.1", + "node": ">=24.11.1", "yarn": ">=4.0.2" }, "resolutions": { "@fiahfy/icns-convert/sharp": "0.29.3", - "@fiahfy/ico-convert/sharp": "0.29.3" + "@fiahfy/ico-convert/sharp": "0.29.3", + "@ewsjs/xhr@npm:^2.0.2": "patch:@ewsjs/xhr@npm%3A2.0.2#~/.yarn/patches/@ewsjs-xhr-npm-2.0.2-77506b0a6c.patch", + "cross-spawn": "7.0.6", + "braces": "3.0.3", + "ws": "8.18.0", + "follow-redirects": "1.15.9", + "form-data": "4.0.5", + "tar-fs": "3.0.8", + "undici": "5.28.5" }, "volta": { - "node": "22.13.1", - "yarn": "4.0.2" + "node": "24.11.1", + "yarn": "4.6.0" }, "packageManager": "yarn@4.6.0" } diff --git a/rollup.config.mjs b/rollup.config.mjs index 31ea4e8feb..4526a831e0 100644 --- a/rollup.config.mjs +++ b/rollup.config.mjs @@ -34,7 +34,14 @@ const run = () => { proc ? 'Restarting main process...' : 'Starting main process...' ); - proc = spawn(electron, ['.'], { stdio: 'inherit' }); + const electronArgs = ['.']; + + // Linux-specific flags for development + if (process.platform === 'linux') { + electronArgs.push('--no-sandbox'); + } + + proc = spawn(electron, electronArgs, { stdio: 'inherit' }); proc.on('close', () => { proc = null; @@ -81,7 +88,7 @@ export default [ ...Object.keys(appManifest.dependencies), ...Object.keys(appManifest.devDependencies), ].filter((moduleName) => moduleName !== '@bugsnag/js'), - input: 'src/videoCallWindow/video-call-window.tsx', + input: 'src/videoCallWindow/video-call-window.ts', preserveEntrySignatures: 'strict', plugins: [ json(), @@ -114,7 +121,7 @@ export default [ ...Object.keys(appManifest.dependencies), ...Object.keys(appManifest.devDependencies), ].filter((moduleName) => moduleName !== '@bugsnag/js'), - input: 'src/videoCallWindow/preload/index.ts', + input: 'src/logViewerWindow/log-viewer-window.tsx', preserveEntrySignatures: 'strict', plugins: [ json(), @@ -135,7 +142,40 @@ export default [ output: [ { dir: 'app', - name: 'preload', + format: 'cjs', + sourcemap: 'inline', + interop: 'auto', + }, + ], + }, + { + external: [ + ...builtinModules, + ...Object.keys(appManifest.dependencies), + ...Object.keys(appManifest.devDependencies), + ].filter((moduleName) => moduleName !== '@bugsnag/js'), + input: 'src/videoCallWindow/preload/index.ts', + preserveEntrySignatures: 'strict', + plugins: [ + json(), + replace({ + 'process.env.NODE_ENV': JSON.stringify(NODE_ENV), + 'preventAssignment': true, + }), + babel({ + babelHelpers: 'bundled', + extensions, + }), + nodeResolve({ + browser: true, + extensions, + }), + commonjs(), + ], + output: [ + { + dir: 'app/preload', + entryFileNames: 'preload.js', format: 'cjs', sourcemap: 'inline', interop: 'auto', diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000000..c6efe9553a --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,149 @@ +# Testing Scripts + +This directory contains automation scripts for testing Rocket.Chat Desktop on Linux. + +## Scripts + +### `install-volta.sh` + +Installs Volta (JavaScript toolchain manager) if it's not already installed. Volta provides node.js and npm, which are required for building the project. + +#### Usage + +```bash +# Interactive mode (prompts before installing) +./scripts/install-volta.sh + +# Non-interactive mode (installs automatically) +./scripts/install-volta.sh --non-interactive +``` + +#### What It Does + +1. **Checks if Volta is installed** + + - Looks for `volta` command in PATH + - Checks `$VOLTA_HOME` environment variable + - Checks common installation location (`~/.volta/bin/volta`) + +2. **Installs Volta if missing** + + - Downloads and runs the official Volta installer + - Adds Volta to PATH for current session + - Adds Volta to `~/.bashrc` for future sessions + +3. **Verifies installation** + - Checks that `volta` command is available + - Displays Volta version + +#### Features + +- Can be sourced by other scripts to use its functions +- Non-interactive mode for CI/automation +- Automatically adds Volta to shell profile +- Safe to run multiple times (skips if already installed) + +#### Requirements + +- `curl` (for downloading Volta installer) +- Internet connection + +### `linux-test-deb.sh` + +Builds, installs, and runs the Rocket.Chat Desktop .deb package for testing purposes. + +#### Usage + +```bash +./scripts/linux-test-deb.sh [OPTIONS] +``` + +#### Options + +- `--skip-build` - Skip building the .deb package (useful if you've already built it) +- `--skip-install` - Skip installing the .deb package (useful for testing installation separately) +- `--skip-run` - Skip running the installed app (useful if you just want to install) +- `--help`, `-h` - Show help message + +#### Examples + +Build, install, and run: + +```bash +./scripts/linux-test-deb.sh +``` + +Skip build if .deb already exists: + +```bash +./scripts/linux-test-deb.sh --skip-build +``` + +Only build and install, don't run: + +```bash +./scripts/linux-test-deb.sh --skip-run +``` + +#### What It Does + +1. **Builds the .deb package** + + - Runs `yarn build-linux` to build the app and create Linux packages + - Outputs to `dist/` directory + +2. **Finds the .deb file** + + - Searches for `dist/rocketchat-*-linux-*.deb` + - Uses the first matching file found + +3. **Installs the .deb package** + + - Uninstalls any previous version if present + - Installs the new package using `dpkg -i` + - Automatically fixes missing dependencies with `apt-get install -f` + +4. **Runs the installed app** + - Launches Rocket.Chat Desktop from `/opt/Rocket.Chat/rocketchat-desktop` + - Runs in background and displays the process ID + +#### Requirements + +- Debian/Ubuntu-based Linux +- `node.js` and `npm` (or Volta - the script will install it automatically if needed) +- Yarn is provided in-repo at `.yarn/releases/yarn-4.6.0.cjs` +- `dpkg` and `apt-get` (standard on Debian/Ubuntu systems) +- `curl` (for installing Volta if needed) +- Sudo access (for installation, if not running as root) + +#### Automatic Dependency Installation + +The script automatically installs missing dependencies: + +- **Volta** (if node.js/npm not found) - provides node.js and npm; Yarn is already bundled in the repository at `.yarn/releases/yarn-4.6.0.cjs` +- **binutils** (if `ar` command not found) - required for building .deb packages +- **Package dependencies** - resolved during .deb installation + +#### Output + +The script provides color-coded output: + +- 🔵 Blue: Informational messages +- 🟢 Green: Success messages +- 🟡 Yellow: Warnings +- 🔴 Red: Errors + +#### Error Handling + +The script will exit with a non-zero status code if any step fails. Common issues: + +- **Build fails**: Check that all dependencies are installed (`yarn install`) +- **Install fails**: Ensure you have sudo access and the .deb file is valid +- **App not found**: The installation path may differ; check `/opt/Rocket.Chat/` or use `which rocketchat-desktop` + +#### Notes + +- The script automatically handles uninstalling previous versions +- Missing dependencies are automatically resolved during installation +- The app runs in the background; use `kill ` to stop it +- This script is intended for development/testing purposes only diff --git a/scripts/install-volta.sh b/scripts/install-volta.sh new file mode 100755 index 0000000000..2549409938 --- /dev/null +++ b/scripts/install-volta.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print colored messages +info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if Volta is already installed +check_volta_installed() { + if command -v volta &> /dev/null; then + return 0 + fi + + # Also check if VOLTA_HOME is set and points to a valid installation + if [ -n "$VOLTA_HOME" ] && [ -f "$VOLTA_HOME/bin/volta" ]; then + return 0 + fi + + # Check common installation location + if [ -f "$HOME/.volta/bin/volta" ]; then + return 0 + fi + + return 1 +} + +# Install Volta +install_volta() { + info "Installing Volta..." + + # Check if curl is available + if ! command -v curl &> /dev/null; then + error "curl is required to install Volta" + error "Please install curl first: sudo apt-get install curl" + return 1 + fi + + # Install Volta using the official installer + if ! curl https://get.volta.sh | bash; then + error "Failed to install Volta" + return 1 + fi + + # Source Volta in current shell + export VOLTA_HOME="$HOME/.volta" + export PATH="$VOLTA_HOME/bin:$PATH" + + # Verify installation + if command -v volta &> /dev/null; then + success "Volta installed successfully!" + volta --version + return 0 + else + error "Volta installation completed but 'volta' command not found" + error "You may need to restart your shell or run: source ~/.bashrc" + return 1 + fi +} + +# Main function +main() { + # Check if running in non-interactive mode + NON_INTERACTIVE=false + if [ "$1" = "--non-interactive" ] || [ -n "$CI" ]; then + NON_INTERACTIVE=true + fi + + # Check if Volta is already installed + if check_volta_installed; then + VOLTA_VERSION=$(volta --version 2>/dev/null || echo "installed") + success "Volta is already installed ($VOLTA_VERSION)" + + # Ensure Volta is in PATH + if [ -d "$HOME/.volta/bin" ] && [[ "$PATH" != *".volta"* ]]; then + export VOLTA_HOME="$HOME/.volta" + export PATH="$VOLTA_HOME/bin:$PATH" + info "Added Volta to PATH" + fi + + return 0 + fi + + # Volta not installed, ask user or install automatically + if [ "$NON_INTERACTIVE" = false ]; then + echo "" + warning "Volta is not installed." + echo "Volta is a JavaScript toolchain manager that provides node.js and npm." + echo "" + read -p "Do you want to install Volta now? (y/n) " -n 1 -r + echo "" + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + info "Skipping Volta installation" + return 1 + fi + else + info "Volta not found, installing automatically..." + fi + + # Install Volta + if install_volta; then + # Ensure it's in PATH for subsequent commands + export VOLTA_HOME="$HOME/.volta" + export PATH="$VOLTA_HOME/bin:$PATH" + + # Add to shell profile if not already there + if [ -f "$HOME/.bashrc" ] && ! grep -q "VOLTA_HOME" "$HOME/.bashrc"; then + info "Adding Volta to ~/.bashrc for future sessions..." + echo '' >> "$HOME/.bashrc" + echo '# Volta' >> "$HOME/.bashrc" + echo 'export VOLTA_HOME="$HOME/.volta"' >> "$HOME/.bashrc" + echo 'export PATH="$VOLTA_HOME/bin:$PATH"' >> "$HOME/.bashrc" + fi + + return 0 + else + return 1 + fi +} + +# If script is executed directly (not sourced), run main +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" + exit $? +fi + +# If sourced, export functions for use in other scripts +export -f check_volta_installed +export -f install_volta + diff --git a/scripts/linux-test-appimage.sh b/scripts/linux-test-appimage.sh new file mode 100755 index 0000000000..def5bf126c --- /dev/null +++ b/scripts/linux-test-appimage.sh @@ -0,0 +1,231 @@ +#!/bin/bash + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +cd "$PROJECT_DIR" + +echo -e "${BLUE}=== Rocket.Chat Linux AppImage Testing Script ===${NC}\n" + +# Check if running on Linux +if [[ "$OSTYPE" != "linux-gnu"* ]]; then + echo -e "${RED}Error: This script is designed for Linux only${NC}" + exit 1 +fi + +# Function to print colored messages +info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Parse command line arguments +SKIP_BUILD=false +SKIP_INSTALL=false +SKIP_RUN=false + +while [[ $# -gt 0 ]]; do + case $1 in + --skip-build) + SKIP_BUILD=true + shift + ;; + --skip-install) + SKIP_INSTALL=true + shift + ;; + --skip-run) + SKIP_RUN=true + shift + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --skip-build Skip building the AppImage" + echo " --skip-install Skip making AppImage executable" + echo " --skip-run Skip running the AppImage" + echo " --help, -h Show this help message" + exit 0 + ;; + *) + error "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done + +# Step 1: Build the AppImage +if [ "$SKIP_BUILD" = false ]; then + info "Building Linux AppImage..." + + # Ensure Volta is installed (provides node.js) + if ! command -v node &> /dev/null && ! command -v yarn &> /dev/null; then + info "Checking for Volta (JavaScript toolchain manager)..." + if [ -f "$SCRIPT_DIR/install-volta.sh" ]; then + # Source the install script to get its functions, or run it + bash "$SCRIPT_DIR/install-volta.sh" --non-interactive || { + warning "Volta installation skipped or failed" + warning "You may need node.js/yarn installed manually" + } + # Ensure Volta is in PATH + if [ -d "$HOME/.volta/bin" ]; then + export PATH="$HOME/.volta/bin:$PATH" + fi + fi + fi + + # Find yarn command + YARN_CMD="" + if command -v yarn &> /dev/null; then + YARN_CMD="yarn" + elif [ -f "$PROJECT_DIR/.yarn/releases/yarn-4.6.0.cjs" ]; then + # Use local yarn binary if available + if command -v node &> /dev/null; then + YARN_CMD="node $PROJECT_DIR/.yarn/releases/yarn-4.6.0.cjs" + else + error "Neither 'yarn' nor 'node' is available. Please install yarn or node." + exit 1 + fi + else + error "yarn is not installed and local yarn binary not found." + error "Please install yarn or ensure .yarn/releases/yarn-*.cjs exists." + exit 1 + fi + + info "Using yarn: $YARN_CMD" + + # Build AppImage + info "Building AppImage..." + if ! $YARN_CMD build; then + error "Build failed!" + exit 1 + fi + if ! $YARN_CMD electron-builder --publish never --linux AppImage; then + error "Build failed!" + exit 1 + fi + + success "Build completed!" +else + info "Skipping build (--skip-build flag set)" +fi + +# Step 2: Find the AppImage file +info "Looking for AppImage in dist/ directory..." + +APPIMAGE_FILE=$(find dist -name "rocketchat-*-linux-*.AppImage" -type f | head -n 1) + +if [ -z "$APPIMAGE_FILE" ]; then + error "No AppImage file found in dist/ directory" + error "Expected pattern: dist/rocketchat-*-linux-*.AppImage" + exit 1 +fi + +success "Found AppImage: $APPIMAGE_FILE" + +# Step 3: Make the AppImage executable +if [ "$SKIP_INSTALL" = false ]; then + info "Making AppImage executable..." + + chmod +x "$APPIMAGE_FILE" + + success "AppImage is now executable!" +else + info "Skipping making executable (--skip-install flag set)" +fi + +# Step 4: Run the AppImage +if [ "$SKIP_RUN" = false ]; then + info "Launching Rocket.Chat from AppImage..." + + # Kill any existing Rocket.Chat instances to ensure clean start + EXISTING_PIDS=$(pgrep -f "rocketchat.*AppImage" 2>/dev/null || true) + if [ -n "$EXISTING_PIDS" ]; then + warning "Found existing Rocket.Chat processes, stopping them..." + echo "$EXISTING_PIDS" | xargs kill -9 2>/dev/null || true + sleep 1 + fi + + # Clear saved window state to ensure window appears + ROCKET_CONFIG_DIR="$HOME/.config/Rocket.Chat" + if [ -f "$ROCKET_CONFIG_DIR/rootWindowState.json" ]; then + info "Clearing saved window state to ensure window appears..." + rm -f "$ROCKET_CONFIG_DIR/rootWindowState.json" + fi + + success "Starting Rocket.Chat from: $APPIMAGE_FILE" + + # Ensure DISPLAY is set for GUI apps + if [ -z "$DISPLAY" ]; then + export DISPLAY=:0 + info "Setting DISPLAY=$DISPLAY" + fi + + # Launch the AppImage directly (app handles X11 enforcement internally) + nohup "$APPIMAGE_FILE" > /tmp/rocketchat-appimage.log 2>&1 & + + # Get the PID + sleep 1 + APP_PID=$(pgrep -f "rocketchat.*AppImage" | head -1) + + if [ -z "$APP_PID" ]; then + error "Failed to start Rocket.Chat" + if [ -f /tmp/rocketchat-appimage.log ]; then + error "Last 10 lines of log:" + tail -10 /tmp/rocketchat-appimage.log | sed 's/^/ /' + fi + exit 1 + fi + + # Wait for window to appear + info "Waiting for window to appear..." + sleep 5 + + # Give the app a moment to start and show the window + sleep 3 + + # Check if the process is still running + if ! kill -0 "$APP_PID" 2>/dev/null; then + warning "App process exited quickly. Check logs: /tmp/rocketchat-appimage.log" + if [ -f /tmp/rocketchat-appimage.log ]; then + error "Last 10 lines of log:" + tail -10 /tmp/rocketchat-appimage.log | sed 's/^/ /' + fi + warning "Testing completed with warnings - app exited early" + else + echo "" + success "Rocket.Chat started! (PID: $APP_PID)" + info "The app window should be visible now." + info "Logs are available at: /tmp/rocketchat-appimage.log" + info "To stop it, run: kill $APP_PID" + echo "" + success "All steps completed successfully!" + fi +else + info "Skipping run (--skip-run flag set)" + success "All steps completed successfully!" +fi + diff --git a/scripts/linux-test-deb.sh b/scripts/linux-test-deb.sh new file mode 100755 index 0000000000..c01ed3c081 --- /dev/null +++ b/scripts/linux-test-deb.sh @@ -0,0 +1,313 @@ +#!/bin/bash + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +cd "$PROJECT_DIR" + +echo -e "${BLUE}=== Rocket.Chat Linux .deb Testing Script ===${NC}\n" + +# Check if running on Linux +if [[ "$OSTYPE" != "linux-gnu"* ]]; then + echo -e "${RED}Error: This script is designed for Linux only${NC}" + exit 1 +fi + +# Check if we need sudo for install operations +# dpkg operations always require root, so use sudo unless we're already root +NEED_SUDO=false +if [ "$EUID" -ne 0 ]; then + NEED_SUDO=true +fi + +# Function to print colored messages +info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Parse command line arguments +SKIP_BUILD=false +SKIP_INSTALL=false +SKIP_RUN=false + +while [[ $# -gt 0 ]]; do + case $1 in + --skip-build) + SKIP_BUILD=true + shift + ;; + --skip-install) + SKIP_INSTALL=true + shift + ;; + --skip-run) + SKIP_RUN=true + shift + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --skip-build Skip building the .deb package" + echo " --skip-install Skip installing the .deb package" + echo " --skip-run Skip running the installed app" + echo " --help, -h Show this help message" + exit 0 + ;; + *) + error "Unknown option: $1" + echo "Use --help for usage information" + exit 1 + ;; + esac +done + +# Step 1: Build the .deb package +if [ "$SKIP_BUILD" = false ]; then + info "Building Linux .deb package..." + + # Ensure Volta is installed (provides node.js) + if ! command -v node &> /dev/null && ! command -v yarn &> /dev/null; then + info "Checking for Volta (JavaScript toolchain manager)..." + if [ -f "$SCRIPT_DIR/install-volta.sh" ]; then + # Source the install script to get its functions, or run it + bash "$SCRIPT_DIR/install-volta.sh" --non-interactive || { + warning "Volta installation skipped or failed" + warning "You may need node.js/yarn installed manually" + } + # Ensure Volta is in PATH + if [ -d "$HOME/.volta/bin" ]; then + export PATH="$HOME/.volta/bin:$PATH" + fi + fi + fi + + # Check for required build dependencies and install if missing + MISSING_DEPS=() + if ! command -v ar &> /dev/null; then + MISSING_DEPS+=("binutils") + fi + + if [ ${#MISSING_DEPS[@]} -gt 0 ]; then + warning "Missing required build dependencies: ${MISSING_DEPS[*]}" + info "Attempting to install missing dependencies..." + + if [ "$NEED_SUDO" = true ]; then + if ! sudo apt-get update || ! sudo apt-get install -y "${MISSING_DEPS[@]}"; then + error "Failed to install dependencies: ${MISSING_DEPS[*]}" + error "Please install them manually with: sudo apt-get install ${MISSING_DEPS[*]}" + exit 1 + fi + else + if ! apt-get update || ! apt-get install -y "${MISSING_DEPS[@]}"; then + error "Failed to install dependencies: ${MISSING_DEPS[*]}" + error "Please install them manually with: sudo apt-get install ${MISSING_DEPS[*]}" + exit 1 + fi + fi + + success "Dependencies installed successfully!" + fi + + # Find yarn command + YARN_CMD="" + if command -v yarn &> /dev/null; then + YARN_CMD="yarn" + elif [ -f "$PROJECT_DIR/.yarn/releases/yarn-4.6.0.cjs" ]; then + # Use local yarn binary if available + if command -v node &> /dev/null; then + YARN_CMD="node $PROJECT_DIR/.yarn/releases/yarn-4.6.0.cjs" + else + error "Neither 'yarn' nor 'node' is available. Please install yarn or node." + exit 1 + fi + else + error "yarn is not installed and local yarn binary not found." + error "Please install yarn or ensure .yarn/releases/yarn-*.cjs exists." + exit 1 + fi + + info "Using yarn: $YARN_CMD" + + # Build only .deb package to avoid issues with other Linux targets + info "Building .deb package only..." + if ! $YARN_CMD build; then + error "Build failed!" + exit 1 + fi + if ! $YARN_CMD electron-builder --publish never --linux deb; then + error "Build failed!" + exit 1 + fi + + success "Build completed!" +else + info "Skipping build (--skip-build flag set)" +fi + +# Step 2: Find the .deb file +info "Looking for .deb package in dist/ directory..." + +DEB_FILE=$(find dist -name "rocketchat-*-linux-*.deb" -type f | head -n 1) + +if [ -z "$DEB_FILE" ]; then + error "No .deb file found in dist/ directory" + error "Expected pattern: dist/rocketchat-*-linux-*.deb" + exit 1 +fi + +success "Found .deb package: $DEB_FILE" + +# Step 3: Install the .deb package +if [ "$SKIP_INSTALL" = false ]; then + info "Installing .deb package..." + + # Check if Rocket.Chat is already installed + if dpkg -l | grep -q "rocketchat-desktop"; then + warning "Rocket.Chat is already installed. Uninstalling previous version..." + + if [ "$NEED_SUDO" = true ]; then + sudo dpkg -r rocketchat-desktop 2>/dev/null || true + sudo apt-get purge -y rocketchat-desktop 2>/dev/null || true + else + dpkg -r rocketchat-desktop 2>/dev/null || true + apt-get purge -y rocketchat-desktop 2>/dev/null || true + fi + fi + + # Install the new package + if [ "$NEED_SUDO" = true ]; then + set +e + sudo dpkg -i "$DEB_FILE" + dpkg_rc=$? + set -e + if [ $dpkg_rc -ne 0 ]; then + warning "Installation had dependency issues, attempting to fix..." + sudo apt-get install -f -y + fi + else + set +e + dpkg -i "$DEB_FILE" + dpkg_rc=$? + set -e + if [ $dpkg_rc -ne 0 ]; then + warning "Installation had dependency issues, attempting to fix..." + apt-get install -f -y + fi + fi + + success "Package installed successfully!" +else + info "Skipping install (--skip-install flag set)" +fi + +# Step 4: Run the installed app +if [ "$SKIP_RUN" = false ]; then + info "Launching Rocket.Chat..." + + # Kill any existing Rocket.Chat instances to ensure clean start + EXISTING_PIDS=$(pgrep -f "rocketchat-desktop" 2>/dev/null || true) + if [ -n "$EXISTING_PIDS" ]; then + warning "Found existing Rocket.Chat processes, stopping them..." + echo "$EXISTING_PIDS" | xargs kill -9 2>/dev/null || true + sleep 1 + fi + + # Clear saved window state to ensure window appears + # The app might have saved a "hidden" state + ROCKET_CONFIG_DIR="$HOME/.config/Rocket.Chat" + if [ -f "$ROCKET_CONFIG_DIR/rootWindowState.json" ]; then + info "Clearing saved window state to ensure window appears..." + rm -f "$ROCKET_CONFIG_DIR/rootWindowState.json" + fi + + # Standard Electron app install path + APP_PATH="/opt/Rocket.Chat/rocketchat-desktop" + + if [ ! -f "$APP_PATH" ]; then + error "App not found at expected path: $APP_PATH" + error "Trying alternative: rocketchat-desktop command" + + if command -v rocketchat-desktop &> /dev/null; then + APP_PATH="rocketchat-desktop" + else + error "Could not find installed Rocket.Chat executable" + exit 1 + fi + fi + + success "Starting Rocket.Chat from: $APP_PATH" + + # Ensure DISPLAY is set for GUI apps + if [ -z "$DISPLAY" ]; then + export DISPLAY=:0 + info "Setting DISPLAY=$DISPLAY" + fi + + # Launch the app directly (app handles X11 enforcement internally) + nohup "$APP_PATH" > /tmp/rocketchat-desktop.log 2>&1 & + + # Get the PID + sleep 1 + APP_PID=$(pgrep -f "rocketchat-desktop" | head -1) + + if [ -z "$APP_PID" ]; then + error "Failed to start Rocket.Chat" + if [ -f /tmp/rocketchat-desktop.log ]; then + error "Last 10 lines of log:" + tail -10 /tmp/rocketchat-desktop.log | sed 's/^/ /' + fi + exit 1 + fi + + # Wait for window to appear + info "Waiting for window to appear..." + sleep 5 + + # Give the app a moment to start and show the window + sleep 3 + + # Check if the process is still running + if ! kill -0 "$APP_PID" 2>/dev/null; then + warning "App process exited quickly. Check logs: /tmp/rocketchat-desktop.log" + if [ -f /tmp/rocketchat-desktop.log ]; then + error "Last 10 lines of log:" + tail -10 /tmp/rocketchat-desktop.log | sed 's/^/ /' + fi + warning "Testing completed with warnings - app exited early" + else + echo "" + success "Rocket.Chat started! (PID: $APP_PID)" + info "The app window should be visible now." + info "Logs are available at: /tmp/rocketchat-desktop.log" + info "To stop it, run: kill $APP_PID" + echo "" + success "All steps completed successfully!" + fi +else + info "Skipping run (--skip-run flag set)" + success "All steps completed successfully!" +fi + diff --git a/scripts/release-tag.ts b/scripts/release-tag.ts new file mode 100644 index 0000000000..00813168a1 --- /dev/null +++ b/scripts/release-tag.ts @@ -0,0 +1,178 @@ +import { execSync, execFileSync } from 'child_process'; +import { createInterface } from 'readline'; +import { parse, gt, prerelease, SemVer } from 'semver'; +import { readFileSync } from 'fs'; +import { join } from 'path'; + +const REPO_URL = 'https://github.com/RocketChat/Rocket.Chat.Electron'; + +const getVersion = (): string => { + const packageJson = JSON.parse( + readFileSync(join(__dirname, '..', 'package.json'), 'utf-8') + ); + return packageJson.version; +}; + +const getChannel = (version: SemVer): string => { + const pre = prerelease(version); + if (!pre || pre.length === 0) return 'stable'; + if (pre[0] === 'alpha') return 'alpha'; + if (pre[0] === 'beta') return 'beta'; + if (pre[0] === 'rc' || pre[0] === 'candidate') return 'candidate'; + return 'prerelease'; +}; + +const exec = (cmd: string): string | null => { + try { + return execSync(cmd, { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }).trim(); + } catch { + return null; + } +}; + +const fetchTags = (): void => { + console.log('Fetching tags from remote...'); + execSync('git fetch --tags', { stdio: 'inherit' }); +}; + +const normalizeTag = (tag: string): string => { + // Strip leading 'v' if present for consistent comparison + return tag.startsWith('v') ? tag.slice(1) : tag; +}; + +const getExistingTags = (): string[] => { + const output = exec('git tag -l'); + if (output === null) { + console.error(' Warning: Failed to list git tags'); + return []; + } + if (!output) return []; + // Return normalized tags (without 'v' prefix) for consistent comparison + return output.split('\n').filter(Boolean).map(normalizeTag); +}; + +const getLatestTagForChannel = ( + tags: string[], + channel: string +): SemVer | null => { + const channelTags = tags + .map((tag) => parse(tag)) + .filter((v): v is SemVer => v !== null) + .filter((v) => getChannel(v) === channel) + .sort((a, b) => (gt(a, b) ? -1 : 1)); + + return channelTags[0] || null; +}; + +const prompt = (question: string): Promise => { + const rl = createInterface({ + input: process.stdin, + output: process.stdout, + }); + + return new Promise((resolve) => { + rl.question(question, (answer) => { + rl.close(); + resolve(answer.trim().toLowerCase()); + }); + }); +}; + +const main = async (): Promise => { + console.log('\n Release Tag Creator\n'); + + // 1. Read version from package.json + const versionString = getVersion(); + const version = parse(versionString); + + if (!version) { + console.error(`Error: Invalid version in package.json: ${versionString}`); + process.exit(1); + } + + // 2. Detect channel + const channel = getChannel(version); + + console.log(` Version: ${version.version}`); + console.log(` Channel: ${channel}`); + console.log(` Tag: ${version.version}`); + console.log(''); + + // 3. Fetch tags + fetchTags(); + + // 4. Check if tag already exists + const existingTags = getExistingTags(); + + if (existingTags.includes(version.version)) { + console.error(`\n Error: Tag ${version.version} already exists!`); + console.error(` The version in package.json has already been released.`); + console.error(` Please bump the version before creating a new release.\n`); + process.exit(1); + } + + console.log(` Tag does not exist yet`); + + // 5. Check if version is newer than latest in channel + const latestInChannel = getLatestTagForChannel(existingTags, channel); + + if (latestInChannel && !gt(version, latestInChannel)) { + console.warn(`\n Warning: Version ${version.version} is not greater than`); + console.warn( + ` the latest ${channel} release (${latestInChannel.version}).` + ); + console.warn(` This may be intentional, but please verify.\n`); + } else if (latestInChannel) { + console.log(` Latest ${channel}: ${latestInChannel.version}`); + } else { + console.log(` First ${channel} release`); + } + + // 6. Show confirmation + console.log('\n This will:'); + console.log(` 1. Create git tag: ${version.version}`); + console.log(` 2. Push tag to origin`); + console.log(` 3. Trigger GitHub Actions build-release workflow\n`); + + const answer = await prompt(' Proceed? (y/N): '); + + if (answer !== 'y' && answer !== 'yes') { + console.log('\n Aborted.\n'); + process.exit(0); + } + + // 7. Create and push tag + console.log(`\n Creating tag ${version.version}...`); + try { + execFileSync('git', ['tag', '--', version.version], { stdio: 'inherit' }); + } catch { + console.error(` Error: Failed to create tag`); + process.exit(1); + } + + console.log(` Pushing tag to origin...`); + try { + execFileSync('git', ['push', 'origin', `refs/tags/${version.version}`], { + stdio: 'inherit', + }); + } catch { + console.error(` Error: Failed to push tag`); + console.error( + ` The local tag was created. You may need to push it manually.` + ); + process.exit(1); + } + + // 8. Success message + console.log(`\n Tag created and pushed successfully!\n`); + console.log(` Monitor build: ${REPO_URL}/actions`); + console.log(` Releases: ${REPO_URL}/releases\n`); +}; + +main().catch((error) => { + console.error('Unexpected error:', error); + process.exit(1); +}); diff --git a/src/app/PersistableValues.ts b/src/app/PersistableValues.ts index e987f3fdf6..1fe354ec3b 100644 --- a/src/app/PersistableValues.ts +++ b/src/app/PersistableValues.ts @@ -75,9 +75,40 @@ type PersistableValues_4_2_0 = PersistableValues_4_1_0 & { selectedBrowser: string | null; }; +type PersistableValues_4_4_0 = PersistableValues_4_2_0 & { + isDeveloperModeEnabled: boolean; +}; + +type PersistableValues_4_5_0 = PersistableValues_4_4_0 & { + updateChannel: string; +}; + +type PersistableValues_4_7_2 = PersistableValues_4_5_0 & { + isVideoCallDevtoolsAutoOpenEnabled: boolean; +}; + +type PersistableValues_4_9_0 = PersistableValues_4_7_2 & { + isTransparentWindowEnabled: boolean; + isVideoCallScreenCaptureFallbackEnabled: boolean; +}; + +type PersistableValues_4_10_0 = PersistableValues_4_9_0 & { + userThemePreference: 'auto' | 'light' | 'dark'; +}; + +type PersistableValues_4_11_0 = PersistableValues_4_10_0 & { + outlookCalendarSyncInterval: number; + isVerboseOutlookLoggingEnabled: boolean; + isDetailedEventsLoggingEnabled: boolean; +}; + +type PersistableValues_4_13_0 = PersistableValues_4_11_0 & { + isDebugLoggingEnabled: boolean; +}; + export type PersistableValues = Pick< - PersistableValues_4_2_0, - keyof PersistableValues_4_2_0 + PersistableValues_4_13_0, + keyof PersistableValues_4_13_0 >; export const migrations = { @@ -139,4 +170,35 @@ export const migrations = { ...before, selectedBrowser: null, }), + '>=4.4.0': (before: PersistableValues_4_2_0): PersistableValues_4_4_0 => ({ + ...before, + isDeveloperModeEnabled: false, + }), + '>=4.5.0': (before: PersistableValues_4_4_0): PersistableValues_4_5_0 => ({ + ...before, + updateChannel: 'latest', + }), + '>=4.7.2': (before: PersistableValues_4_5_0): PersistableValues_4_7_2 => ({ + ...before, + isVideoCallDevtoolsAutoOpenEnabled: false, + }), + '>=4.9.0': (before: PersistableValues_4_7_2): PersistableValues_4_9_0 => ({ + ...before, + isTransparentWindowEnabled: false, + isVideoCallScreenCaptureFallbackEnabled: false, + }), + '>=4.10.0': (before: PersistableValues_4_9_0): PersistableValues_4_10_0 => ({ + ...before, + userThemePreference: 'auto', + }), + '>=4.11.0': (before: PersistableValues_4_10_0): PersistableValues_4_11_0 => ({ + ...before, + outlookCalendarSyncInterval: 60, + isVerboseOutlookLoggingEnabled: false, + isDetailedEventsLoggingEnabled: false, + }), + '>=4.13.0': (before: PersistableValues_4_11_0): PersistableValues_4_13_0 => ({ + ...before, + isDebugLoggingEnabled: false, + }), }; diff --git a/src/app/actions.ts b/src/app/actions.ts index 7a9079f20f..12831f10a0 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -8,13 +8,21 @@ export const APP_ALLOWED_NTLM_CREDENTIALS_DOMAINS_SET = 'app/allowed-ntlm-credentials-domains-set'; export const APP_MAIN_WINDOW_TITLE_SET = 'app/main-window-title-set'; export const APP_MACHINE_THEME_SET = 'app/machine-theme-set'; +export const APP_SCREEN_CAPTURE_FALLBACK_FORCED_SET = + 'app/screen-capture-fallback-forced-set'; + +export type OverrideOnlySettings = { + allowInsecureOutlookConnections?: boolean; + outlookCalendarSyncIntervalOverride?: number | null; +}; export type AppActionTypeToPayloadMap = { [APP_ERROR_THROWN]: Error; [APP_PATH_SET]: string; [APP_VERSION_SET]: string; - [APP_SETTINGS_LOADED]: Partial; + [APP_SETTINGS_LOADED]: Partial & OverrideOnlySettings; [APP_ALLOWED_NTLM_CREDENTIALS_DOMAINS_SET]: string; [APP_MAIN_WINDOW_TITLE_SET]: string; [APP_MACHINE_THEME_SET]: string; + [APP_SCREEN_CAPTURE_FALLBACK_FORCED_SET]: boolean; }; diff --git a/src/app/main/app.main.spec.ts b/src/app/main/app.main.spec.ts new file mode 100644 index 0000000000..8fffea9a6f --- /dev/null +++ b/src/app/main/app.main.spec.ts @@ -0,0 +1,507 @@ +import * as fs from 'fs'; + +import { app } from 'electron'; + +import { performElectronStartup } from './app'; + +jest.mock('fs'); + +jest.mock('electron', () => ({ + app: { + setAsDefaultProtocolClient: jest.fn(), + setAppUserModelId: jest.fn(), + commandLine: { + appendSwitch: jest.fn(), + hasSwitch: jest.fn(), + }, + disableHardwareAcceleration: jest.fn(), + requestSingleInstanceLock: jest.fn(() => true), + getPath: jest.fn(() => '/test/path'), + isPackaged: false, + }, +})); + +jest.mock('../../store/readSetting', () => ({ + readSetting: jest.fn(() => undefined), +})); + +jest.mock('rimraf', () => ({ + rimraf: { + sync: jest.fn(), + }, +})); + +const originalPlatform = process.platform; +const originalEnv = process.env; +const originalArgv = process.argv; +const originalConsoleLog = console.log; + +describe('performElectronStartup - Platform Detection', () => { + let appendSwitchMock: jest.Mock; + let consoleLogMock: jest.Mock; + let statSyncMock: jest.Mock; + + beforeEach(() => { + jest.clearAllMocks(); + appendSwitchMock = app.commandLine.appendSwitch as jest.Mock; + consoleLogMock = jest.fn(); + console.log = consoleLogMock; + + statSyncMock = fs.statSync as jest.Mock; + statSyncMock.mockReturnValue({ isSocket: () => true }); + + Object.defineProperty(process, 'platform', { + value: 'linux', + writable: true, + configurable: true, + }); + process.env = { ...originalEnv }; + process.argv = ['node', 'script.js']; + + delete process.env.XDG_SESSION_TYPE; + delete process.env.WAYLAND_DISPLAY; + delete process.env.DISPLAY; + process.env.XDG_RUNTIME_DIR = '/run/user/1000'; + }); + + afterEach(() => { + Object.defineProperty(process, 'platform', { + value: originalPlatform, + writable: true, + configurable: true, + }); + process.env = originalEnv; + process.argv = originalArgv; + console.log = originalConsoleLog; + }); + + describe('Manual Override Detection', () => { + it('should respect --ozone-platform=x11 flag', () => { + process.argv = ['node', 'script.js', '--ozone-platform=x11']; + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + + performElectronStartup(); + + const ozonePlatformCalls = appendSwitchMock.mock.calls.filter( + (call) => call[0] === 'ozone-platform' + ); + expect(ozonePlatformCalls).toHaveLength(0); + }); + + it('should respect --ozone-platform=wayland flag', () => { + process.argv = ['node', 'script.js', '--ozone-platform=wayland']; + process.env.XDG_SESSION_TYPE = 'x11'; + + performElectronStartup(); + + const ozonePlatformCalls = appendSwitchMock.mock.calls.filter( + (call) => call[0] === 'ozone-platform' + ); + expect(ozonePlatformCalls).toHaveLength(0); + }); + + it('should respect --ozone-platform=auto flag', () => { + process.argv = ['node', 'script.js', '--ozone-platform=auto']; + process.env.XDG_SESSION_TYPE = 'x11'; + + performElectronStartup(); + + const ozonePlatformCalls = appendSwitchMock.mock.calls.filter( + (call) => call[0] === 'ozone-platform' + ); + expect(ozonePlatformCalls).toHaveLength(0); + }); + + it('should not override when manual flag is present', () => { + process.argv = ['node', 'script.js', '--ozone-platform=x11']; + delete process.env.XDG_SESSION_TYPE; + delete process.env.WAYLAND_DISPLAY; + + performElectronStartup(); + + const waylandLogs = consoleLogMock.mock.calls.filter((call) => + call[0]?.includes('Wayland') + ); + const x11Logs = consoleLogMock.mock.calls.filter((call) => + call[0]?.includes('X11') + ); + expect(waylandLogs).toHaveLength(0); + expect(x11Logs).toHaveLength(0); + }); + + // Note: ELECTRON_OZONE_PLATFORM_HINT tests removed - env var deprecated in Electron 38 + }); + + describe('Wayland Detection', () => { + it('should use Wayland when XDG_SESSION_TYPE=wayland AND WAYLAND_DISPLAY is set', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + + performElectronStartup(); + + const ozonePlatformCalls = appendSwitchMock.mock.calls.filter( + (call) => call[0] === 'ozone-platform' + ); + expect(ozonePlatformCalls).toHaveLength(0); + + const waylandLog = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Using Wayland platform') + ); + expect(waylandLog).toBeDefined(); + expect(JSON.parse(waylandLog[1])).toEqual({ + sessionType: 'wayland', + waylandDisplay: 'wayland-0', + }); + }); + + it('should force X11 when XDG_SESSION_TYPE=wayland but WAYLAND_DISPLAY is unset', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + delete process.env.WAYLAND_DISPLAY; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('no-wayland-display'); + expect(logData.sessionType).toBe('wayland'); + expect(logData.waylandDisplay).toBe('unset'); + }); + + it('should force X11 when XDG_SESSION_TYPE=wayland but WAYLAND_DISPLAY is empty string', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = ''; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('no-wayland-display'); + }); + + it('should force X11 when XDG_SESSION_TYPE=wayland but WAYLAND_DISPLAY is whitespace only', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = ' '; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('no-wayland-display'); + }); + + it('should force X11 when Wayland socket does not exist', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + statSyncMock.mockImplementation(() => { + throw new Error('ENOENT'); + }); + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('socket-not-found'); + }); + + it('should force X11 when path exists but is not a socket', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + statSyncMock.mockReturnValue({ isSocket: () => false }); + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('socket-not-found'); + }); + }); + + describe('X11 Detection', () => { + it('should force X11 when XDG_SESSION_TYPE=x11', () => { + process.env.XDG_SESSION_TYPE = 'x11'; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('x11-session'); + expect(logData.sessionType).toBe('x11'); + }); + + it('should force X11 when XDG_SESSION_TYPE is unset', () => { + delete process.env.XDG_SESSION_TYPE; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('no-session-type'); + expect(logData.sessionType).toBe('unset'); + }); + + it('should force X11 when XDG_SESSION_TYPE is empty string', () => { + process.env.XDG_SESSION_TYPE = ''; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('no-session-type'); + }); + }); + + describe('Invalid Session Type Handling', () => { + it('should force X11 when XDG_SESSION_TYPE=tty', () => { + process.env.XDG_SESSION_TYPE = 'tty'; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('invalid-session-type'); + expect(logData.sessionType).toBe('tty'); + }); + + it('should force X11 when XDG_SESSION_TYPE=mir', () => { + process.env.XDG_SESSION_TYPE = 'mir'; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('invalid-session-type'); + }); + + it('should force X11 for any invalid session type', () => { + process.env.XDG_SESSION_TYPE = 'unknown'; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.reason).toBe('invalid-session-type'); + expect(logData.sessionType).toBe('unknown'); + }); + }); + + describe('Normalization', () => { + it('should trim whitespace from XDG_SESSION_TYPE', () => { + process.env.XDG_SESSION_TYPE = ' wayland '; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + + performElectronStartup(); + + const waylandLog = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Using Wayland platform') + ); + expect(waylandLog).toBeDefined(); + const logData = JSON.parse(waylandLog[1]); + expect(logData.sessionType).toBe('wayland'); + }); + + it('should trim whitespace from WAYLAND_DISPLAY', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = ' wayland-0 '; + + performElectronStartup(); + + const waylandLog = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Using Wayland platform') + ); + expect(waylandLog).toBeDefined(); + const logData = JSON.parse(waylandLog[1]); + expect(logData.waylandDisplay).toBe('wayland-0'); + }); + + it('should handle XDG_SESSION_TYPE with leading/trailing spaces', () => { + process.env.XDG_SESSION_TYPE = ' x11 '; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + const logData = JSON.parse(x11Log[1]); + expect(logData.sessionType).toBe('x11'); + }); + + it('should handle WAYLAND_DISPLAY with leading/trailing spaces', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = ' wayland-0 '; + + performElectronStartup(); + + const waylandLog = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Using Wayland platform') + ); + expect(waylandLog).toBeDefined(); + const logData = JSON.parse(waylandLog[1]); + expect(logData.waylandDisplay).toBe('wayland-0'); + }); + }); + + describe('Logging', () => { + it('should log messages with valid JSON structure', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + + performElectronStartup(); + + const waylandLog = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Using Wayland platform') + ); + expect(waylandLog).toBeDefined(); + expect(waylandLog).toHaveLength(2); + expect(typeof waylandLog[0]).toBe('string'); + const logData = JSON.parse(waylandLog[1]); + expect(logData).toHaveProperty('sessionType'); + expect(logData).toHaveProperty('waylandDisplay'); + }); + + it('should log X11 messages with reason code in JSON', () => { + process.env.XDG_SESSION_TYPE = 'x11'; + + performElectronStartup(); + + const x11Log = consoleLogMock.mock.calls.find((call) => + call[0]?.includes('Forcing X11 platform') + ); + expect(x11Log).toBeDefined(); + expect(x11Log).toHaveLength(2); + expect(typeof x11Log[0]).toBe('string'); + const logData = JSON.parse(x11Log[1]); + expect(logData).toHaveProperty('reason'); + expect(logData).toHaveProperty('sessionType'); + }); + }); + + describe('Platform-Specific Behavior', () => { + it('should only run detection on Linux platform', () => { + Object.defineProperty(process, 'platform', { + value: 'darwin', + writable: true, + configurable: true, + }); + process.env.XDG_SESSION_TYPE = 'x11'; + + performElectronStartup(); + + const ozonePlatformCalls = appendSwitchMock.mock.calls.filter( + (call) => call[0] === 'ozone-platform' + ); + expect(ozonePlatformCalls).toHaveLength(0); + }); + + it('should not affect non-Linux platforms', () => { + Object.defineProperty(process, 'platform', { + value: 'win32', + writable: true, + configurable: true, + }); + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + + performElectronStartup(); + + const ozonePlatformCalls = appendSwitchMock.mock.calls.filter( + (call) => call[0] === 'ozone-platform' + ); + expect(ozonePlatformCalls).toHaveLength(0); + }); + }); + + describe('Integration', () => { + it('should work correctly with PipeWire feature enabled', () => { + process.env.XDG_SESSION_TYPE = 'x11'; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith( + 'enable-features', + 'WebRTCPipeWireCapturer' + ); + expect(appendSwitchMock).toHaveBeenCalledWith('ozone-platform', 'x11'); + }); + + it('should not interfere with other command line switches', () => { + process.env.XDG_SESSION_TYPE = 'wayland'; + process.env.WAYLAND_DISPLAY = 'wayland-0'; + + performElectronStartup(); + + expect(appendSwitchMock).toHaveBeenCalledWith( + 'autoplay-policy', + 'no-user-gesture-required' + ); + expect(appendSwitchMock).toHaveBeenCalledWith( + 'enable-features', + 'WebRTCPipeWireCapturer' + ); + + const ozonePlatformCalls = appendSwitchMock.mock.calls.filter( + (call) => call[0] === 'ozone-platform' + ); + expect(ozonePlatformCalls).toHaveLength(0); + }); + }); +}); diff --git a/src/app/main/app.ts b/src/app/main/app.ts index a1b0cbf7e4..c1101196dd 100644 --- a/src/app/main/app.ts +++ b/src/app/main/app.ts @@ -1,4 +1,7 @@ -import { app, session } from 'electron'; +import { spawn } from 'child_process'; +import * as fs from 'fs'; + +import { app, session, BrowserWindow } from 'electron'; import { rimraf } from 'rimraf'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -16,6 +19,8 @@ import { SETTINGS_CLEAR_PERMITTED_SCREEN_CAPTURE_PERMISSIONS, SETTINGS_NTLM_CREDENTIALS_CHANGED, SETTINGS_SET_HARDWARE_ACCELERATION_OPT_IN_CHANGED, + SETTINGS_SET_IS_TRANSPARENT_WINDOW_ENABLED_CHANGED, + SETTINGS_SET_IS_VIDEO_CALL_SCREEN_CAPTURE_FALLBACK_ENABLED_CHANGED, } from '../../ui/actions'; import { askForClearScreenCapturePermission } from '../../ui/main/dialogs'; import { getRootWindow } from '../../ui/main/rootWindow'; @@ -25,6 +30,7 @@ import { APP_MAIN_WINDOW_TITLE_SET, APP_PATH_SET, APP_VERSION_SET, + APP_SCREEN_CAPTURE_FALLBACK_FORCED_SET, } from '../actions'; export const packageJsonInformation = { @@ -37,6 +43,8 @@ export const electronBuilderJsonInformation = { protocol: electronBuilderJson.protocols.schemes[0], }; +let isScreenCaptureFallbackForced = false; + export const getPlatformName = (): string => { switch (process.platform) { case 'win32': @@ -51,6 +59,24 @@ export const getPlatformName = (): string => { }; export const relaunchApp = (...args: string[]): void => { + // For AppImage, use spawn to relaunch because app.relaunch() doesn't work reliably + if (process.env.APPIMAGE) { + console.log('Relaunching AppImage:', { + appImage: process.env.APPIMAGE, + args, + }); + + // Spawn the AppImage as a detached process + spawn(process.env.APPIMAGE, args, { + detached: true, + stdio: 'ignore', + }).unref(); + + app.exit(); + return; + } + + // For non-AppImage, use the standard relaunch method const command = process.argv.slice(1, app.isPackaged ? 1 : 2); app.relaunch({ args: [...command, ...args] }); app.exit(); @@ -60,11 +86,12 @@ export const performElectronStartup = (): void => { app.setAsDefaultProtocolClient(electronBuilderJsonInformation.protocol); app.setAppUserModelId(electronBuilderJsonInformation.appId); - app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required'); - app.commandLine.appendSwitch( - 'disable-features', - 'HardwareMediaKeyHandling,MediaSessionService' - ); + app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required'); + + const disabledChromiumFeatures = [ + 'HardwareMediaKeyHandling', + 'MediaSessionService', + ]; if (getPlatformName() === 'macOS' && process.mas) { app.commandLine.appendSwitch('disable-accelerated-video-decode'); @@ -88,6 +115,11 @@ export const performElectronStartup = (): void => { const isHardwareAccelerationEnabled = readSetting( 'isHardwareAccelerationEnabled' ); + const isScreenCaptureFallbackEnabled = readSetting( + 'isVideoCallScreenCaptureFallbackEnabled' + ); + + isScreenCaptureFallbackForced = false; if ( args.includes('--disable-gpu') || @@ -95,23 +127,215 @@ export const performElectronStartup = (): void => { ) { console.log('Disabling Hardware acceleration'); app.disableHardwareAcceleration(); - app.commandLine.appendSwitch('--disable-2d-canvas-image-chromium'); - app.commandLine.appendSwitch('--disable-accelerated-2d-canvas'); - app.commandLine.appendSwitch('--disable-gpu'); + app.commandLine.appendSwitch('disable-2d-canvas-image-chromium'); + app.commandLine.appendSwitch('disable-accelerated-2d-canvas'); + app.commandLine.appendSwitch('disable-gpu'); + } + + if (process.platform === 'win32') { + const sessionName = process.env.SESSIONNAME; + const isRdpSession = + typeof sessionName === 'string' && sessionName !== 'Console'; + + isScreenCaptureFallbackForced = isRdpSession; + + if (isScreenCaptureFallbackEnabled || isRdpSession) { + console.log( + 'Disabling Windows Graphics Capture for video calls', + JSON.stringify({ + reason: isScreenCaptureFallbackEnabled + ? 'user-setting' + : 'rdp-session', + sessionName, + }) + ); + disabledChromiumFeatures.push('WebRtcAllowWgcDesktopCapturer'); + disabledChromiumFeatures.push('WebRtcAllowWgcScreenCapturer'); + } + } + + // Apply all disabled features in a single call + app.commandLine.appendSwitch( + 'disable-features', + disabledChromiumFeatures.join(',') + ); + + // Enable PipeWire screen capture for Linux (Wayland support) + if (process.platform === 'linux') { + app.commandLine.appendSwitch('enable-features', 'WebRTCPipeWireCapturer'); + + // Detect display server and force X11 if not in Wayland session + // This must happen BEFORE Electron tries to auto-select platform + // to prevent segfaults when Wayland is attempted but unavailable + // Note: ELECTRON_OZONE_PLATFORM_HINT was removed in Electron 38 + const hasOzonePlatformOverride = args.some((arg) => + arg.startsWith('--ozone-platform=') + ); + + if (!hasOzonePlatformOverride) { + const sessionType = process.env.XDG_SESSION_TYPE; + const waylandDisplay = process.env.WAYLAND_DISPLAY; + + // Normalize values: trim whitespace and handle empty strings + const normalizedSessionType = sessionType?.trim() || ''; + const normalizedWaylandDisplay = waylandDisplay?.trim() || ''; + + // Only use Wayland if we're actually in a Wayland session AND have a valid socket + // This covers all edge cases: + // - X11 sessions (sessionType === 'x11' or unset) → force X11 + // - Invalid session types (tty, mir, etc.) → force X11 + // - Wayland session but no display → force X11 + // - Wayland session but socket doesn't exist → force X11 + const checkWaylandSocket = (): boolean => { + if ( + normalizedSessionType !== 'wayland' || + normalizedWaylandDisplay === '' + ) { + return false; + } + try { + const runtimeDir = + process.env.XDG_RUNTIME_DIR || + `/run/user/${process.getuid?.() ?? 1000}`; + const socketPath = `${runtimeDir}/${normalizedWaylandDisplay}`; + const stats = fs.statSync(socketPath); + return stats.isSocket(); + } catch { + return false; + } + }; + const isWaylandSession = checkWaylandSocket(); + + if (isWaylandSession) { + console.log( + 'Using Wayland platform', + JSON.stringify({ + sessionType: normalizedSessionType, + waylandDisplay: normalizedWaylandDisplay, + }) + ); + // Let Electron use Wayland (default auto behavior) + // Don't set ozone-platform, let Electron auto-detect + } else { + let reason: string; + if ( + normalizedSessionType === 'wayland' && + normalizedWaylandDisplay !== '' + ) { + reason = 'socket-not-found'; + } else if (normalizedSessionType === 'wayland') { + reason = 'no-wayland-display'; + } else if (normalizedSessionType === 'x11') { + reason = 'x11-session'; + } else if (normalizedSessionType === '') { + reason = 'no-session-type'; + } else { + reason = 'invalid-session-type'; + } + + console.log( + 'Forcing X11 platform', + JSON.stringify({ + sessionType: normalizedSessionType || 'unset', + waylandDisplay: normalizedWaylandDisplay || 'unset', + reason, + }) + ); + app.commandLine.appendSwitch('ozone-platform', 'x11'); + } + } } }; +export const initializeScreenCaptureFallbackState = (): void => { + dispatch({ + type: APP_SCREEN_CAPTURE_FALLBACK_FORCED_SET, + payload: isScreenCaptureFallbackForced, + }); +}; + +export const markMainWindowStable = (): void => { + // No-op: kept for API compatibility +}; + +export const setupGpuCrashHandler = (): void => { + if (process.platform !== 'linux') { + return; + } + + const args = process.argv.slice(app.isPackaged ? 1 : 2); + if (args.includes('--disable-gpu')) { + return; + } + + // Handle GPU process crashes (runtime failures) + app.on('child-process-gone', (_event, details) => { + if (details.type !== 'GPU') { + return; + } + + console.log('GPU process crashed, disabling GPU and relaunching with X11'); + const userArgs = process.argv.slice(app.isPackaged ? 1 : 2); + relaunchApp('--disable-gpu', '--ozone-platform=x11', ...userArgs); + }); + + // Proactive GPU detection: check GPU status once info is available + app.once('gpu-info-update', () => { + const gpuFeatures = app.getGPUFeatureStatus(); + const { gpu_compositing: gpuCompositing, webgl } = gpuFeatures; + + // Check if key GPU features are disabled/unavailable (includes _software variants) + const isGpuBroken = (status: string | undefined): boolean => + !status || + status.startsWith('disabled') || + status.startsWith('unavailable'); + + if (isGpuBroken(gpuCompositing) || isGpuBroken(webgl)) { + console.log( + 'GPU features unavailable, disabling GPU and relaunching with X11', + JSON.stringify(gpuFeatures) + ); + const userArgs = process.argv.slice(app.isPackaged ? 1 : 2); + relaunchApp('--disable-gpu', '--ozone-platform=x11', ...userArgs); + return; + } + + console.log('GPU features status:', JSON.stringify(gpuFeatures)); + }); +}; + export const setupApp = (): void => { app.addListener('activate', async () => { - const browserWindow = await getRootWindow(); - if (!browserWindow.isVisible()) { - browserWindow.showInactive(); + try { + const browserWindow = await getRootWindow(); + if (!browserWindow.isVisible()) { + const wasMinimized = browserWindow.isMinimized(); + const wasMaximized = browserWindow.isMaximized(); + browserWindow.showInactive(); + if (wasMinimized) browserWindow.minimize(); + if (wasMaximized) browserWindow.maximize(); + } + browserWindow.focus(); + } catch (error) { + console.warn('Could not activate window:', error); } - browserWindow.focus(); }); app.addListener('window-all-closed', () => { - app.quit(); + // Don't quit immediately if this might be caused by video call window closure + // especially during first launch when main window might not be fully ready + setTimeout(() => { + const allWindows = BrowserWindow.getAllWindows(); + + // Only quit if there are truly no windows left after a brief delay + // This prevents crashes when video call window closes before main window is established + if (allWindows.length === 0) { + console.log('No windows remaining after delay, quitting application'); + app.quit(); + } else { + console.log(`${allWindows.length} window(s) still exist, not quitting`); + } + }, 100); // Brief delay to let window state stabilize }); app.whenReady().then(() => preloadBrowsersList()); @@ -120,6 +344,31 @@ export const setupApp = (): void => { relaunchApp(); }); + listen(SETTINGS_SET_IS_TRANSPARENT_WINDOW_ENABLED_CHANGED, () => { + relaunchApp(); + }); + + listen( + SETTINGS_SET_IS_VIDEO_CALL_SCREEN_CAPTURE_FALLBACK_ENABLED_CHANGED, + (action) => { + const newSettingValue = action.payload; + const currentPersistedSetting = readSetting( + 'isVideoCallScreenCaptureFallbackEnabled' + ); + const sessionName = process.env.SESSIONNAME; + const isRdpSession = + typeof sessionName === 'string' && sessionName !== 'Console'; + + if (newSettingValue !== currentPersistedSetting && !isRdpSession) { + relaunchApp(); + } else if (isRdpSession) { + console.log( + 'Screen Capture Fallback setting changed in RDP session. Change will apply when running locally. No restart needed now since WGC is already disabled by RDP detection.' + ); + } + } + ); + listen(APP_ALLOWED_NTLM_CREDENTIALS_DOMAINS_SET, (action) => { if (action.payload.length > 0) { session.defaultSession.allowNTLMCredentialsForDomains(action.payload); diff --git a/src/app/main/data.spec.ts b/src/app/main/data.spec.ts new file mode 100644 index 0000000000..d42fb57206 --- /dev/null +++ b/src/app/main/data.spec.ts @@ -0,0 +1,180 @@ +import * as store from '../../store'; +import { APP_SETTINGS_LOADED } from '../actions'; +import { mergePersistableValues } from './data'; + +jest.mock('../../store'); + +const mockDispatch = jest.fn(); +const mockSelect = jest.fn(); + +beforeEach(() => { + jest.clearAllMocks(); + (store.dispatch as jest.Mock).mockImplementation(mockDispatch); + (store.select as jest.Mock).mockImplementation(mockSelect); +}); + +describe('mergePersistableValues', () => { + const mockInitialValues = { + isMenuBarEnabled: true, + isSideBarEnabled: true, + rootWindowState: { + focused: true, + visible: true, + maximized: false, + minimized: false, + fullscreen: false, + normal: true, + bounds: { x: 0, y: 0, width: 1200, height: 800 }, + }, + }; + + beforeEach(() => { + mockSelect.mockReturnValue(mockInitialValues); + + jest.doMock('./persistence', () => ({ + getPersistedValues: jest.fn().mockReturnValue({}), + })); + + jest.doMock('fs', () => ({ + promises: { + readFile: jest.fn().mockRejectedValue(new Error('File not found')), + unlink: jest.fn().mockResolvedValue(undefined), + }, + })); + + jest.doMock('electron', () => ({ + app: { + getPath: jest.fn().mockReturnValue('/user/data'), + }, + })); + }); + + describe('menubar and sidebar recovery mechanism', () => { + it('should enable sidebar when both menubar and sidebar are disabled', async () => { + const localStorage = {}; + + mockSelect.mockReturnValueOnce({ + ...mockInitialValues, + isMenuBarEnabled: false, + isSideBarEnabled: false, + }); + + await mergePersistableValues(localStorage); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: APP_SETTINGS_LOADED, + payload: expect.objectContaining({ + isMenuBarEnabled: false, + isSideBarEnabled: true, + }), + }); + }); + + it('should not modify settings when menubar is enabled and sidebar is disabled', async () => { + const localStorage = {}; + + mockSelect.mockReturnValueOnce({ + ...mockInitialValues, + isMenuBarEnabled: true, + isSideBarEnabled: false, + }); + + await mergePersistableValues(localStorage); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: APP_SETTINGS_LOADED, + payload: expect.objectContaining({ + isMenuBarEnabled: true, + isSideBarEnabled: false, + }), + }); + }); + + it('should not modify settings when sidebar is enabled and menubar is disabled', async () => { + const localStorage = {}; + + mockSelect.mockReturnValueOnce({ + ...mockInitialValues, + isMenuBarEnabled: false, + isSideBarEnabled: true, + }); + + await mergePersistableValues(localStorage); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: APP_SETTINGS_LOADED, + payload: expect.objectContaining({ + isMenuBarEnabled: false, + isSideBarEnabled: true, + }), + }); + }); + + it('should not modify settings when both menubar and sidebar are enabled', async () => { + const localStorage = {}; + + mockSelect.mockReturnValueOnce({ + ...mockInitialValues, + isMenuBarEnabled: true, + isSideBarEnabled: true, + }); + + await mergePersistableValues(localStorage); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: APP_SETTINGS_LOADED, + payload: expect.objectContaining({ + isMenuBarEnabled: true, + isSideBarEnabled: true, + }), + }); + }); + }); + + describe('legacy localStorage migration', () => { + it('should handle autohideMenu from localStorage', async () => { + const localStorage = { + autohideMenu: 'true', + }; + + mockSelect.mockReturnValueOnce({ + ...mockInitialValues, + isMenuBarEnabled: false, + isSideBarEnabled: false, + }); + + await mergePersistableValues(localStorage); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: APP_SETTINGS_LOADED, + payload: expect.objectContaining({ + isMenuBarEnabled: false, + isSideBarEnabled: true, + }), + }); + }); + + it('should handle sidebar-closed from localStorage with recovery', async () => { + const localStorage = { + 'sidebar-closed': 'true', + 'autohideMenu': 'true', + }; + + mockSelect.mockReturnValueOnce({ + ...mockInitialValues, + isMenuBarEnabled: false, + isSideBarEnabled: false, + }); + + await mergePersistableValues(localStorage); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: APP_SETTINGS_LOADED, + payload: expect.objectContaining({ + isMenuBarEnabled: false, + isSideBarEnabled: true, + }), + }); + }); + }); +}); diff --git a/src/app/main/data.ts b/src/app/main/data.ts index 06bebf9690..aafee53893 100644 --- a/src/app/main/data.ts +++ b/src/app/main/data.ts @@ -62,12 +62,21 @@ export const mergePersistableValues = async ( }) ); + const { + outlookCalendarSyncInterval: _udSync, + ...userDataPersistableOverrides + } = userDataOverriddenSettings; + const { + outlookCalendarSyncInterval: _aaSync, + ...appAsarPersistableOverrides + } = appAsarOverriddenSettings; + let values = selectPersistableValues({ ...initialValues, ...electronStoreValues, ...localStorageValues, - ...userDataOverriddenSettings, - ...appAsarOverriddenSettings, + ...userDataPersistableOverrides, + ...appAsarPersistableOverrides, }); if (localStorage.autohideMenu) { @@ -153,9 +162,39 @@ export const mergePersistableValues = async ( }, }; + if (!values.isMenuBarEnabled && !values.isSideBarEnabled) { + values = { + ...values, + isSideBarEnabled: true, + }; + } + + const mergedOverrides: Record = { + ...userDataOverriddenSettings, + ...appAsarOverriddenSettings, + }; + + const outlookCalendarSyncIntervalOverride = + mergedOverrides.outlookCalendarSyncInterval !== undefined + ? (() => { + const raw = Number(mergedOverrides.outlookCalendarSyncInterval); + return Number.isFinite(raw) && Number.isInteger(raw) + ? Math.max(1, Math.min(60, raw)) + : 60; + })() + : null; + dispatch({ type: APP_SETTINGS_LOADED, - payload: values, + payload: { + ...values, + allowInsecureOutlookConnections: + mergedOverrides.allowInsecureOutlookConnections === true || + String( + mergedOverrides.allowInsecureOutlookConnections + ).toLowerCase() === 'true', + outlookCalendarSyncIntervalOverride, + }, }); }; diff --git a/src/app/reducers/screenCaptureFallbackForced.ts b/src/app/reducers/screenCaptureFallbackForced.ts new file mode 100644 index 0000000000..eb87a70e08 --- /dev/null +++ b/src/app/reducers/screenCaptureFallbackForced.ts @@ -0,0 +1,16 @@ +import type { Reducer } from 'redux'; + +import type { ActionOf } from '../../store/actions'; +import { APP_SCREEN_CAPTURE_FALLBACK_FORCED_SET } from '../actions'; + +export const screenCaptureFallbackForced: Reducer< + boolean, + ActionOf +> = (state = false, action) => { + switch (action.type) { + case APP_SCREEN_CAPTURE_FALLBACK_FORCED_SET: + return action.payload; + default: + return state; + } +}; diff --git a/src/app/selectors.ts b/src/app/selectors.ts index 5b55ccb1fc..cc10bfd3a3 100644 --- a/src/app/selectors.ts +++ b/src/app/selectors.ts @@ -42,6 +42,8 @@ export const selectPersistableValues = createStructuredSelector({ isMinimizeOnCloseEnabled, isAddNewServersEnabled: ({ isAddNewServersEnabled }: RootState) => isAddNewServersEnabled, + isDeveloperModeEnabled: ({ isDeveloperModeEnabled }: RootState) => + isDeveloperModeEnabled, hasHideOnTrayNotificationShown: ({ hasHideOnTrayNotificationShown, }: RootState) => hasHideOnTrayNotificationShown, @@ -57,4 +59,28 @@ export const selectPersistableValues = createStructuredSelector({ selectedBrowser: ({ selectedBrowser }: RootState) => selectedBrowser, videoCallWindowState: ({ videoCallWindowState }: RootState) => videoCallWindowState, + isVideoCallWindowPersistenceEnabled: ({ + isVideoCallWindowPersistenceEnabled, + }: RootState) => isVideoCallWindowPersistenceEnabled, + isVideoCallDevtoolsAutoOpenEnabled: ({ + isVideoCallDevtoolsAutoOpenEnabled, + }: RootState) => isVideoCallDevtoolsAutoOpenEnabled, + isTransparentWindowEnabled: ({ isTransparentWindowEnabled }: RootState) => + isTransparentWindowEnabled, + isVideoCallScreenCaptureFallbackEnabled: ({ + isVideoCallScreenCaptureFallbackEnabled, + }: RootState) => isVideoCallScreenCaptureFallbackEnabled, + updateChannel: ({ updateChannel }: RootState) => updateChannel, + userThemePreference: ({ userThemePreference }: RootState) => + userThemePreference, + outlookCalendarSyncInterval: ({ outlookCalendarSyncInterval }: RootState) => + outlookCalendarSyncInterval, + isVerboseOutlookLoggingEnabled: ({ + isVerboseOutlookLoggingEnabled, + }: RootState) => isVerboseOutlookLoggingEnabled, + isDetailedEventsLoggingEnabled: ({ + isDetailedEventsLoggingEnabled, + }: RootState) => isDetailedEventsLoggingEnabled, + isDebugLoggingEnabled: ({ isDebugLoggingEnabled }: RootState) => + isDebugLoggingEnabled, }); diff --git a/src/documentViewer/ipc.ts b/src/documentViewer/ipc.ts index 0ad9ecb1d1..71ba674939 100644 --- a/src/documentViewer/ipc.ts +++ b/src/documentViewer/ipc.ts @@ -38,6 +38,39 @@ export const startDocumentViewerHandler = (): void => { return; } webContent.on('will-navigate', (event, url) => { + // Only prevent navigation for PDF viewer webviews, not video call windows + // Check if this is actually a PDF viewer by examining the context + const currentUrl = webContent.getURL(); + + // Skip handling if this is a video call window or not a PDF viewer context + if ( + currentUrl.includes('video-call-window.html') || + currentUrl.includes('app/video-call-window.html') + ) { + return; + } + + // Also check if the navigation URL is an external protocol (like zoommtg://) + // that should be handled by the system, not intercepted + try { + const navUrl = new URL(url); + const isExternalProtocol = ![ + 'http:', + 'https:', + 'file:', + 'data:', + 'about:', + ].includes(navUrl.protocol); + + // If it's an external protocol, let the system handle it normally + if (isExternalProtocol) { + return; + } + } catch (e) { + // If URL parsing fails, let the default handling proceed + return; + } + event.preventDefault(); setTimeout(() => { openExternal(url); diff --git a/src/downloads/actions.spec.ts b/src/downloads/actions.spec.ts new file mode 100644 index 0000000000..c4d003c2a7 --- /dev/null +++ b/src/downloads/actions.spec.ts @@ -0,0 +1,258 @@ +import type { ActionOf } from '../store/actions'; +import { + DOWNLOAD_CREATED, + DOWNLOAD_REMOVED, + DOWNLOAD_UPDATED, + DOWNLOADS_CLEARED, +} from './actions'; +import type { Download } from './common'; +import { DownloadStatus } from './common'; + +describe('download actions', () => { + const mockDownload: Download = { + itemId: 1640995200000, + state: 'progressing', + status: DownloadStatus.ALL, + fileName: 'test-file.pdf', + receivedBytes: 1024, + totalBytes: 2048, + startTime: 1640995200000, + endTime: undefined, + url: 'https://example.com/file.pdf', + serverUrl: 'https://open.rocket.chat', + serverTitle: 'Rocket.Chat Community', + mimeType: 'application/pdf', + savePath: '/downloads/test-file.pdf', + }; + + describe('action constants', () => { + it('should have correct action type constants', () => { + expect(DOWNLOAD_CREATED).toBe('downloads/created'); + expect(DOWNLOAD_UPDATED).toBe('downloads/updated'); + expect(DOWNLOAD_REMOVED).toBe('downloads/removed'); + expect(DOWNLOADS_CLEARED).toBe('downloads/cleared'); + }); + }); + + describe('action creators', () => { + describe('DOWNLOAD_CREATED action', () => { + it('should create a valid DOWNLOAD_CREATED action', () => { + const action: ActionOf = { + type: DOWNLOAD_CREATED, + payload: mockDownload, + }; + + expect(action.type).toBe(DOWNLOAD_CREATED); + expect(action.payload).toEqual(mockDownload); + }); + + it('should work with different download states', () => { + const pausedDownload: Download = { + ...mockDownload, + state: 'paused', + status: DownloadStatus.PAUSED, + }; + + const action: ActionOf = { + type: DOWNLOAD_CREATED, + payload: pausedDownload, + }; + + expect(action.payload.state).toBe('paused'); + expect(action.payload.status).toBe(DownloadStatus.PAUSED); + }); + }); + + describe('DOWNLOAD_UPDATED action', () => { + it('should create a valid DOWNLOAD_UPDATED action with complete payload', () => { + const updatePayload = { + itemId: mockDownload.itemId, + state: 'completed' as const, + status: DownloadStatus.ALL, + fileName: 'test-file.pdf', + receivedBytes: 2048, + totalBytes: 2048, + startTime: 1640995200000, + endTime: 1640995400000, + url: 'https://example.com/file.pdf', + mimeType: 'application/pdf', + savePath: '/downloads/test-file.pdf', + }; + + const action: ActionOf = { + type: DOWNLOAD_UPDATED, + payload: updatePayload, + }; + + expect(action.type).toBe(DOWNLOAD_UPDATED); + expect(action.payload).toEqual(updatePayload); + }); + + it('should create a valid DOWNLOAD_UPDATED action with partial payload', () => { + const partialUpdatePayload = { + itemId: mockDownload.itemId, + receivedBytes: 1536, + endTime: 1640995300000, + }; + + const action: ActionOf = { + type: DOWNLOAD_UPDATED, + payload: partialUpdatePayload, + }; + + expect(action.type).toBe(DOWNLOAD_UPDATED); + expect(action.payload.itemId).toBe(mockDownload.itemId); + expect(action.payload.receivedBytes).toBe(1536); + expect(action.payload.endTime).toBe(1640995300000); + }); + + it('should handle different download states in updates', () => { + const cancelledUpdatePayload = { + itemId: mockDownload.itemId, + state: 'cancelled' as const, + status: DownloadStatus.CANCELLED, + endTime: 1640995400000, + }; + + const action: ActionOf = { + type: DOWNLOAD_UPDATED, + payload: cancelledUpdatePayload, + }; + + expect(action.payload.state).toBe('cancelled'); + expect(action.payload.status).toBe(DownloadStatus.CANCELLED); + }); + }); + + describe('DOWNLOAD_REMOVED action', () => { + it('should create a valid DOWNLOAD_REMOVED action', () => { + const action: ActionOf = { + type: DOWNLOAD_REMOVED, + payload: mockDownload.itemId, + }; + + expect(action.type).toBe(DOWNLOAD_REMOVED); + expect(action.payload).toBe(mockDownload.itemId); + }); + + it('should work with different itemId types', () => { + const numericItemId = 1640995200000; + const action: ActionOf = { + type: DOWNLOAD_REMOVED, + payload: numericItemId, + }; + + expect(action.payload).toBe(numericItemId); + expect(typeof action.payload).toBe('number'); + }); + }); + + describe('DOWNLOADS_CLEARED action', () => { + it('should create a valid DOWNLOADS_CLEARED action', () => { + const action: ActionOf = { + type: DOWNLOADS_CLEARED, + }; + + expect(action.type).toBe(DOWNLOADS_CLEARED); + expect('payload' in action).toBe(false); + }); + }); + }); + + describe('action type guards', () => { + it('should correctly identify DOWNLOAD_CREATED actions', () => { + const action: ActionOf = { + type: DOWNLOAD_CREATED, + payload: mockDownload, + }; + + expect(action.type === DOWNLOAD_CREATED).toBe(true); + }); + + it('should correctly identify DOWNLOAD_UPDATED actions', () => { + const action: ActionOf = { + type: DOWNLOAD_UPDATED, + payload: { + itemId: mockDownload.itemId, + receivedBytes: 1536, + }, + }; + + expect(action.type === DOWNLOAD_UPDATED).toBe(true); + }); + + it('should correctly identify DOWNLOAD_REMOVED actions', () => { + const action: ActionOf = { + type: DOWNLOAD_REMOVED, + payload: mockDownload.itemId, + }; + + expect(action.type === DOWNLOAD_REMOVED).toBe(true); + }); + + it('should correctly identify DOWNLOADS_CLEARED actions', () => { + const action: ActionOf = { + type: DOWNLOADS_CLEARED, + }; + + expect(action.type === DOWNLOADS_CLEARED).toBe(true); + }); + }); + + describe('payload validation', () => { + it('should require valid Download object for DOWNLOAD_CREATED', () => { + // This is a compile-time test, but we can verify the structure + const action: ActionOf = { + type: DOWNLOAD_CREATED, + payload: mockDownload, + }; + + // All required fields should be present + expect(action.payload.itemId).toBeDefined(); + expect(action.payload.state).toBeDefined(); + expect(action.payload.status).toBeDefined(); + expect(action.payload.fileName).toBeDefined(); + expect(action.payload.receivedBytes).toBeDefined(); + expect(action.payload.totalBytes).toBeDefined(); + expect(action.payload.startTime).toBeDefined(); + expect(action.payload.url).toBeDefined(); + expect(action.payload.serverUrl).toBeDefined(); + expect(action.payload.serverTitle).toBeDefined(); + expect(action.payload.mimeType).toBeDefined(); + expect(action.payload.savePath).toBeDefined(); + }); + + it('should allow partial Download object for DOWNLOAD_UPDATED', () => { + const action: ActionOf = { + type: DOWNLOAD_UPDATED, + payload: { + itemId: mockDownload.itemId, + // Only some fields - this should be valid + receivedBytes: 1500, + state: 'progressing', + }, + }; + + expect(action.payload.itemId).toBe(mockDownload.itemId); + expect(action.payload.receivedBytes).toBe(1500); + expect(action.payload.state).toBe('progressing'); + }); + }); + + describe('immutability', () => { + it('should not modify the original download object when creating actions', () => { + const originalDownload = { ...mockDownload }; + + const action: ActionOf = { + type: DOWNLOAD_CREATED, + payload: mockDownload, + }; + + // Modify the action payload + action.payload.receivedBytes = 9999; + + // Original should be unchanged + expect(originalDownload.receivedBytes).toBe(1024); + }); + }); +}); diff --git a/src/downloads/actions.ts b/src/downloads/actions.ts index bd2d959071..9bd827f9d0 100644 --- a/src/downloads/actions.ts +++ b/src/downloads/actions.ts @@ -1,7 +1,7 @@ import type { Download } from './common'; export const DOWNLOAD_CREATED = 'downloads/created'; -export const DOWNLOAD_REMOVED = 'dowloads/removed'; +export const DOWNLOAD_REMOVED = 'downloads/removed'; export const DOWNLOADS_CLEARED = 'downloads/cleared'; export const DOWNLOAD_UPDATED = 'downloads/updated'; diff --git a/src/downloads/integration.spec.ts b/src/downloads/integration.spec.ts new file mode 100644 index 0000000000..076951aebc --- /dev/null +++ b/src/downloads/integration.spec.ts @@ -0,0 +1,476 @@ +import type { DownloadItem, Event, WebContents } from 'electron'; +import { clipboard, shell, webContents } from 'electron'; +import electronDl from 'electron-dl'; + +import { createMainReduxStore, dispatch, select } from '../store'; +import { + DOWNLOAD_CREATED, + DOWNLOAD_REMOVED, + DOWNLOAD_UPDATED, + DOWNLOADS_CLEARED, +} from './actions'; +import type { Download } from './common'; +import { DownloadStatus } from './common'; +import { handleWillDownloadEvent, setupDownloads } from './main'; + +// Mock electron modules +jest.mock('electron', () => ({ + clipboard: { + writeText: jest.fn(), + }, + shell: { + showItemInFolder: jest.fn(), + }, + webContents: { + getAllWebContents: jest.fn(() => []), + }, +})); + +jest.mock('electron-dl', () => jest.fn()); + +// Mock IPC handler +const mockHandle = jest.fn(); +jest.mock('../ipc/main', () => ({ + handle: mockHandle, +})); + +// Mock notifications +jest.mock('../notifications/preload', () => ({ + createNotification: jest.fn(), +})); + +// Mock i18next +jest.mock('i18next', () => ({ + t: jest.fn((key: string) => key), +})); + +// Mock main.ts to avoid circular dependencies +jest.mock('./main', () => { + const actual = jest.requireActual('./main'); + return { + ...actual, + setupDownloads: actual.setupDownloads, + handleWillDownloadEvent: actual.handleWillDownloadEvent, + }; +}); + +describe('downloads integration tests', () => { + beforeEach(() => { + jest.clearAllMocks(); + createMainReduxStore(); + }); + + const createMockDownloadItem = ( + overrides: Partial = {} + ): jest.Mocked => { + return { + getFilename: jest.fn(() => 'test-file.pdf'), + getState: jest.fn(() => 'progressing'), + isPaused: jest.fn(() => false), + getReceivedBytes: jest.fn(() => 1024), + getTotalBytes: jest.fn(() => 2048), + getStartTime: jest.fn(() => 1640995200), + getURL: jest.fn(() => 'https://example.com/file.pdf'), + getMimeType: jest.fn(() => 'application/pdf'), + getSavePath: jest.fn(() => '/downloads/test-file.pdf'), + on: jest.fn(), + ...overrides, + } as unknown as jest.Mocked; + }; + + const createMockWebContents = (id = 123): WebContents => + ({ + id, + }) as WebContents; + + const createMockEvent = (): Event => + ({ + defaultPrevented: false, + preventDefault: jest.fn(), + }) as unknown as Event; + + describe('end-to-end download flow', () => { + it('should handle complete download lifecycle with electron-dl integration', async () => { + // Setup electron-dl mock + const electronDlMock = electronDl as jest.MockedFunction< + typeof electronDl + >; + let onStartedCallback: (item: DownloadItem) => void; + let onCompletedCallback: (file: { filename: string }) => void; + + electronDlMock.mockImplementation((config) => { + if (config) { + onStartedCallback = config.onStarted!; + onCompletedCallback = config.onCompleted! as any; + } + }); + + // Setup downloads system + setupDownloads(); + + // Mock webContents for electron-dl onStarted callback + const webContentsMock = webContents as jest.Mocked; + const mockWC = createMockWebContents(); + webContentsMock.getAllWebContents.mockReturnValue([mockWC] as any); + + // Create a download item + const mockItem = createMockDownloadItem(); + let updatedListener: () => void; + let doneListener: (event: Event, state: string) => void; + + mockItem.on.mockImplementation(((event: string, listener: any) => { + if (event === 'updated') { + updatedListener = listener; + } else if (event === 'done') { + doneListener = listener; + } + return mockItem; + }) as any); + + // Mock store state with server + const mockSelectImplementation = jest.fn((selector) => { + if (typeof selector === 'function') { + return selector({ + servers: [ + { + url: 'https://open.rocket.chat', + title: 'Rocket.Chat Community', + webContentsId: mockWC.id, + }, + ], + downloads: {}, + }); + } + return undefined; + }); + + (select as jest.MockedFunction).mockImplementation( + mockSelectImplementation + ); + + // Simulate electron-dl onStarted callback + onStartedCallback!(mockItem); + + // Verify download was created + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_CREATED, + payload: expect.objectContaining({ + fileName: 'test-file.pdf', + serverUrl: 'https://open.rocket.chat', + serverTitle: 'Rocket.Chat Community', + }), + }); + + // Simulate download progress + mockItem.getReceivedBytes.mockReturnValue(1536); + updatedListener!(); + + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_UPDATED, + payload: expect.objectContaining({ + receivedBytes: 1536, + }), + }); + + // Simulate download completion + mockItem.getState.mockReturnValue('completed'); + mockItem.getReceivedBytes.mockReturnValue(2048); + doneListener!(createMockEvent(), 'completed'); + + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_UPDATED, + payload: expect.objectContaining({ + state: 'completed', + receivedBytes: 2048, + }), + }); + + // Simulate electron-dl onCompleted callback + onCompletedCallback!({ filename: 'test-file.pdf' }); + + // Should have triggered notification (tested separately) + }); + + it('should handle download operations via IPC', async () => { + setupDownloads(); + + // Mock download in store + const mockDownload: Download = { + itemId: 1640995200000, + state: 'completed', + status: DownloadStatus.ALL, + fileName: 'test-file.pdf', + receivedBytes: 2048, + totalBytes: 2048, + startTime: 1640995200000, + endTime: 1640995400000, + url: 'https://example.com/file.pdf', + serverUrl: 'https://open.rocket.chat', + serverTitle: 'Rocket.Chat Community', + mimeType: 'application/pdf', + savePath: '/downloads/test-file.pdf', + }; + + (select as jest.MockedFunction).mockReturnValue( + mockDownload + ); + + // Get IPC handlers + const showInFolderHandler = mockHandle.mock.calls.find( + ([channel]) => channel === 'downloads/show-in-folder' + )?.[1]; + + const copyLinkHandler = mockHandle.mock.calls.find( + ([channel]) => channel === 'downloads/copy-link' + )?.[1]; + + // Test show in folder + await showInFolderHandler?.(null, mockDownload.itemId); + expect(shell.showItemInFolder).toHaveBeenCalledWith( + '/downloads/test-file.pdf' + ); + + // Test copy link + await copyLinkHandler?.(null, mockDownload.itemId); + expect(clipboard.writeText).toHaveBeenCalledWith( + 'https://example.com/file.pdf' + ); + }); + + it('should handle bulk download operations', async () => { + setupDownloads(); + + // Get IPC handlers + const clearAllHandler = mockHandle.mock.calls.find( + ([channel]) => channel === 'downloads/clear-all' + )?.[1]; + + const removeHandler = mockHandle.mock.calls.find( + ([channel]) => channel === 'downloads/remove' + )?.[1]; + + // Test clear all + await clearAllHandler?.(); + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOADS_CLEARED, + }); + + // Test remove individual + await removeHandler?.(null, 123456789); + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_REMOVED, + payload: 123456789, + }); + }); + }); + + describe('error handling and edge cases', () => { + it('should handle downloads when no server is found', async () => { + const mockItem = createMockDownloadItem(); + const mockWebContents = createMockWebContents(999); // Non-existent webContents ID + const mockEvent = createMockEvent(); + + // Mock store to return no servers + (select as jest.MockedFunction).mockReturnValue(undefined); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_CREATED, + payload: expect.objectContaining({ + serverUrl: 'unknown', + serverTitle: 'Unknown Server', + }), + }); + }); + + it('should handle IPC operations for non-existent downloads', async () => { + setupDownloads(); + + // Mock store to return undefined download + (select as jest.MockedFunction).mockReturnValue(undefined); + + // Get IPC handlers + const showInFolderHandler = mockHandle.mock.calls.find( + ([channel]) => channel === 'downloads/show-in-folder' + )?.[1]; + + const copyLinkHandler = mockHandle.mock.calls.find( + ([channel]) => channel === 'downloads/copy-link' + )?.[1]; + + // Test operations on non-existent download + await showInFolderHandler?.(null, 'non-existent-id'); + expect(shell.showItemInFolder).not.toHaveBeenCalled(); + + await copyLinkHandler?.(null, 'non-existent-id'); + expect(clipboard.writeText).not.toHaveBeenCalled(); + }); + + it('should handle electron-dl onStarted when no webContents are available', () => { + const electronDlMock = electronDl as jest.MockedFunction< + typeof electronDl + >; + let onStartedCallback: (item: DownloadItem) => void; + + electronDlMock.mockImplementation((config) => { + if (config) { + onStartedCallback = config.onStarted!; + } + }); + + setupDownloads(); + + const webContentsMock = webContents as jest.Mocked; + webContentsMock.getAllWebContents.mockReturnValue([]); + + const mockItem = createMockDownloadItem(); + + // Should not throw when no webContents are available + expect(() => onStartedCallback!(mockItem)).not.toThrow(); + }); + + it('should handle electron-dl onStarted when webContents are destroyed', () => { + const electronDlMock = electronDl as jest.MockedFunction< + typeof electronDl + >; + let onStartedCallback: (item: DownloadItem) => void; + + electronDlMock.mockImplementation((config) => { + if (config) { + onStartedCallback = config.onStarted!; + } + }); + + setupDownloads(); + + const webContentsMock = webContents as jest.Mocked; + const destroyedWC = { + id: 123, + isDestroyed: () => true, + }; + webContentsMock.getAllWebContents.mockReturnValue([destroyedWC] as any); + + const mockItem = createMockDownloadItem(); + + // Should not throw when all webContents are destroyed + expect(() => onStartedCallback!(mockItem)).not.toThrow(); + }); + }); + + describe('concurrent downloads', () => { + it('should handle multiple simultaneous downloads', async () => { + const electronDlMock = electronDl as jest.MockedFunction< + typeof electronDl + >; + let onStartedCallback: (item: DownloadItem) => void; + + electronDlMock.mockImplementation((config) => { + if (config) { + onStartedCallback = config.onStarted!; + } + }); + + setupDownloads(); + + // Mock multiple webContents + const webContentsMock = webContents as jest.Mocked; + const mockWC1 = createMockWebContents(123); + const mockWC2 = createMockWebContents(456); + webContentsMock.getAllWebContents.mockReturnValue([ + mockWC1, + mockWC2, + ] as any); + + // Mock store with multiple servers + (select as jest.MockedFunction).mockImplementation( + (selector) => { + if (typeof selector === 'function') { + return selector({ + servers: [ + { + url: 'https://server1.com', + title: 'Server 1', + webContentsId: 123, + }, + { + url: 'https://server2.com', + title: 'Server 2', + webContentsId: 456, + }, + ], + downloads: {}, + } as any); + } + return undefined; + } + ); + + // Create multiple download items + const mockItem1 = createMockDownloadItem({ + getFilename: jest.fn(() => 'file1.pdf'), + }); + + const mockItem2 = createMockDownloadItem({ + getFilename: jest.fn(() => 'file2.jpg'), + }); + + // Simulate simultaneous downloads + onStartedCallback!(mockItem1); + onStartedCallback!(mockItem2); + + // Both downloads should be created + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_CREATED, + payload: expect.objectContaining({ + fileName: 'file1.pdf', + }), + }); + + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_CREATED, + payload: expect.objectContaining({ + fileName: 'file2.jpg', + }), + }); + }); + }); + + describe('memory and cleanup', () => { + it('should properly clean up download items after completion', async () => { + const mockItem = createMockDownloadItem(); + const mockWebContents = createMockWebContents(); + const mockEvent = createMockEvent(); + + let doneListener: (event: Event, state: string) => void; + mockItem.on.mockImplementation(((event: string, listener: any) => { + if (event === 'done') { + doneListener = listener; + } + return mockItem; + }) as any); + + (select as jest.MockedFunction).mockReturnValue({ + url: 'https://test.com', + title: 'Test Server', + }); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + // Verify download was added to items map (internal state) + // This is tested indirectly through the behavior + + // Simulate completion to trigger cleanup + doneListener!(mockEvent, 'completed'); + + // After completion, the item should be removed from internal tracking + // This is tested indirectly through the dispatch call + expect(dispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_UPDATED, + payload: expect.objectContaining({ + state: 'completed', + }), + }); + }); + }); +}); diff --git a/src/downloads/main.spec.ts b/src/downloads/main.spec.ts new file mode 100644 index 0000000000..11aa1f147c --- /dev/null +++ b/src/downloads/main.spec.ts @@ -0,0 +1,403 @@ +import type { DownloadItem, Event, WebContents } from 'electron'; +import { clipboard, shell } from 'electron'; + +import { handle } from '../ipc/main'; +import { dispatch, select } from '../store'; +import { + DOWNLOAD_CREATED, + DOWNLOAD_REMOVED, + DOWNLOAD_UPDATED, + DOWNLOADS_CLEARED, +} from './actions'; +import { DownloadStatus } from './common'; +import { handleWillDownloadEvent, setupDownloads } from './main'; + +// Mock electron modules +jest.mock('electron', () => ({ + clipboard: { + writeText: jest.fn(), + }, + shell: { + showItemInFolder: jest.fn(), + }, + webContents: {}, +})); + +// Mock IPC handler +jest.mock('../ipc/main', () => ({ + handle: jest.fn(), +})); + +// Mock notifications +jest.mock('../notifications/preload', () => ({ + createNotification: jest.fn(), +})); + +// Mock i18next +jest.mock('i18next', () => ({ + t: jest.fn((key: string) => key), +})); + +// Mock Redux store +jest.mock('../store', () => ({ + dispatch: jest.fn(), + select: jest.fn(), +})); + +describe('downloads/main', () => { + const mockDispatch = dispatch as jest.MockedFunction; + const mockSelect = select as jest.MockedFunction; + const mockHandle = handle as jest.MockedFunction; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('handleWillDownloadEvent', () => { + const createMockDownloadItem = ( + overrides: Partial = {} + ): jest.Mocked => { + const mockItem = { + getFilename: jest.fn(() => 'test-file.pdf'), + getState: jest.fn(() => 'progressing'), + isPaused: jest.fn(() => false), + getReceivedBytes: jest.fn(() => 1024), + getTotalBytes: jest.fn(() => 2048), + getStartTime: jest.fn(() => 1640995200), // Unix timestamp + getURL: jest.fn(() => 'https://example.com/file.pdf'), + getMimeType: jest.fn(() => 'application/pdf'), + getSavePath: jest.fn(() => '/downloads/test-file.pdf'), + on: jest.fn(), + pause: jest.fn(), + resume: jest.fn(), + cancel: jest.fn(), + ...overrides, + } as unknown as jest.Mocked; + + return mockItem; + }; + + const createMockWebContents = (id = 123): WebContents => + ({ + id, + }) as WebContents; + + const createMockEvent = (): Event => + ({ + defaultPrevented: false, + preventDefault: jest.fn(), + }) as unknown as Event; + + it('should create a download entry in Redux store when server is found', async () => { + const mockItem = createMockDownloadItem(); + const mockWebContents = createMockWebContents(123); + const mockEvent = createMockEvent(); + + // Mock server selection to return a server + mockSelect.mockReturnValue({ + url: 'https://open.rocket.chat', + title: 'Rocket.Chat Community', + }); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_CREATED, + payload: expect.objectContaining({ + fileName: 'test-file.pdf', + state: 'progressing', + status: DownloadStatus.ALL, + receivedBytes: 1024, + totalBytes: 2048, + url: 'https://example.com/file.pdf', + serverUrl: 'https://open.rocket.chat', + serverTitle: 'Rocket.Chat Community', + mimeType: 'application/pdf', + savePath: '/downloads/test-file.pdf', + }), + }); + }); + + it('should use fallback values when server is not found', async () => { + const mockItem = createMockDownloadItem(); + const mockWebContents = createMockWebContents(999); // Non-existent webContents ID + const mockEvent = createMockEvent(); + + // Mock server selection to return undefined + mockSelect.mockReturnValue(undefined); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_CREATED, + payload: expect.objectContaining({ + serverUrl: 'unknown', + serverTitle: 'Unknown Server', + }), + }); + }); + + it('should handle paused downloads correctly', async () => { + const mockItem = createMockDownloadItem({ + isPaused: jest.fn(() => true), + }); + const mockWebContents = createMockWebContents(); + const mockEvent = createMockEvent(); + + mockSelect.mockReturnValue(undefined); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_CREATED, + payload: expect.objectContaining({ + state: 'paused', + status: DownloadStatus.PAUSED, + }), + }); + }); + + it('should set up download item event listeners', async () => { + const mockItem = createMockDownloadItem(); + const mockWebContents = createMockWebContents(); + const mockEvent = createMockEvent(); + + mockSelect.mockReturnValue(undefined); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + // Verify that 'updated' and 'done' event listeners were set up + expect(mockItem.on).toHaveBeenCalledWith('updated', expect.any(Function)); + expect(mockItem.on).toHaveBeenCalledWith('done', expect.any(Function)); + }); + + it('should dispatch DOWNLOAD_UPDATED when download is updated', async () => { + const mockItem = createMockDownloadItem(); + const mockWebContents = createMockWebContents(); + const mockEvent = createMockEvent(); + + // Capture the event listeners + let updateListener: () => void; + mockItem.on.mockImplementation((event: string, listener: any) => { + if (event === 'updated') { + updateListener = listener; + } + return mockItem; + }); + + mockSelect.mockReturnValue(undefined); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + // Simulate an update + mockItem.getReceivedBytes.mockReturnValue(1500); + updateListener!(); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_UPDATED, + payload: expect.objectContaining({ + receivedBytes: 1500, + endTime: expect.any(Number), + }), + }); + }); + + it('should dispatch DOWNLOAD_UPDATED when download is completed', async () => { + const mockItem = createMockDownloadItem(); + const mockWebContents = createMockWebContents(); + const mockEvent = createMockEvent(); + + // Capture the event listeners + let doneListener: (event: Event, state: string) => void; + mockItem.on.mockImplementation((event: string, listener: any) => { + if (event === 'done') { + doneListener = listener; + } + return mockItem; + }); + + mockSelect.mockReturnValue(undefined); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + // Simulate completion + mockItem.getState.mockReturnValue('completed'); + doneListener!(mockEvent, 'completed'); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_UPDATED, + payload: expect.objectContaining({ + state: 'completed', + status: DownloadStatus.ALL, + endTime: expect.any(Number), + }), + }); + }); + + it('should handle cancelled downloads correctly', async () => { + const mockItem = createMockDownloadItem(); + const mockWebContents = createMockWebContents(); + const mockEvent = createMockEvent(); + + // Capture the event listeners + let doneListener: (event: Event, state: string) => void; + mockItem.on.mockImplementation((event: string, listener: any) => { + if (event === 'done') { + doneListener = listener; + } + return mockItem; + }); + + mockSelect.mockReturnValue(undefined); + + await handleWillDownloadEvent(mockEvent, mockItem, mockWebContents); + + // Simulate cancellation + mockItem.getState.mockReturnValue('cancelled'); + doneListener!(mockEvent, 'cancelled'); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_UPDATED, + payload: expect.objectContaining({ + state: 'cancelled', + status: DownloadStatus.CANCELLED, + }), + }); + }); + }); + + describe('setupDownloads', () => { + beforeEach(() => { + setupDownloads(); + }); + + it('should register all IPC handlers', () => { + expect(mockHandle).toHaveBeenCalledWith( + 'downloads/show-in-folder', + expect.any(Function) + ); + expect(mockHandle).toHaveBeenCalledWith( + 'downloads/copy-link', + expect.any(Function) + ); + expect(mockHandle).toHaveBeenCalledWith( + 'downloads/pause', + expect.any(Function) + ); + expect(mockHandle).toHaveBeenCalledWith( + 'downloads/resume', + expect.any(Function) + ); + expect(mockHandle).toHaveBeenCalledWith( + 'downloads/cancel', + expect.any(Function) + ); + expect(mockHandle).toHaveBeenCalledWith( + 'downloads/retry', + expect.any(Function) + ); + expect(mockHandle).toHaveBeenCalledWith( + 'downloads/clear-all', + expect.any(Function) + ); + expect(mockHandle).toHaveBeenCalledWith( + 'downloads/remove', + expect.any(Function) + ); + }); + + describe('IPC handler: downloads/show-in-folder', () => { + it('should show download in folder when download exists', async () => { + const mockDownload = { + savePath: '/downloads/test-file.pdf', + }; + + mockSelect.mockReturnValue(mockDownload); + + // Get the registered handler + const showInFolderHandler = mockHandle.mock.calls.find( + ([channel]) => (channel as string) === 'downloads/show-in-folder' + )?.[1] as any; + + await showInFolderHandler?.({} as any, 'test-item-id'); + + expect(shell.showItemInFolder).toHaveBeenCalledWith( + '/downloads/test-file.pdf' + ); + }); + + it('should do nothing when download does not exist', async () => { + mockSelect.mockReturnValue(undefined); + + const showInFolderHandler = mockHandle.mock.calls.find( + ([channel]) => (channel as string) === 'downloads/show-in-folder' + )?.[1] as any; + + await showInFolderHandler?.({} as any, 'non-existent-id'); + + expect(shell.showItemInFolder).not.toHaveBeenCalled(); + }); + }); + + describe('IPC handler: downloads/copy-link', () => { + it('should copy download URL to clipboard when download exists', async () => { + const mockDownload = { + url: 'https://example.com/file.pdf', + }; + + mockSelect.mockReturnValue(mockDownload); + + const copyLinkHandler = mockHandle.mock.calls.find( + ([channel]) => (channel as string) === 'downloads/copy-link' + )?.[1] as any; + + await copyLinkHandler?.({} as any, 'test-item-id'); + + expect(clipboard.writeText).toHaveBeenCalledWith( + 'https://example.com/file.pdf' + ); + }); + + it('should do nothing when download does not exist', async () => { + mockSelect.mockReturnValue(undefined); + + const copyLinkHandler = mockHandle.mock.calls.find( + ([channel]) => (channel as string) === 'downloads/copy-link' + )?.[1] as any; + + await copyLinkHandler?.({} as any, 'non-existent-id'); + + expect(clipboard.writeText).not.toHaveBeenCalled(); + }); + }); + + describe('IPC handler: downloads/clear-all', () => { + it('should dispatch DOWNLOADS_CLEARED action', async () => { + const clearAllHandler = mockHandle.mock.calls.find( + ([channel]) => (channel as string) === 'downloads/clear-all' + )?.[1] as any; + + await clearAllHandler?.({} as any); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: DOWNLOADS_CLEARED, + }); + }); + }); + + describe('IPC handler: downloads/remove', () => { + it('should dispatch DOWNLOAD_REMOVED action with correct itemId', async () => { + const removeHandler = mockHandle.mock.calls.find( + ([channel]) => (channel as string) === 'downloads/remove' + )?.[1] as any; + + await removeHandler?.({} as any, 'test-item-id'); + + expect(mockDispatch).toHaveBeenCalledWith({ + type: DOWNLOAD_REMOVED, + payload: 'test-item-id', + }); + }); + }); + }); +}); diff --git a/src/downloads/main.ts b/src/downloads/main.ts index 6b67864d40..e8cff219ad 100644 --- a/src/downloads/main.ts +++ b/src/downloads/main.ts @@ -9,6 +9,7 @@ import { DOWNLOAD_CREATED, DOWNLOAD_REMOVED, DOWNLOAD_UPDATED, + DOWNLOADS_CLEARED, } from './actions'; import type { Download } from './common'; import { DownloadStatus } from './common'; @@ -21,35 +22,36 @@ export const handleWillDownloadEvent = async ( serverWebContents: WebContents ): Promise => { const itemId = Date.now(); - items.set(itemId, item); const server = select(({ servers }) => servers.find((server) => server.webContentsId === serverWebContents.id) ); - if (!server) { - // TODO: check if the download always comes from the main frame webContents - throw new Error('could not match the server'); - } + const serverUrl = server?.url || 'unknown'; + const serverTitle = server?.title || 'Unknown Server'; + + const downloadPayload = { + itemId, + state: item.isPaused() + ? ('paused' as const) + : (item.getState() as Download['state']), + status: item.isPaused() ? DownloadStatus.PAUSED : DownloadStatus.ALL, + fileName: item.getFilename(), + receivedBytes: item.getReceivedBytes(), + totalBytes: item.getTotalBytes(), + startTime: item.getStartTime() * 1000, + endTime: undefined, + url: item.getURL(), + serverUrl, + serverTitle, + mimeType: item.getMimeType(), + savePath: item.getSavePath(), + }; dispatch({ type: DOWNLOAD_CREATED, - payload: { - itemId, - state: item.isPaused() ? 'paused' : item.getState(), - status: item.isPaused() ? DownloadStatus.PAUSED : DownloadStatus.ALL, - fileName: item.getFilename(), - receivedBytes: item.getReceivedBytes(), - totalBytes: item.getTotalBytes(), - startTime: item.getStartTime() * 1000, - endTime: undefined, - url: item.getURL(), - serverUrl: server?.url, - serverTitle: server?.title, - mimeType: item.getMimeType(), - savePath: item.getSavePath(), - }, + payload: downloadPayload, }); item.on('updated', () => { @@ -57,7 +59,9 @@ export const handleWillDownloadEvent = async ( type: DOWNLOAD_UPDATED, payload: { itemId, - state: item.isPaused() ? 'paused' : item.getState(), + state: item.isPaused() + ? ('paused' as const) + : (item.getState() as Download['state']), status: item.isPaused() ? DownloadStatus.PAUSED : DownloadStatus.ALL, fileName: item.getFilename(), receivedBytes: item.getReceivedBytes(), @@ -71,21 +75,12 @@ export const handleWillDownloadEvent = async ( }); }); - item.on('done', (_event, state) => { - createNotification({ - title: 'Downloads', - body: item.getFilename(), - subtitle: - state === 'completed' - ? t('downloads.notifications.downloadFinished') - : t('downloads.notifications.downloadCancelled'), - }); - + item.on('done', (_event, _state) => { dispatch({ type: DOWNLOAD_UPDATED, payload: { itemId, - state: item.getState(), + state: item.getState() as Download['state'], status: item.getState() === 'cancelled' ? DownloadStatus.CANCELLED @@ -123,7 +118,7 @@ export const setupDownloads = (): void => { return; } - clipboard.write({ text: download.url }); + clipboard.writeText(download.url); }); handle('downloads/pause', async (_webContent, itemId) => { @@ -197,6 +192,7 @@ export const setupDownloads = (): void => { title: t('downloads.notifications.downloadExpired'), body: t('downloads.notifications.downloadExpiredMessage'), subtitle: download.fileName, + category: 'DOWNLOADS', }); dispatch({ @@ -226,6 +222,12 @@ export const setupDownloads = (): void => { } }); + handle('downloads/clear-all' as any, async () => { + dispatch({ + type: DOWNLOADS_CLEARED, + }); + }); + handle('downloads/remove', async (_webContent, itemId) => { if (items.has(itemId)) { const item = items.get(itemId); diff --git a/src/downloads/main/download-persistence.spec.ts b/src/downloads/main/download-persistence.spec.ts new file mode 100644 index 0000000000..cafbd3b047 --- /dev/null +++ b/src/downloads/main/download-persistence.spec.ts @@ -0,0 +1,655 @@ +import path from 'path'; + +import type { DownloadItem } from 'electron'; +import { app, webContents } from 'electron'; +import electronDl from 'electron-dl'; + +import { setupElectronDlWithTracking } from './setup'; + +// Mock fs functions for directory validation +jest.mock('fs', () => ({ + existsSync: jest.fn(() => true), // Default to directories existing + statSync: jest.fn(() => ({ + isDirectory: () => true, // Default to being directories + })), +})); + +// Mock os module for cross-platform compatibility +jest.mock('os', () => ({ + tmpdir: jest.fn(() => '/tmp'), +})); + +// Mock all dependencies with comprehensive mocking +jest.mock('electron', () => ({ + app: { + getPath: jest.fn(() => '/Users/test/Downloads'), + }, + webContents: { + getAllWebContents: jest.fn(() => []), + }, +})); + +jest.mock('electron-dl', () => jest.fn()); + +jest.mock('electron-store', () => { + // Create a shared mock store instance inside the mock factory + const sharedMockStore = { + get: jest.fn(), + set: jest.fn(), + has: jest.fn(), + delete: jest.fn(), + clear: jest.fn(), + size: 0, + store: {}, + }; + + return jest.fn(() => sharedMockStore); +}); + +// Get access to the shared mock store for tests +let sharedMockStore: any; +beforeAll(() => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const ElectronStore = require('electron-store'); + sharedMockStore = new ElectronStore(); +}); + +// Mock the handleWillDownloadEvent function from the parent directory +jest.mock('../main', () => ({ + handleWillDownloadEvent: jest.fn(() => Promise.resolve()), +})); + +// Mock i18next +jest.mock('i18next', () => ({ + t: jest.fn((key: string) => key), +})); + +// Mock notifications +jest.mock('../../notifications/preload', () => ({ + createNotification: jest.fn(), +})); + +// Don't mock the setup module - we want to use the real implementation +// but with mocked dependencies. The mocks above will handle the dependencies. + +describe('Download Folder Persistence', () => { + let electronDlMock: jest.MockedFunction; + let appMock: jest.Mocked; + let webContentsMock: jest.Mocked; + + beforeEach(() => { + // Clear all mocks + jest.clearAllMocks(); + + // Set up mocks + electronDlMock = electronDl as jest.MockedFunction; + appMock = app as jest.Mocked; + webContentsMock = webContents as jest.Mocked; + + // Set up default mock behaviors + appMock.getPath.mockReturnValue('/Users/test/Downloads'); + webContentsMock.getAllWebContents.mockReturnValue([]); + sharedMockStore.get.mockReturnValue('/Users/test/Downloads'); + sharedMockStore.set.mockReturnValue(undefined); + }); + + const createMockDownloadItem = ( + filename = 'test-file.pdf' + ): jest.Mocked => + ({ + getFilename: jest.fn(() => filename), + setSaveDialogOptions: jest.fn(), + on: jest.fn(), + getState: jest.fn(() => 'progressing'), + isPaused: jest.fn(() => false), + getReceivedBytes: jest.fn(() => 1024), + getTotalBytes: jest.fn(() => 2048), + getStartTime: jest.fn(() => 1640995200), + getURL: jest.fn(() => 'https://example.com/file.pdf'), + getMimeType: jest.fn(() => 'application/pdf'), + getSavePath: jest.fn(() => '/test/path/test-file.pdf'), + // Add all other required DownloadItem methods + pause: jest.fn(), + resume: jest.fn(), + cancel: jest.fn(), + canResume: jest.fn(() => true), + getETag: jest.fn(() => ''), + getLastModifiedTime: jest.fn(() => ''), + hasUserGesture: jest.fn(() => true), + once: jest.fn(), + addListener: jest.fn(), + removeListener: jest.fn(), + removeAllListeners: jest.fn(), + setMaxListeners: jest.fn(), + getMaxListeners: jest.fn(() => 10), + listeners: jest.fn(() => []), + rawListeners: jest.fn(() => []), + emit: jest.fn(() => true), + listenerCount: jest.fn(() => 0), + prependListener: jest.fn(), + prependOnceListener: jest.fn(), + eventNames: jest.fn(() => []), + off: jest.fn(), + }) as any; + + describe('Store Configuration', () => { + it('should use the mocked store for download persistence', () => { + setupElectronDlWithTracking(); + + // Verify that electron-dl was called with the correct configuration + expect(electronDlMock).toHaveBeenCalledWith({ + saveAs: true, + onStarted: expect.any(Function), + onCompleted: expect.any(Function), + }); + }); + }); + + describe('onStarted - Path Setting', () => { + let onStartedCallback: (item: DownloadItem) => void; + + beforeEach(() => { + setupElectronDlWithTracking(); + const electronDlCall = electronDlMock.mock.calls[0]; + if (electronDlCall?.[0]?.onStarted) { + onStartedCallback = electronDlCall[0].onStarted; + } + }); + + it('should set save dialog options with directory and filename', () => { + expect(onStartedCallback).toBeDefined(); + + const mockItem = createMockDownloadItem('document.pdf'); + sharedMockStore.get.mockReturnValue('/Users/test/Documents'); + + onStartedCallback(mockItem); + + expect(sharedMockStore.get).toHaveBeenCalledWith( + 'lastDownloadDirectory', + '/Users/test/Downloads' + ); + + expect(mockItem.setSaveDialogOptions).toHaveBeenCalledWith({ + defaultPath: path.join('/Users/test/Documents', 'document.pdf'), + }); + }); + + it('should handle special characters in filenames', () => { + expect(onStartedCallback).toBeDefined(); + + const mockItem = createMockDownloadItem('My File (2023) [Copy].pdf'); + sharedMockStore.get.mockReturnValue('/Users/test/Documents'); + + onStartedCallback(mockItem); + + expect(mockItem.setSaveDialogOptions).toHaveBeenCalledWith({ + defaultPath: path.join( + '/Users/test/Documents', + 'My File (2023) [Copy].pdf' + ), + }); + }); + + it('should use fallback directory when store returns empty', () => { + expect(onStartedCallback).toBeDefined(); + + const mockItem = createMockDownloadItem('test.pdf'); + sharedMockStore.get.mockReturnValue('/Users/test/Downloads'); // Fallback value + + onStartedCallback(mockItem); + + expect(mockItem.setSaveDialogOptions).toHaveBeenCalledWith({ + defaultPath: expect.stringContaining('test.pdf'), + }); + }); + + it('should use fallback directory when configured directory does not exist', () => { + expect(onStartedCallback).toBeDefined(); + + // Mock fs functions to simulate non-existent directory + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { existsSync } = require('fs'); + existsSync.mockReturnValueOnce(false); // Directory doesn't exist + + const mockItem = createMockDownloadItem('test.pdf'); + sharedMockStore.get.mockReturnValue('/Users/test/NonExistentDirectory'); + + onStartedCallback(mockItem); + + expect(mockItem.setSaveDialogOptions).toHaveBeenCalledWith({ + defaultPath: path.join('/Users/test/Downloads', 'test.pdf'), // Should fallback to default + }); + }); + + it('should handle different file extensions correctly', () => { + expect(onStartedCallback).toBeDefined(); + + const testCases = [ + { filename: 'image.jpg', dir: '/Users/test/Pictures' }, + { filename: 'video.mp4', dir: '/Users/test/Videos' }, + { filename: 'archive.zip', dir: '/Users/test/Downloads' }, + { filename: 'no-extension', dir: '/Users/test/Documents' }, + ]; + + testCases.forEach(({ filename, dir }) => { + const mockItem = createMockDownloadItem(filename); + sharedMockStore.get.mockReturnValue(dir); + + onStartedCallback(mockItem); + + expect(mockItem.setSaveDialogOptions).toHaveBeenCalledWith({ + defaultPath: path.join(dir, filename), + }); + }); + }); + + it('should handle errors gracefully in onStarted callback', () => { + expect(onStartedCallback).toBeDefined(); + + const mockItem = createMockDownloadItem('test.pdf'); + sharedMockStore.get.mockImplementation(() => { + throw new Error('Store error'); + }); + + // Should not throw + expect(() => onStartedCallback(mockItem)).not.toThrow(); + }); + }); + + describe('onCompleted - Directory Storage', () => { + let onCompletedCallback: (file: { filename: string; path: string }) => void; + + beforeEach(() => { + setupElectronDlWithTracking(); + const electronDlCall = electronDlMock.mock.calls[0]; + if (electronDlCall?.[0]?.onCompleted) { + onCompletedCallback = electronDlCall[0].onCompleted as any; + } + }); + + it('should store the directory of completed download', () => { + expect(onCompletedCallback).toBeDefined(); + + const mockFile = { + filename: 'completed-file.pdf', + path: '/Users/test/Documents/Projects/completed-file.pdf', + }; + + onCompletedCallback(mockFile); + + expect(sharedMockStore.set).toHaveBeenCalledWith( + 'lastDownloadDirectory', + path.dirname('/Users/test/Documents/Projects/completed-file.pdf') + ); + }); + + it('should handle root directory paths', () => { + expect(onCompletedCallback).toBeDefined(); + + const mockFile = { + filename: 'root-file.txt', + path: '/root-file.txt', + }; + + onCompletedCallback(mockFile); + + expect(sharedMockStore.set).toHaveBeenCalledWith( + 'lastDownloadDirectory', + path.dirname('/root-file.txt') + ); + }); + + it('should extract directory from nested paths', () => { + expect(onCompletedCallback).toBeDefined(); + + const mockFile = { + filename: 'deep-file.json', + path: '/Users/test/Documents/Work/2023/Q4/deep-file.json', + }; + + onCompletedCallback(mockFile); + + expect(sharedMockStore.set).toHaveBeenCalledWith( + 'lastDownloadDirectory', + path.dirname('/Users/test/Documents/Work/2023/Q4/deep-file.json') + ); + }); + + it('should handle errors gracefully in onCompleted callback', () => { + expect(onCompletedCallback).toBeDefined(); + + sharedMockStore.set.mockImplementation(() => { + throw new Error('Store write error'); + }); + + const mockFile = { + filename: 'test.pdf', + path: '/Users/test/Documents/test.pdf', + }; + + // Should not throw + expect(() => onCompletedCallback(mockFile)).not.toThrow(); + }); + + it('should handle invalid file paths', () => { + expect(onCompletedCallback).toBeDefined(); + + const invalidPaths = ['', '/']; + + invalidPaths.forEach((invalidPath) => { + const mockFile = { + filename: 'test.pdf', + path: invalidPath, + }; + + expect(() => onCompletedCallback(mockFile)).not.toThrow(); + }); + }); + }); + + describe('Integration Flow', () => { + let onStartedCallback: (item: DownloadItem) => void; + let onCompletedCallback: (file: { filename: string; path: string }) => void; + + beforeEach(() => { + setupElectronDlWithTracking(); + const electronDlCall = electronDlMock.mock.calls[0]; + if (electronDlCall?.[0]) { + onStartedCallback = electronDlCall[0].onStarted!; + onCompletedCallback = electronDlCall[0].onCompleted! as any; + } + }); + + it('should remember folder across multiple downloads', () => { + expect(onStartedCallback).toBeDefined(); + expect(onCompletedCallback).toBeDefined(); + + // First download + const firstItem = createMockDownloadItem('first-file.pdf'); + sharedMockStore.get.mockReturnValueOnce('/Users/test/Downloads'); + onStartedCallback(firstItem); + + // User saves to custom directory + onCompletedCallback({ + filename: 'first-file.pdf', + path: '/Users/test/Documents/first-file.pdf', + }); + + expect(sharedMockStore.set).toHaveBeenCalledWith( + 'lastDownloadDirectory', + path.dirname('/Users/test/Documents/first-file.pdf') + ); + + // Second download should use stored directory + const secondItem = createMockDownloadItem('second-file.xlsx'); + sharedMockStore.get.mockReturnValueOnce('/Users/test/Documents'); + onStartedCallback(secondItem); + + expect(secondItem.setSaveDialogOptions).toHaveBeenCalledWith({ + defaultPath: path.join('/Users/test/Documents', 'second-file.xlsx'), + }); + }); + + it('should handle multiple directory changes in sequence', () => { + expect(onStartedCallback).toBeDefined(); + expect(onCompletedCallback).toBeDefined(); + + const testSequence = [ + { + filename: 'file1.pdf', + savePath: '/Users/test/Downloads/file1.pdf', + expectedDir: '/Users/test/Downloads', + }, + { + filename: 'file2.docx', + savePath: '/Users/test/Documents/file2.docx', + expectedDir: '/Users/test/Documents', + }, + { + filename: 'file3.jpg', + savePath: '/Users/test/Pictures/file3.jpg', + expectedDir: '/Users/test/Pictures', + }, + ]; + + testSequence.forEach(({ filename, savePath }, index) => { + const mockItem = createMockDownloadItem(filename); + + // Mock store to return previous directory + if (index > 0) { + sharedMockStore.get.mockReturnValueOnce( + path.dirname(testSequence[index - 1].savePath) + ); + } else { + sharedMockStore.get.mockReturnValueOnce('/Users/test/Downloads'); + } + + onStartedCallback(mockItem); + onCompletedCallback({ filename, path: savePath }); + + expect(sharedMockStore.set).toHaveBeenCalledWith( + 'lastDownloadDirectory', + path.dirname(savePath) + ); + }); + }); + }); + + describe('Error Handling and Edge Cases', () => { + let onStartedCallback: (item: DownloadItem) => void; + let onCompletedCallback: (file: { filename: string; path: string }) => void; + + beforeEach(() => { + setupElectronDlWithTracking(); + const electronDlCall = electronDlMock.mock.calls[0]; + if (electronDlCall?.[0]) { + onStartedCallback = electronDlCall[0].onStarted!; + onCompletedCallback = electronDlCall[0].onCompleted! as any; + } + }); + + it('should handle undefined or null callbacks gracefully', () => { + // Clear mocks and setup without callbacks + jest.clearAllMocks(); + electronDlMock.mockImplementation(() => {}); + + expect(() => { + setupElectronDlWithTracking(); + }).not.toThrow(); + }); + + it('should handle missing webContents', () => { + expect(onStartedCallback).toBeDefined(); + + webContentsMock.getAllWebContents.mockReturnValue([]); + const mockItem = createMockDownloadItem('test.pdf'); + + expect(() => onStartedCallback(mockItem)).not.toThrow(); + }); + + it('should handle webContents with missing isDestroyed method', () => { + expect(onStartedCallback).toBeDefined(); + + const mockWebContents = { id: 123 } as any; + webContentsMock.getAllWebContents.mockReturnValue([mockWebContents]); + + const mockItem = createMockDownloadItem('test.pdf'); + + expect(() => onStartedCallback(mockItem)).not.toThrow(); + }); + + it('should handle path operations on malformed paths', () => { + expect(onCompletedCallback).toBeDefined(); + + const malformedPaths = [ + { filename: 'test.pdf', path: '' }, + { filename: 'test.pdf', path: '/' }, + { filename: 'test.pdf', path: 'no-slash-path' }, + { filename: 'test.pdf', path: '/single/file.pdf' }, + ]; + + malformedPaths.forEach((file) => { + expect(() => onCompletedCallback(file)).not.toThrow(); + }); + }); + }); +}); + +describe('Download Regression Prevention', () => { + let electronDlMock: jest.MockedFunction; + let appMock: jest.Mocked; + + beforeEach(() => { + jest.clearAllMocks(); + + electronDlMock = electronDl as jest.MockedFunction; + appMock = app as jest.Mocked; + + // Set up default behaviors + appMock.getPath.mockReturnValue('/Users/test/Downloads'); + sharedMockStore.get.mockReturnValue('/Users/test/Downloads'); + sharedMockStore.set.mockReturnValue(undefined); + }); + + const createMockDownloadItem = ( + filename = 'test-file.pdf' + ): jest.Mocked => + ({ + getFilename: jest.fn(() => filename), + setSaveDialogOptions: jest.fn(), + on: jest.fn(), + }) as any; + + it('should maintain download folder persistence after app restarts', () => { + // First session - simulate download completion + setupElectronDlWithTracking(); + const electronDlCall = electronDlMock.mock.calls[0]; + const onCompletedCallback = electronDlCall?.[0]?.onCompleted as any; + + expect(onCompletedCallback).toBeDefined(); + + // Simulate download completion + onCompletedCallback({ + filename: 'test-file.pdf', + path: '/Users/test/Custom/test-file.pdf', + }); + + // Verify directory was stored + expect(sharedMockStore.set).toHaveBeenCalledWith( + 'lastDownloadDirectory', + path.dirname('/Users/test/Custom/test-file.pdf') + ); + + // Simulate app restart by clearing mocks and setting up again + jest.clearAllMocks(); + sharedMockStore.get.mockReturnValue('/Users/test/Custom'); // Simulate persisted value + + setupElectronDlWithTracking(); + const newElectronDlCall = electronDlMock.mock.calls[0]; + const newOnStartedCallback = newElectronDlCall?.[0]?.onStarted; + + expect(newOnStartedCallback).toBeDefined(); + + // Test new download uses persisted directory + const mockItem = createMockDownloadItem('new-file.xlsx'); + + if (newOnStartedCallback) { + newOnStartedCallback(mockItem); + + expect(mockItem.setSaveDialogOptions).toHaveBeenCalledWith({ + defaultPath: path.join('/Users/test/Custom', 'new-file.xlsx'), + }); + } + }); + + it('should persist preference changes across multiple sessions', () => { + const sessionData = [ + { dir: '/Users/test/Downloads', filename: 'file1.pdf' }, + { dir: '/Users/test/Documents', filename: 'file2.docx' }, + { dir: '/Users/test/Pictures', filename: 'file3.jpg' }, + ]; + + sessionData.forEach(({ dir, filename }, sessionIndex) => { + // Setup new session + jest.clearAllMocks(); + + // Mock persisted directory from previous session + if (sessionIndex > 0) { + sharedMockStore.get.mockReturnValue(sessionData[sessionIndex - 1].dir); + } else { + sharedMockStore.get.mockReturnValue('/Users/test/Downloads'); + } + + setupElectronDlWithTracking(); + const electronDlCall = electronDlMock.mock.calls[0]; + const onStartedCallback = electronDlCall?.[0]?.onStarted; + const onCompletedCallback = electronDlCall?.[0]?.onCompleted as any; + + expect(onStartedCallback).toBeDefined(); + expect(onCompletedCallback).toBeDefined(); + + // Test download uses persisted directory + const mockItem = createMockDownloadItem(filename); + + if (onStartedCallback) { + onStartedCallback(mockItem); + + // Verify it used the expected directory + const expectedStartDir = + sessionIndex > 0 + ? sessionData[sessionIndex - 1].dir + : '/Users/test/Downloads'; + + expect(mockItem.setSaveDialogOptions).toHaveBeenCalledWith({ + defaultPath: path.join(expectedStartDir, filename), + }); + } + + // Complete download to new directory + if (onCompletedCallback) { + onCompletedCallback({ + filename, + path: path.join(dir, filename), + }); + + // Verify new directory was stored + expect(sharedMockStore.set).toHaveBeenCalledWith( + 'lastDownloadDirectory', + path.dirname(path.join(dir, filename)) + ); + } + }); + }); + + it('should handle complete failure scenarios gracefully', () => { + // Test when everything fails + sharedMockStore.get.mockImplementation(() => { + throw new Error('Complete store failure'); + }); + sharedMockStore.set.mockImplementation(() => { + throw new Error('Complete store failure'); + }); + appMock.getPath.mockImplementation(() => { + throw new Error('App path failure'); + }); + + expect(() => { + setupElectronDlWithTracking(); + + const electronDlCall = electronDlMock.mock.calls[0]; + const callbacks = electronDlCall?.[0]; + + if (callbacks?.onStarted) { + const mockItem = createMockDownloadItem('fail-test.pdf'); + callbacks.onStarted(mockItem); + } + + if (callbacks?.onCompleted) { + (callbacks.onCompleted as any)({ + filename: 'fail-test.pdf', + path: '/some/path/fail-test.pdf', + }); + } + }).not.toThrow(); + }); +}); diff --git a/src/downloads/main/setup.ts b/src/downloads/main/setup.ts new file mode 100644 index 0000000000..63758a11f0 --- /dev/null +++ b/src/downloads/main/setup.ts @@ -0,0 +1,119 @@ +import { existsSync, statSync } from 'fs'; +import os from 'os'; +import path from 'path'; + +import { app, webContents } from 'electron'; +import electronDl from 'electron-dl'; +import ElectronStore from 'electron-store'; +import { t } from 'i18next'; + +import { createNotification } from '../../notifications/preload'; +import { handleWillDownloadEvent } from '../main'; + +type DownloadPrefs = { + lastDownloadDirectory?: string; +}; + +// Lazy default download path resolution +let cachedDefaultDownloadPath: string | null = null; + +function getDefaultDownloadPath(): string { + if (cachedDefaultDownloadPath === null) { + try { + cachedDefaultDownloadPath = app.getPath('downloads'); + } catch { + cachedDefaultDownloadPath = os.tmpdir(); + } + } + return cachedDefaultDownloadPath; +} + +function isValidDirectory(dir: string): boolean { + try { + return !!dir && existsSync(dir) && statSync(dir).isDirectory(); + } catch { + return false; + } +} + +const downloadStore = new ElectronStore({ + name: 'download-preferences', + schema: { + lastDownloadDirectory: { type: 'string' }, + }, +}); + +export const setupElectronDlWithTracking = () => { + electronDl({ + saveAs: true, + onStarted: (item) => { + try { + // Set the save dialog options with both directory and filename + const configuredDir = downloadStore.get( + 'lastDownloadDirectory', + getDefaultDownloadPath() + ); + const lastDownloadDir = + configuredDir && isValidDirectory(configuredDir) + ? configuredDir + : getDefaultDownloadPath(); + const fullPath = path.join(lastDownloadDir, item.getFilename()); + + item.setSaveDialogOptions({ + defaultPath: fullPath, + }); + + // Find the webContents that initiated this download + // According to electron-dl docs, onStarted only receives DownloadItem + // We need to find an active webContents for tracking purposes + const webContentsArray = webContents.getAllWebContents(); + + // Use the first available webContents for tracking + let sourceWebContents = null; + for (const wc of webContentsArray) { + if (wc && typeof wc.isDestroyed === 'function' && !wc.isDestroyed()) { + sourceWebContents = wc; + break; + } + } + + if (sourceWebContents) { + const fakeEvent = { + defaultPrevented: false, + preventDefault: () => {}, + }; + handleWillDownloadEvent( + fakeEvent as any, + item, + sourceWebContents + ).catch(() => { + // Silently handle tracking errors + }); + } + } catch { + // Silently handle any other errors in onStarted + } + }, + onCompleted: (file) => { + try { + // Remember the directory where the file was saved + if (file.path) { + const downloadDirectory = path.dirname(file.path); + downloadStore.set('lastDownloadDirectory', downloadDirectory); + } + + createNotification({ + title: t('downloads.title', { defaultValue: 'Downloads' }), + body: file.filename || 'Unknown file', + subtitle: t('downloads.notifications.downloadFinished'), + category: 'DOWNLOADS', + }); + } catch { + // Silently handle any errors in onCompleted + } + }, + }); +}; + +// Export the store for testing purposes +export { downloadStore }; diff --git a/src/downloads/notifications.spec.ts b/src/downloads/notifications.spec.ts new file mode 100644 index 0000000000..8728d31bfa --- /dev/null +++ b/src/downloads/notifications.spec.ts @@ -0,0 +1,406 @@ +import type { DownloadItem } from 'electron'; +import { t } from 'i18next'; + +import { createNotification } from '../notifications/preload'; + +// Mock modules +jest.mock('../notifications/preload', () => ({ + createNotification: jest.fn(), +})); + +jest.mock('i18next', () => ({ + t: jest.fn((key: string) => { + const translations: Record = { + 'downloads.notifications.downloadFinished': 'Download finished', + 'downloads.notifications.downloadCancelled': 'Download cancelled', + }; + return translations[key] || key; + }), +})); + +describe('download notifications', () => { + let createNotificationMock: jest.MockedFunction; + let tMock: jest.MockedFunction; + + beforeEach(() => { + jest.clearAllMocks(); + createNotificationMock = createNotification as jest.MockedFunction< + typeof createNotification + >; + tMock = t as jest.MockedFunction; + }); + + const createMockDownloadItem = ( + overrides: Partial = {} + ): jest.Mocked => { + return { + getFilename: jest.fn(() => 'test-file.pdf'), + getState: jest.fn(() => 'completed'), + isPaused: jest.fn(() => false), + getReceivedBytes: jest.fn(() => 2048), + getTotalBytes: jest.fn(() => 2048), + getStartTime: jest.fn(() => 1640995200), + getURL: jest.fn(() => 'https://example.com/file.pdf'), + getMimeType: jest.fn(() => 'application/pdf'), + getSavePath: jest.fn(() => '/downloads/test-file.pdf'), + on: jest.fn(), + ...overrides, + } as unknown as jest.Mocked; + }; + + describe('electron-dl integration notifications', () => { + it('should create notification when download completes via electron-dl', () => { + // Simulate the electron-dl onCompleted callback behavior + const mockFile = { filename: 'completed-file.pdf' }; + + // This is what happens in main.ts electron-dl onCompleted callback + createNotificationMock({ + title: 'Downloads', + body: mockFile.filename, + subtitle: tMock('downloads.notifications.downloadFinished'), + category: 'DOWNLOADS', + }); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: 'completed-file.pdf', + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + + expect(tMock).toHaveBeenCalledWith( + 'downloads.notifications.downloadFinished' + ); + }); + + it('should handle different file types in notifications', () => { + const testCases = [ + { filename: 'document.pdf', expected: 'document.pdf' }, + { filename: 'image.jpg', expected: 'image.jpg' }, + { filename: 'archive.zip', expected: 'archive.zip' }, + { filename: 'spreadsheet.xlsx', expected: 'spreadsheet.xlsx' }, + { filename: 'presentation.pptx', expected: 'presentation.pptx' }, + { + filename: 'very-long-filename-that-might-be-truncated.docx', + expected: 'very-long-filename-that-might-be-truncated.docx', + }, + ]; + + testCases.forEach(({ filename, expected }) => { + createNotificationMock.mockClear(); + + createNotificationMock({ + title: 'Downloads', + body: filename, + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: expected, + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + }); + }); + + it('should handle special characters in filenames', () => { + const specialFilenames = [ + 'file with spaces.pdf', + 'file-with-dashes.txt', + 'file_with_underscores.docx', + 'файл-на-русском.pdf', + '文件名-中文.txt', + 'file@#$%^&*()+={}[]|\\:";\'<>?,.pdf', + ]; + + specialFilenames.forEach((filename) => { + createNotificationMock.mockClear(); + + createNotificationMock({ + title: 'Downloads', + body: filename, + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: filename, + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + }); + }); + }); + + describe('legacy download item notifications', () => { + it('should create notification on download completion via item.on("done")', () => { + const mockItem = createMockDownloadItem(); + + // Capture the 'done' event listener + let doneListener: (event: any, state: string) => void; + mockItem.on.mockImplementation(((event: string, listener: any) => { + if (event === 'done') { + doneListener = listener; + } + return mockItem; + }) as any); + + // Simulate setting up the listener (this happens in handleWillDownloadEvent) + mockItem.on('done', (_event, state) => { + createNotificationMock({ + title: 'Downloads', + body: mockItem.getFilename(), + subtitle: + state === 'completed' + ? tMock('downloads.notifications.downloadFinished') + : tMock('downloads.notifications.downloadCancelled'), + category: 'DOWNLOADS', + }); + }); + + // Simulate download completion + doneListener!({}, 'completed'); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: 'test-file.pdf', + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + }); + + it('should create appropriate notification on download cancellation', () => { + const mockItem = createMockDownloadItem(); + + let doneListener: (event: any, state: string) => void; + mockItem.on.mockImplementation(((event: string, listener: any) => { + if (event === 'done') { + doneListener = listener; + } + return mockItem; + }) as any); + + mockItem.on('done', (_event, state) => { + createNotificationMock({ + title: 'Downloads', + body: mockItem.getFilename(), + subtitle: + state === 'completed' + ? tMock('downloads.notifications.downloadFinished') + : tMock('downloads.notifications.downloadCancelled'), + category: 'DOWNLOADS', + }); + }); + + // Simulate download cancellation + doneListener!({}, 'cancelled'); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: 'test-file.pdf', + subtitle: 'Download cancelled', + category: 'DOWNLOADS', + }); + }); + + it('should handle different download states appropriately', () => { + const mockItem = createMockDownloadItem(); + + let doneListener: (event: any, state: string) => void; + mockItem.on.mockImplementation(((event: string, listener: any) => { + if (event === 'done') { + doneListener = listener; + } + return mockItem; + }) as any); + + mockItem.on('done', (_event, state) => { + createNotificationMock({ + title: 'Downloads', + body: mockItem.getFilename(), + subtitle: + state === 'completed' + ? tMock('downloads.notifications.downloadFinished') + : tMock('downloads.notifications.downloadCancelled'), + category: 'DOWNLOADS', + }); + }); + + // Test different states + const testStates = ['completed', 'cancelled', 'interrupted']; + + testStates.forEach((state) => { + createNotificationMock.mockClear(); + tMock.mockClear(); + + doneListener!({}, state); + + if (state === 'completed') { + expect(tMock).toHaveBeenCalledWith( + 'downloads.notifications.downloadFinished' + ); + } else { + expect(tMock).toHaveBeenCalledWith( + 'downloads.notifications.downloadCancelled' + ); + } + }); + }); + }); + + describe('notification behavior edge cases', () => { + it('should handle empty filename gracefully', () => { + createNotificationMock({ + title: 'Downloads', + body: '', + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: '', + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + }); + + it('should handle missing translation keys', () => { + tMock.mockReturnValue('missing.translation.key'); + + createNotificationMock({ + title: 'Downloads', + body: 'test-file.pdf', + subtitle: tMock('missing.translation.key'), + category: 'DOWNLOADS', + }); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: 'test-file.pdf', + subtitle: 'missing.translation.key', + category: 'DOWNLOADS', + }); + }); + + it('should maintain consistent notification title', () => { + // All download notifications should use 'Downloads' as title + const testFiles = ['file1.pdf', 'file2.jpg', 'file3.zip']; + + testFiles.forEach((filename) => { + createNotificationMock.mockClear(); + + createNotificationMock({ + title: 'Downloads', + body: filename, + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + + expect(createNotificationMock).toHaveBeenCalledWith( + expect.objectContaining({ + title: 'Downloads', + category: 'DOWNLOADS', + }) + ); + }); + }); + }); + + describe('internationalization', () => { + it('should use translated messages for different languages', () => { + // Mock different language translations + const mockTranslations = { + 'en': 'Download finished', + 'es': 'Descarga completada', + 'fr': 'Téléchargement terminé', + 'de': 'Download abgeschlossen', + 'pt-BR': 'Download concluído', + }; + + Object.entries(mockTranslations).forEach(([_locale, translation]) => { + tMock.mockReturnValue(translation); + createNotificationMock.mockClear(); + + createNotificationMock({ + title: 'Downloads', + body: 'test-file.pdf', + subtitle: tMock('downloads.notifications.downloadFinished'), + category: 'DOWNLOADS', + }); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: 'test-file.pdf', + subtitle: translation, + category: 'DOWNLOADS', + }); + }); + }); + + it('should handle cancellation messages in different languages', () => { + const mockTranslations = { + 'en': 'Download cancelled', + 'es': 'Descarga cancelada', + 'fr': 'Téléchargement annulé', + 'de': 'Download abgebrochen', + 'pt-BR': 'Download cancelado', + }; + + Object.entries(mockTranslations).forEach(([_locale, translation]) => { + tMock.mockReturnValue(translation); + createNotificationMock.mockClear(); + + createNotificationMock({ + title: 'Downloads', + body: 'test-file.pdf', + subtitle: tMock('downloads.notifications.downloadCancelled'), + }); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: 'test-file.pdf', + subtitle: translation, + }); + }); + }); + }); + + describe('notification frequency and timing', () => { + it('should only create notification once per download completion', () => { + // Simulate electron-dl onCompleted callback + const mockFile = { filename: 'test-file.pdf' }; + + createNotificationMock({ + title: 'Downloads', + body: mockFile.filename, + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + + expect(createNotificationMock).toHaveBeenCalledTimes(1); + }); + + it('should create separate notifications for multiple downloads', () => { + const downloads = [ + { filename: 'file1.pdf' }, + { filename: 'file2.jpg' }, + { filename: 'file3.zip' }, + ]; + + downloads.forEach((file) => { + createNotificationMock({ + title: 'Downloads', + body: file.filename, + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + }); + + expect(createNotificationMock).toHaveBeenCalledTimes(3); + }); + }); +}); diff --git a/src/downloads/reducers/downloads.spec.ts b/src/downloads/reducers/downloads.spec.ts new file mode 100644 index 0000000000..a47de02a9d --- /dev/null +++ b/src/downloads/reducers/downloads.spec.ts @@ -0,0 +1,337 @@ +import { + DOWNLOAD_CREATED, + DOWNLOAD_REMOVED, + DOWNLOAD_UPDATED, + DOWNLOADS_CLEARED, +} from '../actions'; +import type { Download } from '../common'; +import { DownloadStatus } from '../common'; +import { downloads } from './downloads'; + +describe('downloads reducer', () => { + const mockDownload: Download = { + itemId: 1640995200000, + state: 'progressing', + status: DownloadStatus.ALL, + fileName: 'test-file.pdf', + receivedBytes: 1024, + totalBytes: 2048, + startTime: 1640995200000, + endTime: undefined, + url: 'https://example.com/file.pdf', + serverUrl: 'https://open.rocket.chat', + serverTitle: 'Rocket.Chat Community', + mimeType: 'application/pdf', + savePath: '/downloads/test-file.pdf', + }; + + it('should return initial state', () => { + expect(downloads(undefined, { type: 'UNKNOWN_ACTION' } as any)).toEqual({}); + }); + + describe('DOWNLOAD_CREATED', () => { + it('should add a new download to the state', () => { + const action = { + type: DOWNLOAD_CREATED, + payload: mockDownload, + } as const; + + const newState = downloads({}, action as any); + + expect(newState).toEqual({ + [mockDownload.itemId]: mockDownload, + }); + }); + + it('should add download to existing state without affecting other downloads', () => { + const existingDownload: Download = { + ...mockDownload, + itemId: 1640995100000, + fileName: 'existing-file.txt', + }; + + const initialState = { + [existingDownload.itemId]: existingDownload, + }; + + const newDownload: Download = { + ...mockDownload, + itemId: 1640995300000, + fileName: 'new-file.pdf', + }; + + const action = { + type: DOWNLOAD_CREATED, + payload: newDownload, + }; + + const newState = downloads(initialState, action as any); + + expect(newState).toEqual({ + [existingDownload.itemId]: existingDownload, + [newDownload.itemId]: newDownload, + }); + }); + }); + + describe('DOWNLOAD_UPDATED', () => { + it('should update an existing download', () => { + const initialState = { + [mockDownload.itemId]: mockDownload, + }; + + const updatePayload = { + itemId: mockDownload.itemId, + receivedBytes: 1536, + state: 'progressing' as const, + endTime: 1640995300000, + }; + + const action = { + type: DOWNLOAD_UPDATED, + payload: updatePayload, + }; + + const newState = downloads(initialState, action as any); + + expect(newState[mockDownload.itemId]).toEqual({ + ...mockDownload, + receivedBytes: 1536, + endTime: 1640995300000, + }); + }); + + it('should handle download completion', () => { + const initialState = { + [mockDownload.itemId]: mockDownload, + }; + + const updatePayload = { + itemId: mockDownload.itemId, + state: 'completed' as const, + status: DownloadStatus.ALL, + endTime: 1640995400000, + }; + + const action = { + type: DOWNLOAD_UPDATED, + payload: updatePayload, + }; + + const newState = downloads(initialState, action as any); + + expect(newState[mockDownload.itemId]).toEqual({ + ...mockDownload, + state: 'completed', + endTime: 1640995400000, + }); + }); + + it('should handle download cancellation', () => { + const initialState = { + [mockDownload.itemId]: mockDownload, + }; + + const updatePayload = { + itemId: mockDownload.itemId, + state: 'cancelled' as const, + status: DownloadStatus.CANCELLED, + endTime: 1640995400000, + }; + + const action = { + type: DOWNLOAD_UPDATED, + payload: updatePayload, + }; + + const newState = downloads(initialState, action as any); + + expect(newState[mockDownload.itemId]).toEqual({ + ...mockDownload, + state: 'cancelled', + status: DownloadStatus.CANCELLED, + endTime: 1640995400000, + }); + }); + + it('should do nothing if download does not exist', () => { + const initialState = { + [mockDownload.itemId]: mockDownload, + }; + + const updatePayload = { + itemId: 9999999999999, // Non-existent itemId + receivedBytes: 1536, + }; + + const action = { + type: DOWNLOAD_UPDATED, + payload: updatePayload, + }; + + const newState = downloads(initialState, action as any); + + expect(newState).toEqual(initialState); + }); + }); + + describe('DOWNLOAD_REMOVED', () => { + it('should remove a download from the state', () => { + const anotherDownload: Download = { + ...mockDownload, + itemId: 1640995300000, + fileName: 'another-file.txt', + }; + + const initialState = { + [mockDownload.itemId]: mockDownload, + [anotherDownload.itemId]: anotherDownload, + }; + + const action = { + type: DOWNLOAD_REMOVED, + payload: mockDownload.itemId, + }; + + const newState = downloads(initialState, action as any); + + expect(newState).toEqual({ + [anotherDownload.itemId]: anotherDownload, + }); + }); + + it('should do nothing if download does not exist', () => { + const initialState = { + [mockDownload.itemId]: mockDownload, + }; + + const action = { + type: DOWNLOAD_REMOVED, + payload: 9999999999999, // Non-existent itemId + }; + + const newState = downloads(initialState, action as any); + + expect(newState).toEqual(initialState); + }); + }); + + describe('DOWNLOADS_CLEARED', () => { + it('should clear all downloads from the state', () => { + const anotherDownload: Download = { + ...mockDownload, + itemId: 1640995300000, + fileName: 'another-file.txt', + }; + + const initialState = { + [mockDownload.itemId]: mockDownload, + [anotherDownload.itemId]: anotherDownload, + }; + + const action = { + type: DOWNLOADS_CLEARED, + }; + + const newState = downloads(initialState, action as any); + + expect(newState).toEqual({}); + }); + + it('should return empty state when already empty', () => { + const action = { + type: DOWNLOADS_CLEARED, + }; + + const newState = downloads({}, action as any); + + expect(newState).toEqual({}); + }); + }); + + describe('edge cases', () => { + it('should handle partial update payloads', () => { + const initialState = { + [mockDownload.itemId]: mockDownload, + }; + + const action = { + type: DOWNLOAD_UPDATED, + payload: { + itemId: mockDownload.itemId, + receivedBytes: 1800, + // Only updating receivedBytes, other fields should remain unchanged + }, + }; + + const newState = downloads(initialState, action as any); + + expect(newState[mockDownload.itemId]).toEqual({ + ...mockDownload, + receivedBytes: 1800, + }); + }); + + it('should preserve immutability - not mutate original state', () => { + const initialState = { + [mockDownload.itemId]: mockDownload, + }; + + const action = { + type: DOWNLOAD_UPDATED, + payload: { + itemId: mockDownload.itemId, + receivedBytes: 1800, + }, + }; + + const newState = downloads(initialState, action as any); + + // Original state should not be mutated + expect(initialState[mockDownload.itemId].receivedBytes).toBe(1024); + expect(newState[mockDownload.itemId].receivedBytes).toBe(1800); + expect(newState).not.toBe(initialState); + }); + + it('should handle multiple download status types', () => { + const pausedDownload: Download = { + ...mockDownload, + itemId: 1640995300000, + state: 'paused', + status: DownloadStatus.PAUSED, + }; + + const cancelledDownload: Download = { + ...mockDownload, + itemId: 1640995400000, + state: 'cancelled', + status: DownloadStatus.CANCELLED, + }; + + let state: Record = {}; + + // Add downloads with different statuses + state = downloads(state, { + type: DOWNLOAD_CREATED, + payload: mockDownload, + } as any); + + state = downloads(state, { + type: DOWNLOAD_CREATED, + payload: pausedDownload, + } as any); + + state = downloads(state, { + type: DOWNLOAD_CREATED, + payload: cancelledDownload, + } as any); + + expect(Object.keys(state)).toHaveLength(3); + expect(state[mockDownload.itemId]?.status).toBe(DownloadStatus.ALL); + expect(state[pausedDownload.itemId]?.status).toBe(DownloadStatus.PAUSED); + expect(state[cancelledDownload.itemId]?.status).toBe( + DownloadStatus.CANCELLED + ); + }); + }); +}); diff --git a/src/downloads/reducers/downloads.ts b/src/downloads/reducers/downloads.ts index 601b1c22d1..51f3d91f14 100644 --- a/src/downloads/reducers/downloads.ts +++ b/src/downloads/reducers/downloads.ts @@ -41,9 +41,14 @@ export const downloads = ( } case DOWNLOAD_UPDATED: { + const existingDownload = state[action.payload.itemId]; + if (!existingDownload) { + return state; // Don't update if download doesn't exist + } + const newState = { ...state }; newState[action.payload.itemId] = { - ...newState[action.payload.itemId], + ...existingDownload, ...action.payload, }; return newState; diff --git a/src/errors.ts b/src/errors.ts index c8edd08285..62829ac9f8 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,10 +1,99 @@ import Bugsnag from '@bugsnag/js'; +import { app } from 'electron'; import { select, listen } from './store'; import { SETTINGS_SET_REPORT_OPT_IN_CHANGED } from './ui/actions'; type AppType = 'main' | 'rootWindow'; +const DEFAULT_CRITICAL_PATTERNS = [ + 'FATAL', + 'Cannot access native module', + 'Electron internal error', +]; + +export type CriticalErrorMatcher = (error: Error) => boolean; + +let _criticalMatcher: CriticalErrorMatcher | null = null; + +/** + * Set a custom critical error matcher. Returns a cleanup function that restores the previous matcher. + * @param fn - Custom matcher function or null to reset to default + * @returns Cleanup function that restores the previous matcher + * @example + * const restore = setCriticalErrorMatcher((err) => err.message.includes('FATAL')); + * try { + * // ... code that might throw ... + * } finally { + * restore(); // Restores previous matcher + * } + */ +export const setCriticalErrorMatcher = ( + fn: CriticalErrorMatcher | null +): (() => void) => { + const previous = _criticalMatcher; + _criticalMatcher = fn; + + return () => { + _criticalMatcher = previous; + }; +}; + +const isCriticalError = (error: Error): boolean => { + if (_criticalMatcher) { + try { + return _criticalMatcher(error); + } catch (matcherError) { + console.error('Critical error matcher failed:', matcherError); + // Fall through to default behavior + } + } + + return DEFAULT_CRITICAL_PATTERNS.some( + (pattern) => + error.message?.includes(pattern) || error.stack?.includes(pattern) + ); +}; + +let _globalHandlersBound = false; + +const setupGlobalErrorHandlers = (): void => { + if (_globalHandlersBound) return; + + process.on('uncaughtException', (error: Error) => { + console.error('Uncaught Exception:', error); + + if (Bugsnag.isStarted()) { + Bugsnag.notify(error); + } + + if (isCriticalError(error)) { + console.error('Critical error detected, app cannot continue'); + app.quit(); + } + }); + + process.on('unhandledRejection', (reason: unknown) => { + const error = + reason instanceof Error + ? reason + : new Error(`Unhandled Promise Rejection: ${String(reason)}`); + + console.error('Unhandled Promise Rejection:', error); + + if (Bugsnag.isStarted()) { + Bugsnag.notify(error); + } + + if (isCriticalError(error)) { + console.error('Critical promise rejection, app cannot continue'); + app.quit(); + } + }); + + _globalHandlersBound = true; +}; + const initBugsnag = (apiKey: string, appVersion: string, appType: AppType) => Bugsnag.start({ apiKey, @@ -12,6 +101,7 @@ const initBugsnag = (apiKey: string, appVersion: string, appType: AppType) => appType, releaseStage: process.env.NODE_ENV, redactedKeys: [/\/Users\/[^\/]+/], + autoTrackSessions: false, }); const listenToBugsnagEnabledToggle = async (appType: AppType) => { @@ -22,14 +112,16 @@ const listenToBugsnagEnabledToggle = async (appType: AppType) => { const { appVersion, isReportEnabled } = select( ({ appVersion, isReportEnabled }) => ({ appVersion, isReportEnabled }) ); - if (!appVersion) { - throw new Error('app version was not set'); + const effectiveVersion = appVersion || app.getVersion(); + if (!effectiveVersion) { + console.warn('Bugsnag init deferred: app version not available yet'); + return; } let bugsnagInstance: ReturnType; if (isReportEnabled && !process.mas) { - bugsnagInstance = initBugsnag(apiKey, appVersion, appType); + bugsnagInstance = initBugsnag(apiKey, effectiveVersion, appType); bugsnagInstance.startSession(); } @@ -38,7 +130,7 @@ const listenToBugsnagEnabledToggle = async (appType: AppType) => { if (isReportEnabled) { bugsnagInstance = - bugsnagInstance || initBugsnag(apiKey, appVersion, appType); + bugsnagInstance || initBugsnag(apiKey, effectiveVersion, appType); bugsnagInstance.startSession(); } else { bugsnagInstance?.pauseSession(); @@ -49,8 +141,10 @@ const listenToBugsnagEnabledToggle = async (appType: AppType) => { export const setupRendererErrorHandling = async ( appType: AppType ): Promise => { - listenToBugsnagEnabledToggle(appType); + await listenToBugsnagEnabledToggle(appType); }; -export const setupMainErrorHandling = async (): Promise => - setupRendererErrorHandling('main'); +export const setupMainErrorHandling = async (): Promise => { + setupGlobalErrorHandlers(); + await setupRendererErrorHandling('main'); +}; diff --git a/src/errors/main.spec.ts b/src/errors/main.spec.ts new file mode 100644 index 0000000000..d71f96ca35 --- /dev/null +++ b/src/errors/main.spec.ts @@ -0,0 +1,256 @@ +/** + * Integration tests for Bugsnag network behavior. + * + * These tests use nock to intercept real HTTP requests from the Bugsnag SDK, + * verifying that no network calls are made when error reporting is disabled. + * + * Note: Some tests require waiting for Bugsnag's 10-second session batching + * interval, resulting in ~30 second total test time. + */ +import nock from 'nock'; + +import { SETTINGS_SET_REPORT_OPT_IN_CHANGED } from '../ui/actions'; + +const TEST_API_KEY = '12345678901234567890123456789012'; +const TEST_APP_VERSION = '4.12.0'; + +// Bugsnag batches sessions and sends them on a 10-second interval +const BUGSNAG_SESSION_INTERVAL_MS = 10000; +const WAIT_FOR_SESSION_BATCH_MS = BUGSNAG_SESSION_INTERVAL_MS + 2000; +const SHORT_WAIT_MS = 500; + +type SettingsChangedCallback = (action: { + type: string; + payload: boolean; +}) => void; + +/** + * Creates standard mocks for store and electron modules + */ +const createMocks = ( + isReportEnabled: boolean, + onListen?: (callback: SettingsChangedCallback) => void +) => { + jest.doMock('../store', () => ({ + select: jest.fn(() => ({ + appVersion: TEST_APP_VERSION, + isReportEnabled, + })), + listen: jest.fn((type: unknown, callback: unknown) => { + if (type === SETTINGS_SET_REPORT_OPT_IN_CHANGED && onListen) { + onListen(callback as SettingsChangedCallback); + } + return () => {}; + }), + })); + + jest.doMock('electron', () => ({ + app: { + getVersion: jest.fn(() => TEST_APP_VERSION), + quit: jest.fn(), + }, + })); +}; + +/** + * Sets up nock interceptors for Bugsnag endpoints and returns a call tracker + */ +const interceptBugsnagCalls = () => { + const tracker = { sessionCalls: 0, notifyCalls: 0 }; + + nock('https://sessions.bugsnag.com') + .post(() => true) + .times(100) + .reply(() => { + tracker.sessionCalls++; + return [200, {}]; + }); + + nock('https://notify.bugsnag.com') + .post(() => true) + .times(100) + .reply(() => { + tracker.notifyCalls++; + return [200, {}]; + }); + + return tracker; +}; + +const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); + +/** + * Environment variable mock helper using Object.defineProperty. + * Stores original descriptors to restore after tests. + */ +const envMocks = new Map< + string, + { descriptor: PropertyDescriptor | undefined; existed: boolean } +>(); + +const setEnvVar = (key: string, value: string): void => { + if (!envMocks.has(key)) { + envMocks.set(key, { + descriptor: Object.getOwnPropertyDescriptor(process.env, key), + existed: key in process.env, + }); + } + Object.defineProperty(process.env, key, { + value, + writable: true, + enumerable: true, + configurable: true, + }); +}; + +const unsetEnvVar = (key: string): void => { + if (!envMocks.has(key)) { + envMocks.set(key, { + descriptor: Object.getOwnPropertyDescriptor(process.env, key), + existed: key in process.env, + }); + } + Object.defineProperty(process.env, key, { + value: undefined, + writable: true, + enumerable: false, + configurable: true, + }); +}; + +const restoreEnvVars = (): void => { + envMocks.forEach(({ descriptor, existed }, key) => { + if (existed && descriptor) { + Object.defineProperty(process.env, key, descriptor); + } else { + Object.defineProperty(process.env, key, { + value: undefined, + writable: true, + enumerable: false, + configurable: true, + }); + } + }); + envMocks.clear(); +}; + +// Skip on Windows due to Jest module mocking issues with Electron runner +const describeOrSkip = process.platform === 'win32' ? describe.skip : describe; + +describeOrSkip('Bugsnag network behavior', () => { + beforeEach(() => { + jest.clearAllMocks(); + jest.resetModules(); + nock.cleanAll(); + nock.disableNetConnect(); + + setEnvVar('BUGSNAG_API_KEY', TEST_API_KEY); + setEnvVar('NODE_ENV', 'test'); + }); + + afterEach(() => { + restoreEnvVars(); + nock.cleanAll(); + nock.enableNetConnect(); + jest.resetModules(); + }); + + describe('when reporting is disabled', () => { + it('should not make any network calls when isReportEnabled is false', async () => { + createMocks(false); + const tracker = interceptBugsnagCalls(); + + const { setupRendererErrorHandling } = await import('../errors'); + await setupRendererErrorHandling('main'); + await wait(SHORT_WAIT_MS); + + expect(tracker.sessionCalls).toBe(0); + expect(tracker.notifyCalls).toBe(0); + }); + + it('should not make any network calls when BUGSNAG_API_KEY is not set', async () => { + unsetEnvVar('BUGSNAG_API_KEY'); + createMocks(true); + const tracker = interceptBugsnagCalls(); + + const { setupRendererErrorHandling } = await import('../errors'); + await setupRendererErrorHandling('main'); + await wait(SHORT_WAIT_MS); + + expect(tracker.sessionCalls).toBe(0); + }); + }); + + describe('when reporting is enabled', () => { + it('should not make immediate session calls (batched on interval)', async () => { + createMocks(true); + const tracker = interceptBugsnagCalls(); + + const { setupRendererErrorHandling } = await import('../errors'); + await setupRendererErrorHandling('main'); + await wait(SHORT_WAIT_MS); + + // Sessions are batched, not sent immediately + expect(tracker.sessionCalls).toBe(0); + }); + + it( + 'should send session after the batching interval', + async () => { + createMocks(true); + const tracker = interceptBugsnagCalls(); + + const { setupRendererErrorHandling } = await import('../errors'); + await setupRendererErrorHandling('main'); + await wait(WAIT_FOR_SESSION_BATCH_MS); + + expect(tracker.sessionCalls).toBeGreaterThanOrEqual(1); + }, + WAIT_FOR_SESSION_BATCH_MS + 3000 + ); + }); + + describe('toggle behavior', () => { + it('should not make network calls when reporting starts disabled', async () => { + let onSettingsChanged: SettingsChangedCallback | null = null; + createMocks(false, (cb) => { + onSettingsChanged = cb; + }); + const tracker = interceptBugsnagCalls(); + + const { setupRendererErrorHandling } = await import('../errors'); + await setupRendererErrorHandling('main'); + + expect(onSettingsChanged).not.toBeNull(); + await wait(SHORT_WAIT_MS); + expect(tracker.sessionCalls).toBe(0); + }); + + it( + 'should start sending sessions after user enables reporting', + async () => { + let onSettingsChanged: SettingsChangedCallback | null = null; + createMocks(false, (cb) => { + onSettingsChanged = cb; + }); + const tracker = interceptBugsnagCalls(); + + const { setupRendererErrorHandling } = await import('../errors'); + await setupRendererErrorHandling('main'); + await wait(SHORT_WAIT_MS); + + expect(tracker.sessionCalls).toBe(0); + + // User enables reporting + onSettingsChanged!({ + type: SETTINGS_SET_REPORT_OPT_IN_CHANGED, + payload: true, + }); + + await wait(WAIT_FOR_SESSION_BATCH_MS); + expect(tracker.sessionCalls).toBeGreaterThanOrEqual(1); + }, + WAIT_FOR_SESSION_BATCH_MS + 3000 + ); + }); +}); diff --git a/src/i18n/ar.i18n.json b/src/i18n/ar.i18n.json index 6f31cf5a2e..a5316a1361 100644 --- a/src/i18n/ar.i18n.json +++ b/src/i18n/ar.i18n.json @@ -1 +1,43 @@ -{ } \ No newline at end of file +{ + "settings": { + "options": { + "transparentWindow": { + "title": "تأثير النافذة الشفافة", + "description": "تمكين تأثير الاهتزاز/الشفافية الأصلي للنافذة. يتطلب إعادة التشغيل للتطبيق." + }, + "themeAppearance": { + "title": "المظهر", + "description": "اختر مظهر الألوان للتطبيق.", + "auto": "اتباع النظام", + "light": "فاتح", + "dark": "داكن" + } + } + }, + "serverInfo": { + "title": "معلومات الخادم", + "urlLabel": "الرابط:", + "versionLabel": "الإصدار:", + "unknown": "غير معروف", + "exchangeUrlLabel": "رابط Outlook Exchange:", + "supportedVersionsTitle": "الإصدارات المدعومة", + "statusLabel": "الحالة:", + "status": { + "loading": "جاري التحميل...", + "error": "فشل التحميل", + "loaded": "تم التحميل", + "idle": "خامل", + "from": "من {{source}}" + }, + "supported": { + "unknown": "غير معروف", + "expiring": "قريب الانتهاء", + "yes": "نعم", + "no": "لا" + }, + "expiration": { + "label": "تاريخ الانتهاء:", + "expiresOn": "ينتهي في {{date}}" + } + } +} diff --git a/src/i18n/de-DE.i18n.json b/src/i18n/de-DE.i18n.json index abe52dc12d..87373115b1 100644 --- a/src/i18n/de-DE.i18n.json +++ b/src/i18n/de-DE.i18n.json @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Beim Start auf Aktualisierungen prüfen", "noUpdatesAvailable": "Es sind keine Aktualisierungen verfügbar.", "copyright": "Copyright {{copyright}}", - "errorWhenLookingForUpdates": "Bei der Suche nach Updates ist ein Fehler aufgetreten" + "errorWhenLookingForUpdates": "Bei der Suche nach Updates ist ein Fehler aufgetreten", + "updateChannel": { + "label": "Update-Kanal", + "latest": "Stabil", + "beta": "Beta", + "alpha": "Alpha (Experimentell)" + } }, "addServer": { "title": "Server hinzufügen", @@ -108,6 +114,28 @@ "dontAskAgain": "Lassen Sie immer zu, dass Videoanrufe von diesem Server Ihren Bildschirm erfassen", "yes": "Erlauben", "no": "Absagen" + }, + "mediaPermission": { + "title": "Medienberechtigung Erforderlich", + "message": "Der Zugriff auf {{- permissionType}} ist derzeit in Ihren Systemeinstellungen deaktiviert.", + "detail": "Um Videoanruf-Funktionen zu aktivieren, erlauben Sie bitte den Zugriff in den Datenschutzeinstellungen Ihres Systems und starten Sie dann die Anwendung neu.", + "openSettings": "Einstellungen Öffnen", + "cancel": "Abbrechen", + "microphone": "Mikrofon", + "camera": "Kamera", + "both": "Mikrofon und Kamera" + }, + "microphonePermissionDenied": { + "title": "Mikrofonzugriff erforderlich", + "message": "Mikrofonzugriff ist erforderlich, um {{- actionType}}. Bitte überprüfen Sie Ihre Systemeinstellungen.", + "detail": "Sie können den Mikrofonzugriff in den Datenschutzeinstellungen Ihres Systems aktivieren.", + "openSettings": "Einstellungen öffnen", + "cancel": "Abbrechen", + "actionTypes": { + "initiateCall": "Anrufe tätigen", + "answerCall": "Anrufe entgegennehmen", + "recordMessage": "Nachrichten aufnehmen" + } } }, "downloads": { @@ -183,6 +211,11 @@ "title": "Hardware-Beschleunigung", "description": "Aktiviert die Verwendung der Hardwarebeschleunigung, sofern verfügbar. Die Anwendung wird bei Änderung neu geladen." }, + "videoCallScreenCaptureFallback": { + "title": "Alternative Bildschirmaufnahme für Videoanrufe", + "description": "Deaktiviert Windows Graphics Capture, damit das Teilen in RDP-Sitzungen funktioniert. Die App startet neu, wenn Sie diese Option ändern.", + "forcedDescription": "Bereits aktiv, weil die Anwendung eine RDP-Sitzung erkannt hat. Der Schalter bestimmt das Verhalten bei zukünftigen lokalen Starts." + }, "internalVideoChatWindow": { "title": "Öffnen Sie den Video-Chat mit dem Anwendungsfenster", "description": "Wenn diese Option aktiviert ist, wird der Video-Chat im Anwendungsfenster anstelle des Standardbrowsers geöffnet. Allerdings wird für Google Meet und Jitsi die Bildschirmaufzeichnung in Electron-Anwendungen nicht unterstützt, daher werden sie unabhängig von dieser Einstellung immer im Browser geöffnet.", @@ -194,11 +227,13 @@ }, "menubar": { "title": "Menüleiste", - "description": "Menüleiste oben im Fenster anzeigen." + "description": "Menüleiste oben im Fenster anzeigen.", + "disabledHint": "Die Menüleiste kann nicht deaktiviert werden, wenn die Seitenleiste bereits deaktiviert ist. Die Einstellungen wären sonst nicht erreichbar." }, "sidebar": { "title": "Seitenleiste", - "description": "Seitenleiste auf der linken Seite des Fensters mit Serverliste, Downloads und Einstellungen anzeigen." + "description": "Seitenleiste auf der linken Seite des Fensters mit Serverliste, Downloads und Einstellungen anzeigen.", + "disabledHint": "Die Seitenleiste kann nicht deaktiviert werden, wenn die Menüleiste bereits deaktiviert ist. Die Einstellungen wären sonst nicht erreichbar." }, "trayIcon": { "title": "Taskleistensymbol", @@ -223,6 +258,17 @@ "videoCallWindowPersistence": { "title": "Videoanruf-Fensterposition speichern", "description": "Position und Größe der Videoanruf-Fenster zwischen Sitzungen speichern und wiederherstellen" + }, + "transparentWindow": { + "title": "Transparentes Fenstereffekt", + "description": "Natives Vibrancy-/Transparenzeffekt für das Fenster aktivieren. Erfordert Neustart zur Anwendung." + }, + "themeAppearance": { + "title": "Design", + "description": "Wählen Sie das Farbschema für die Anwendung.", + "auto": "System folgen", + "light": "Hell", + "dark": "Dunkel" } } }, @@ -242,11 +288,12 @@ "menus": { "about": "Über {{- appName}}", "addNewServer": "&Neuen server hinzufügen", - "back": "&Der Rücken", + "back": "&Zurück", "clearTrustedCertificates": "Vertraute zertifikate löschen", "close": "Schließen", "copy": "&Kopieren", "cut": "&Ausschneiden", + "developerMode": "Entwicklermodus", "disableGpu": "GPU deaktivieren", "documentation": "Dokumentation", "downloads": "Downloads", @@ -287,9 +334,24 @@ "announcement": "Houston, we have a problem", "reload": "Neu laden" }, + "videoCall": { + "loading": { + "initial": "Videoanruf wird geladen...", + "reloading": "Videoanruf wird neu geladen...", + "description": "Bitte warten Sie, während wir eine Verbindung zum Videoanruf herstellen" + }, + "error": { + "title": "Videoanruf konnte nicht geladen werden", + "announcement": "Houston, wir haben ein Problem", + "timeout": "Ladezeitüberschreitung - Videoanruf konnte nicht innerhalb von 15 Sekunden geladen werden", + "crashed": "Webview ist abgestürzt", + "maxRetriesReached": "Laden nach mehreren Versuchen fehlgeschlagen", + "reload": "Videoanruf neu laden" + } + }, "selfxss": { "title": "Halt!", - "description": "Dies ist eine Browserfunktion, die für Entwickler gedacht ist. Wenn Ihnen jemand gesagt hat, dass Sie hier etwas kopieren und einfügen sollen, um eine Rocket.Chat-Funktion zu aktivieren, oder \"hack\" das Konto einer anderen Person, ist dies ein Betrug und verschafft dieser Person Zugriff auf Ihr Rocket.Chat-Konto.", + "description": "Dies ist eine Browserfunktion, die für Entwickler gedacht ist. Wenn Ihnen jemand gesagt hat, dass Sie hier etwas kopieren und einfügen sollen, um eine Rocket.Chat-Funktion zu aktivieren oder um das Konto einer anderen Person zu \"hacken\", ist dies ein Betrug und verschafft dieser Person Zugriff auf Ihr Rocket.Chat-Konto.", "moreInfo": "Weitere Informationen finden Sie unter https://go.rocket.chat/i/xss." }, "sidebar": { @@ -317,7 +379,7 @@ "tooltip": { "noUnreadMessage": "{{- appName}}: keine ungelesene Nachricht", "unreadMention": "{{- appName}}: Sie haben eine ungelesene Erwähnung/Direktnachricht", - "unreadMention_plural": "{{- appName}}: you have {{- count}} ungelesene Erwähnungen/Direktnachrichten", + "unreadMention_plural": "{{- appName}}: Sie haben {{- count}} ungelesene Erwähnungen/Direktnachrichten", "unreadMessage": "{{- appName}}: Sie haben ungelesene Nachrichten" }, "balloon": { @@ -343,5 +405,31 @@ "noWindowsFound": "Keine Fenster gefunden", "cancel": "Abbrechen", "share": "Teilen" + }, + "serverInfo": { + "title": "Serverinformationen", + "urlLabel": "URL:", + "versionLabel": "Version:", + "unknown": "Unbekannt", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Unterstützte Versionen", + "statusLabel": "Status:", + "status": { + "loading": "Lädt...", + "error": "Laden fehlgeschlagen", + "loaded": "Geladen", + "idle": "Inaktiv", + "from": "von {{source}}" + }, + "supported": { + "unknown": "Unbekannt", + "expiring": "Läuft ab", + "yes": "Ja", + "no": "Nein" + }, + "expiration": { + "label": "Ablauf:", + "expiresOn": "Läuft ab am {{date}}" + } } } diff --git a/src/i18n/en.i18n.json b/src/i18n/en.i18n.json index d637486d3e..ea1a677f3d 100644 --- a/src/i18n/en.i18n.json +++ b/src/i18n/en.i18n.json @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Check for Updates on Start", "noUpdatesAvailable": "No updates are available.", "copyright": "Copyright {{copyright}}", - "errorWhenLookingForUpdates": "An error has occurred when looking for updates" + "errorWhenLookingForUpdates": "An error has occurred when looking for updates", + "updateChannel": { + "label": "Update Channel", + "latest": "Stable", + "beta": "Beta", + "alpha": "Alpha (Experimental)" + } }, "addServer": { "title": "Add Server", @@ -125,6 +131,28 @@ "yes": "Allow", "no": "Cancel" }, + "mediaPermission": { + "title": "Media Permission Required", + "message": "{{- permissionType}} access is currently disabled in your system settings.", + "detail": "To enable video calling features, please allow access in your system's privacy settings and then restart the application.", + "openSettings": "Open Settings", + "cancel": "Cancel", + "microphone": "Microphone", + "camera": "Camera", + "both": "Microphone and Camera" + }, + "microphonePermissionDenied": { + "title": "Microphone Access Required", + "message": "Microphone access is required to {{- actionType}}. Please check your system settings.", + "detail": "You can enable microphone access in your system's privacy settings.", + "openSettings": "Open Settings", + "cancel": "Cancel", + "actionTypes": { + "initiateCall": "make calls", + "answerCall": "answer calls", + "recordMessage": "record messages" + } + }, "outlookCalendar": { "title": "Outlook Calendar", "encryptionUnavailableTitle": "Encryption unavailable", @@ -136,6 +164,13 @@ }, "supportedVersion": { "title": "Workspace version unsupported" + }, + "clearLogs": { + "title": "Clear Logs", + "message": "Are you sure you want to clear the log file?", + "detail": "This action cannot be undone. All current log entries will be permanently deleted.", + "yes": "Clear", + "cancel": "Cancel" } }, "downloads": { @@ -194,6 +229,10 @@ "title": "Settings", "general": "General", "certificates": "Certificates", + "developer": "Developer", + "sections": { + "logging": "Logging" + }, "options": { "report": { "title": "Report errors to developers", @@ -211,6 +250,11 @@ "title": "Hardware Acceleration", "description": "Enables the use of hardware acceleration when available. The application will reload on change." }, + "videoCallScreenCaptureFallback": { + "title": "Fallback Screen Capture for Video Calls", + "description": "Disable Windows Graphics Capture so screen sharing works in Remote Desktop sessions. The app restarts when you change this option.", + "forcedDescription": "Currently enforced because the app detected a Remote Desktop session. Toggle now controls future launches when running locally." + }, "internalVideoChatWindow": { "title": "Open Video Chat in Application Window", "description": "If enabled, the Video Chat will open in the application's window instead of the default browser. However, for Google Meet and Jitsi, screen recording is not supported in Electron applications, so they will always open in the browser regardless of this setting.", @@ -222,11 +266,13 @@ }, "menubar": { "title": "Menu bar", - "description": "Show menu bar on the top of the window." + "description": "Show menu bar on the top of the window.", + "disabledHint": "Cannot disable menu bar when sidebar is disabled. Settings would become inaccessible." }, "sidebar": { "title": "Sidebar", - "description": "Show sidebar on the left of the window with the Server List, Downloads and Settings." + "description": "Show sidebar on the left of the window with the Server List, Downloads and Settings.", + "disabledHint": "Cannot disable sidebar when menu bar is disabled. Settings would become inaccessible." }, "trayIcon": { "title": "Tray Icon", @@ -255,6 +301,33 @@ "videoCallWindowPersistence": { "title": "Remember video call window position", "description": "Save and restore the position and size of video call windows between sessions" + }, + "transparentWindow": { + "title": "Transparent window effect", + "description": "Enable native vibrancy/transparency effect for the window. Requires restart to apply." + }, + "themeAppearance": { + "title": "Theme", + "description": "Choose the color theme for the application.", + "auto": "Follow system", + "light": "Light", + "dark": "Dark" + }, + "outlookCalendarSyncInterval": { + "title": "Outlook Calendar Sync Interval", + "description": "How often to sync Outlook calendar events, in minutes (1-60)." + }, + "verboseOutlookLogging": { + "title": "Verbose Outlook Calendar Logging", + "description": "Enable detailed Exchange/NTLM debug logs for troubleshooting Outlook Calendar integration issues." + }, + "detailedEventsLogging": { + "title": "Detailed Events Logging", + "description": "Log full event data exchanged between Outlook and Rocket.Chat during calendar sync. Useful for diagnosing sync issues." + }, + "debugLogging": { + "title": "Verbose Logging", + "description": "Writes all console output to the log file. When disabled, only errors and important messages are saved, keeping logs smaller and focused." } } }, @@ -279,6 +352,7 @@ "close": "Close", "copy": "&Copy", "cut": "Cu&t", + "developerMode": "Developer Mode", "disableGpu": "Disable GPU", "documentation": "Documentation", "downloads": "Downloads", @@ -310,6 +384,10 @@ "showTrayIcon": "Tray icon", "toggleDevTools": "Toggle &DevTools", "openConfigFolder": "Open &Configuration Folder", + "openLogViewer": "Open &Log Viewer", + "videoCallDevTools": "Open Video Call &DevTools", + "videoCallTools": "Video Call Tools", + "videoCallDevToolsAutoOpen": "Auto-open DevTools", "undo": "&Undo", "unhide": "Show All", "viewMenu": "&View", @@ -322,6 +400,22 @@ "announcement": "Houston, we have a problem", "reload": "Reload" }, + "videoCall": { + "loading": { + "initial": "Loading video call...", + "reloading": "Reloading video call...", + "description": "Please wait while we connect to the video call" + }, + "error": { + "title": "Video Call Failed to Load", + "announcement": "Houston, we have a problem", + "timeout": "Loading timeout - video call failed to load within 15 seconds", + "crashed": "Webview crashed", + "maxRetriesReached": "Failed to load after multiple attempts", + "reload": "Reload Video Call", + "noUrl": "No video call URL provided" + } + }, "unsupportedServer": { "title": "{{instanceDomain}} is running an unsupported version of Rocket.Chat", "announcement": "An admin needs to update the workspace to a supported version in order to reenable access from mobile and desktop apps.", @@ -344,7 +438,9 @@ "clearCache": "Clear Cache", "clearStorageData": "Clear Storage Data", "copyCurrentUrl": "Copy current URL", - "reloadClearingCache": "Force reload" + "reloadClearingCache": "Force reload", + "serverInfo": "Server Info", + "supportedVersionsInfo": "Supported Versions Info" }, "tooltips": { "unreadMessage": "{{- count}} unread message", @@ -386,6 +482,7 @@ "permissionDenied": "Screen Recording Permission Denied", "permissionRequired": "Screen recording permission is required to share your screen.", "permissionInstructions": "Please enable it in your system preferences and try again.", + "openSystemPreferences": "Open System Preferences", "title": "Share your screen", "entireScreen": "Your entire screen", "applicationWindow": "Application window", @@ -393,5 +490,159 @@ "noWindowsFound": "No windows found", "cancel": "Cancel", "share": "Share" + }, + "logging": { + "context": { + "processTypes": { + "main": "Main Process", + "renderer": "Renderer Process", + "preload": "Preload Process", + "rendererRoot": "Main Window", + "webview": "Server Webview", + "videoCall": "Video Call Window" + }, + "components": { + "auth": "Authentication", + "connection": "Connection", + "notification": "Notification", + "outlook": "Outlook Calendar", + "videoCall": "Video Call", + "download": "Download", + "spellCheck": "Spell Check", + "general": "General" + }, + "serverInfo": { + "anonymous": "Anonymous Server", + "local": "Local Process", + "unknown": "Unknown Server" + } + }, + "status": { + "moduleNotAvailable": "Store module not available for server context mapping", + "importFailed": "Store import failed" + }, + "errors": { + "configurationFailed": "Failed to configure electron-log", + "storeUnavailable": "Store module not available for server context mapping", + "webContentsLoggingFailed": "Failed to setup webContents logging", + "rendererLogFailed": "Failed to log from renderer", + "consoleOverrideFailed": "Failed to override console methods" + }, + "messages": { + "serverContext": "Server {{- serverNumber}}", + "logContext": "Log Context: {{- context}}" + } + }, + "logViewer": { + "title": "Log Viewer", + "aria": { + "logIcon": "Log viewer icon", + "entriesCount": "{{count}} log entries displayed" + }, + "fileInfo": { + "custom": "Custom", + "entries": "{{count}} entries", + "entriesOfTotal": "{{count}} of {{total}} entries", + "noEntries": "No entries" + }, + "buttons": { + "openLogFile": "Open Log File", + "defaultLog": "Default Log", + "refresh": "Refresh", + "autoRefresh": "Auto Refresh", + "stopAutoRefresh": "Stop Auto Refresh", + "copy": "Copy", + "save": "Save", + "clear": "Clear", + "close": "Close", + "clearFilters": "Clear Filters" + }, + "controls": { + "showContext": "Show Context", + "showServer": "Show Server", + "autoScrollToTop": "Auto Scroll to Top" + }, + "placeholders": { + "loadAmount": "Load amount", + "searchLogs": "Search logs...", + "level": "Level", + "context": "Context", + "exchangeDebug": "Exchange Debug Filter" + }, + "filters": { + "entryLimit": { + "last100": "Last 100 entries", + "last500": "Last 500 entries", + "last1000": "Last 1000 entries", + "last5000": "Last 5000 entries", + "all": "All entries" + }, + "level": { + "all": "All Levels", + "debug": "Debug", + "info": "Info", + "warn": "Warning", + "error": "Error", + "verbose": "Verbose" + }, + "context": { + "all": "All Contexts", + "main": "Main Process", + "renderer": "Renderer", + "webview": "Webview", + "videocall": "Video Call", + "outlook": "Outlook Calendar", + "auth": "Authentication", + "updates": "Updates", + "notifications": "Notifications", + "servers": "Servers", + "ipc": "IPC Communication" + }, + "server": { + "all": "All Servers", + "label": "Server" + }, + "exchangeDebug": { + "all": "All Logs", + "success": "Success Only", + "failure": "Failures Only", + "ntlmFlow": "NTLM Authentication", + "exchangeComm": "Exchange Communication", + "sslCerts": "SSL/Certificate Issues", + "networkErrors": "Network Issues", + "successFactors": "Success Factors", + "outlookCalendar": "Outlook Calendar" + } + }, + "messages": { + "noLogsFound": "No logs found", + "adjustFilters": "Try adjusting your filters or refresh the logs" + } + }, + "serverInfo": { + "title": "Server Information", + "urlLabel": "URL:", + "versionLabel": "Version:", + "unknown": "Unknown", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Supported Versions", + "statusLabel": "Status:", + "status": { + "loading": "Loading...", + "error": "Failed to load", + "loaded": "Loaded", + "idle": "Idle", + "from": "from {{source}}" + }, + "supported": { + "unknown": "Unknown", + "expiring": "Expiring", + "yes": "Yes", + "no": "No" + }, + "expiration": { + "label": "Expiration:", + "expiresOn": "Expires on {{date}}" + } } -} \ No newline at end of file +} diff --git a/src/i18n/es.i18n.json b/src/i18n/es.i18n.json index 4f868e03f6..bc54b04628 100644 --- a/src/i18n/es.i18n.json +++ b/src/i18n/es.i18n.json @@ -1,6 +1,6 @@ { "contextMenu": { - "cut": "Co&rtar", + "cut": "Cor&tar", "copy": "&Copiar", "paste": "&Pegar", "selectAll": "Seleccionar &todo", @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Buscar actualizaciones al iniciar", "noUpdatesAvailable": "No hay actualizaciones disponibles.", "copyright": "Derechos de autor {{copyright}}", - "errorWhenLookingForUpdates": "Ha ocurrido un error al buscar actualizaciones" + "errorWhenLookingForUpdates": "Ha ocurrido un error al buscar actualizaciones", + "updateChannel": { + "label": "Canal de actualización", + "latest": "Estable", + "beta": "Beta", + "alpha": "Alfa (Experimental)" + } }, "addServer": { "title": "Agregar servidor", @@ -116,6 +122,28 @@ "yes": "Permitir", "no": "Cancelar" }, + "mediaPermission": { + "title": "Permiso de Medios Requerido", + "message": "El acceso al {{- permissionType}} está deshabilitado en la configuración de tu sistema.", + "detail": "Para habilitar las funciones de videollamada, por favor permite el acceso en la configuración de privacidad de tu sistema y luego reinicia la aplicación.", + "openSettings": "Abrir Configuración", + "cancel": "Cancelar", + "microphone": "Micrófono", + "camera": "Cámara", + "both": "Micrófono y Cámara" + }, + "microphonePermissionDenied": { + "title": "Acceso al Micrófono Requerido", + "message": "Se requiere acceso al micrófono para {{- actionType}}. Por favor revisa la configuración de tu sistema.", + "detail": "Puedes habilitar el acceso al micrófono en la configuración de privacidad de tu sistema.", + "openSettings": "Abrir Configuración", + "cancel": "Cancelar", + "actionTypes": { + "initiateCall": "realizar llamadas", + "answerCall": "responder llamadas", + "recordMessage": "grabar mensajes" + } + }, "outlookCalendar": { "title": "Calendario de Outlook", "encryptionUnavailableTitle": "Encriptación no disponible", @@ -202,6 +230,11 @@ "title": "Aceleración de hardware", "description": "Habilita el uso de la aceleración de hardware cuando esté disponible. La aplicación se reiniciará al realizar cambios." }, + "videoCallScreenCaptureFallback": { + "title": "Modo alternativo de captura en videollamadas", + "description": "Desactiva Windows Graphics Capture para que el uso compartido funcione en sesiones RDP. La app se reinicia cuando cambias esta opción.", + "forcedDescription": "Actualmente está aplicado porque la app detectó una sesión RDP. El interruptor controla el comportamiento en siguientes arranques locales." + }, "internalVideoChatWindow": { "title": "Abrir videollamada en la ventana de la aplicación", "description": "Si está activado, la videollamada se abrirá en la ventana de la aplicación en lugar del navegador predeterminado. Sin embargo, para Google Meet y Jitsi, la grabación de pantalla no es compatible en aplicaciones Electron, por lo que siempre se abrirán en el navegador independientemente de esta configuración.", @@ -213,11 +246,13 @@ }, "menubar": { "title": "Barra de menú", - "description": "Mostrar la barra de menú en la parte superior de la ventana." + "description": "Mostrar la barra de menú en la parte superior de la ventana.", + "disabledHint": "No se puede desactivar la barra de menú cuando la barra lateral está desactivada. La configuración sería inaccesible." }, "sidebar": { "title": "Barra lateral", - "description": "Mostrar la barra lateral en el lado izquierdo de la ventana con la lista de servidores, las descargas y la configuración." + "description": "Mostrar la barra lateral en el lado izquierdo de la ventana con la lista de servidores, las descargas y la configuración.", + "disabledHint": "No se puede desactivar la barra lateral cuando la barra de menú está desactivada. La configuración sería inaccesible." }, "trayIcon": { "title": "Icono de la bandeja", @@ -246,6 +281,17 @@ "videoCallWindowPersistence": { "title": "Recordar posición de la ventana de videollamada", "description": "Guardar y restaurar la posición y el tamaño de las ventanas de videollamada entre sesiones" + }, + "transparentWindow": { + "title": "Efecto de ventana transparente", + "description": "Habilitar efecto nativo de vibración/transparencia para la ventana. Requiere reinicio para aplicar." + }, + "themeAppearance": { + "title": "Tema", + "description": "Elige el tema de color para la aplicación.", + "auto": "Seguir sistema", + "light": "Claro", + "dark": "Oscuro" } } }, @@ -269,8 +315,9 @@ "clearTrustedCertificates": "Borrar certificados de confianza", "close": "Cerrar", "copy": "&Copiar", - "cut": "Cu&tar", - "disableGpu": "Desactivar GPU", + "cut": "Cor&tar", + "developerMode": "Modo desarrollador", + "disableGpu": "Deshabilitar GPU", "documentation": "Documentación", "downloads": "Descargas", "settings": "Configuración", @@ -311,6 +358,21 @@ "announcement": "Houston, tenemos un problema", "reload": "Recargar" }, + "videoCall": { + "loading": { + "initial": "Cargando videollamada...", + "reloading": "Recargando videollamada...", + "description": "Por favor espera mientras nos conectamos a la videollamada" + }, + "error": { + "title": "Error al cargar la videollamada", + "announcement": "Houston, tenemos un problema", + "timeout": "Tiempo de espera agotado - la videollamada no pudo cargar en 15 segundos", + "crashed": "Webview se ha bloqueado", + "maxRetriesReached": "Error al cargar después de múltiples intentos", + "reload": "Recargar videollamada" + } + }, "unsupportedServer": { "title": "{{instanceDomain}} está ejecutando una versión no compatible de Rocket.Chat", "announcement": "Un administrador debe actualizar el espacio de trabajo a una versión compatible para habilitar el acceso desde aplicaciones móviles y de escritorio.", @@ -373,5 +435,31 @@ "noWindowsFound": "No se encontraron ventanas", "cancel": "Cancelar", "share": "Compartir" + }, + "serverInfo": { + "title": "Información del Servidor", + "urlLabel": "URL:", + "versionLabel": "Versión:", + "unknown": "Desconocido", + "exchangeUrlLabel": "URL de Outlook Exchange:", + "supportedVersionsTitle": "Versiones Soportadas", + "statusLabel": "Estado:", + "status": { + "loading": "Cargando...", + "error": "Error al cargar", + "loaded": "Cargado", + "idle": "Inactivo", + "from": "desde {{source}}" + }, + "supported": { + "unknown": "Desconocido", + "expiring": "Por expirar", + "yes": "Sí", + "no": "No" + }, + "expiration": { + "label": "Expiración:", + "expiresOn": "Expira el {{date}}" + } } -} \ No newline at end of file +} diff --git a/src/i18n/fi.i18n.json b/src/i18n/fi.i18n.json index beb2a148ff..ca8db92622 100644 --- a/src/i18n/fi.i18n.json +++ b/src/i18n/fi.i18n.json @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Tarkista päivitykset käynnistettäessä", "noUpdatesAvailable": "Päivityksiä ei ole saatavilla.", "copyright": "Copyright {{copyright}}", - "errorWhenLookingForUpdates": "Virhe etsittäessä päivityksiä" + "errorWhenLookingForUpdates": "Virhe etsittäessä päivityksiä", + "updateChannel": { + "label": "Päivityskanava", + "latest": "Vakaa", + "beta": "Beta", + "alpha": "Alpha (Kokeellinen)" + } }, "addServer": { "title": "Lisää palvelin", @@ -115,6 +121,28 @@ "dontAskAgain": "Salli aina tämän palvelimen videopuhelujen ottaa näyttökuva", "yes": "Salli", "no": "Peruuta" + }, + "mediaPermission": { + "title": "Medialupa Vaaditaan", + "message": "{{- permissionType}} käyttöoikeus on tällä hetkellä poistettu käytöstä järjestelmäasetuksissa.", + "detail": "Ottaaksesi videopuheluominaisuudet käyttöön, salli pääsy järjestelmän yksityisyysasetuksissa ja käynnistä sitten sovellus uudelleen.", + "openSettings": "Avaa Asetukset", + "cancel": "Peruuta", + "microphone": "Mikrofoni", + "camera": "Kamera", + "both": "Mikrofoni ja Kamera" + }, + "microphonePermissionDenied": { + "title": "Mikrofonin käyttö vaaditaan", + "message": "Mikrofonin käyttö vaaditaan {{- actionType}}. Tarkista järjestelmäsi asetukset.", + "detail": "Voit ottaa mikrofonin käyttöön järjestelmäsi tietosuoja-asetuksista.", + "openSettings": "Avaa asetukset", + "cancel": "Peruuta", + "actionTypes": { + "initiateCall": "soittaa", + "answerCall": "vastata puheluun", + "recordMessage": "tallentaa viestin" + } } }, "downloads": { @@ -190,6 +218,11 @@ "title": "Laitteistokiihdytys", "description": "Ottaa käyttöön laitteistokiihdytyksen, jos se on käytettävissä. Sovellus latautuu uudelleen muutoksen yhteydessä." }, + "videoCallScreenCaptureFallback": { + "title": "Videopuhelujen varanäyttökaappaus", + "description": "Poistaa Windows Graphics Capture -tilan, jotta jakaminen toimii RDP-istunnoissa. Sovellus käynnistyy uudelleen, kun tämä vaihtoehto muuttuu.", + "forcedDescription": "Tällä hetkellä pakotettu, koska sovellus havaitsi RDP-istunnon. Vaihtoehto määrittää seuraavien paikallisten käynnistysten käytöksen." + }, "internalVideoChatWindow": { "title": "Avaa videokeskustelu sovellusikkunaan", "description": "Jos käytössä, videokeskustelu avautuu sovelluksen ikkunaan oletusselaimen sijaan. Kuitenkin Google Meet ja Jitsi -palveluissa ruuduntallennus ei ole tuettu Electron-sovelluksissa, joten ne avautuvat aina selaimessa tästä asetuksesta riippumatta.", @@ -201,11 +234,13 @@ }, "menubar": { "title": "Valikkopalkki", - "description": "Näytä valikkopalkki ikkunan yläreunassa." + "description": "Näytä valikkopalkki ikkunan yläreunassa.", + "disabledHint": "Valikkopalkkinsa poisto ei ole mahdollista, kun sivupalkki on pois käytöstä. Asetukset tulisivat saavuttamattomiksi." }, "sidebar": { "title": "Sivupalkki", - "description": "Näytä ikkunan vasemmassa reunassa sivupalkki, jossa näkyvät palvelinluettelo, lataukset ja asetukset." + "description": "Näytä ikkunan vasemmassa reunassa sivupalkki, jossa näkyvät palvelinluettelo, lataukset ja asetukset.", + "disabledHint": "Sivupalkin poisto ei ole mahdollista, kun valikkopalkki on pois käytöstä. Asetukset tulisivat saavuttamattomiksi." }, "trayIcon": { "title": "Ilmaisinalueen kuvake", @@ -221,6 +256,17 @@ "clearPermittedScreenCaptureServers": { "title": "Tyhjennä näyttökuvaoikeudet", "description": "Tyhjennä valitut näyttökuvaoikeudet, joilla estettiin kysyminen uudelleen videopuheluissa." + }, + "transparentWindow": { + "title": "Läpinäkyvä ikkuna-efekti", + "description": "Ota käyttöön natiivi värinäkyvyys/läpinäkyvyys-efekti ikkunalle. Vaatii uudelleenkäynnistyksen toimiakseen." + }, + "themeAppearance": { + "title": "Teema", + "description": "Valitse sovelluksen väriteema.", + "auto": "Seuraa järjestelmää", + "light": "Vaalea", + "dark": "Tumma" } } }, @@ -286,6 +332,21 @@ "announcement": "Houston, meillä on ongelma", "reload": "Lataa uudelleen" }, + "videoCall": { + "loading": { + "initial": "Ladataan videopuhelua...", + "reloading": "Ladataan videopuhelua uudelleen...", + "description": "Odota hetki, kun yhdistämme videopuheluun" + }, + "error": { + "title": "Videopuhelun lataaminen epäonnistui", + "announcement": "Houston, meillä on ongelma", + "timeout": "Aikakatkaisu - videopuhelua ei voitu ladata 15 sekunnissa", + "crashed": "Webview kaatui", + "maxRetriesReached": "Lataaminen epäonnistui useiden yritysten jälkeen", + "reload": "Lataa videopuhelu uudelleen" + } + }, "selfxss": { "title": "Lopeta!", "description": "Tämä on kehittäjille tarkoitettu selaimen ominaisuus. Jos joku kehottaa sinua kopioimaan ja liittämään tähän jotain, jotta saat käyttöön Rocket.Chat-ominaisuuden tai voit \"hakkeroida\" jonkun tilin, kyse on huijauksesta, jolla kyseinen taho pääsee Rocket.Chat-tilillesi.", @@ -342,5 +403,31 @@ "noWindowsFound": "Ikkunoita ei löytynyt", "cancel": "Peruuta", "share": "Jaa" + }, + "serverInfo": { + "title": "Palvelimen tiedot", + "urlLabel": "URL:", + "versionLabel": "Versio:", + "unknown": "Tuntematon", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Tuetut versiot", + "statusLabel": "Tila:", + "status": { + "loading": "Ladataan...", + "error": "Lataus epäonnistui", + "loaded": "Ladattu", + "idle": "Käyttämättä", + "from": "lähteestä {{source}}" + }, + "supported": { + "unknown": "Tuntematon", + "expiring": "Vanhenee", + "yes": "Kyllä", + "no": "Ei" + }, + "expiration": { + "label": "Vanheneminen:", + "expiresOn": "Vanhenee {{date}}" + } } -} \ No newline at end of file +} diff --git a/src/i18n/fr.i18n.json b/src/i18n/fr.i18n.json index c61e0ccccc..d88a382038 100644 --- a/src/i18n/fr.i18n.json +++ b/src/i18n/fr.i18n.json @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Vérifier les mises à jour au démarrage", "noUpdatesAvailable": "Aucune mise à jour n'est disponible.", "copyright": "Copyright {{copyright}}", - "errorWhenLookingForUpdates": "Une erreur s'est produite lors de la recherche de mises à jour" + "errorWhenLookingForUpdates": "Une erreur s'est produite lors de la recherche de mises à jour", + "updateChannel": { + "label": "Canal de mise à jour", + "latest": "Stable", + "beta": "Bêta", + "alpha": "Alpha (Expérimental)" + } }, "addServer": { "title": "Ajouter un serveur", @@ -115,6 +121,28 @@ "dontAskAgain": "Toujours autoriser les appels vidéo de ce serveur pour capturer votre écran", "yes": "Autoriser", "no": "Annuler" + }, + "mediaPermission": { + "title": "Autorisation Multimédia Requise", + "message": "L'accès au {{- permissionType}} est actuellement désactivé dans les paramètres de votre système.", + "detail": "Pour activer les fonctionnalités d'appel vidéo, veuillez autoriser l'accès dans les paramètres de confidentialité de votre système, puis redémarrer l'application.", + "openSettings": "Ouvrir les Paramètres", + "cancel": "Annuler", + "microphone": "Microphone", + "camera": "Caméra", + "both": "Microphone et Caméra" + }, + "microphonePermissionDenied": { + "title": "Accès au Microphone Requis", + "message": "L'accès au microphone est requis pour {{- actionType}}. Veuillez vérifier les paramètres de votre système.", + "detail": "Vous pouvez activer l'accès au microphone dans les paramètres de confidentialité de votre système.", + "openSettings": "Ouvrir les Paramètres", + "cancel": "Annuler", + "actionTypes": { + "initiateCall": "passer des appels", + "answerCall": "répondre aux appels", + "recordMessage": "enregistrer des messages" + } } }, "downloads": { @@ -190,6 +218,11 @@ "title": "Accélération matérielle", "description": "Active l'utilisation de l'accélération matérielle lorsqu'elle est disponible. L'application se rechargera en cas de changement." }, + "videoCallScreenCaptureFallback": { + "title": "Solution de capture d'écran pour les appels vidéo", + "description": "Désactive Windows Graphics Capture pour permettre le partage d'écran dans les sessions RDP. L'application redémarre lorsque vous modifiez cette option.", + "forcedDescription": "Actuellement appliqué car l'application a détecté une session RDP. Le commutateur contrôle le comportement lors des prochains démarrages locaux." + }, "internalVideoChatWindow": { "title": "Ouvrir le chat vidéo à l'aide de la fenêtre de l'application", "description": "Si cette option est activée, le chat vidéo s'ouvrira dans la fenêtre de l'application au lieu du navigateur par défaut. Cependant, pour Google Meet et Jitsi, l'enregistrement d'écran n'est pas pris en charge dans les applications Electron, donc ils s'ouvriront toujours dans le navigateur, quel que soit ce paramètre.", @@ -201,11 +234,13 @@ }, "menubar": { "title": "Barre de menu", - "description": "Afficher la barre de menus en haut de la fenêtre." + "description": "Afficher la barre de menus en haut de la fenêtre.", + "disabledHint": "Impossible de désactiver la barre de menu quand la barre latérale est désactivée. Les paramètres deviendraient inaccessibles." }, "sidebar": { "title": "Barre latérale", - "description": "Afficher la barre latérale à gauche de la fenêtre avec la liste des serveurs, les téléchargements et les paramètres." + "description": "Afficher la barre latérale à gauche de la fenêtre avec la liste des serveurs, les téléchargements et les paramètres.", + "disabledHint": "Impossible de désactiver la barre latérale quand la barre de menu est désactivée. Les paramètres deviendraient inaccessibles." }, "trayIcon": { "title": "Icône de la barre d'état système", @@ -221,6 +256,17 @@ "clearPermittedScreenCaptureServers": { "title": "Effacer les autorisations de capture d'écran", "description": "Effacez les autorisations de capture d'écran qui ont été sélectionnées pour ne plus demander lors des appels vidéo." + }, + "transparentWindow": { + "title": "Effet de fenêtre transparente", + "description": "Activer l'effet natif de vibrance/transparence pour la fenêtre. Nécessite un redémarrage pour s'appliquer." + }, + "themeAppearance": { + "title": "Thème", + "description": "Choisissez le thème de couleur pour l'application.", + "auto": "Suivre le système", + "light": "Clair", + "dark": "Sombre" } } }, @@ -245,6 +291,7 @@ "close": "Fermer", "copy": "&Copier", "cut": "Cou&per", + "developerMode": "Mode développeur", "disableGpu": "Désactiver le GPU", "documentation": "Documentation", "downloads": "Téléchargements", @@ -286,6 +333,21 @@ "announcement": "Houston, nous avons un problème", "reload": "Recharger" }, + "videoCall": { + "loading": { + "initial": "Chargement de l'appel vidéo...", + "reloading": "Rechargement de l'appel vidéo...", + "description": "Veuillez patienter pendant que nous nous connectons à l'appel vidéo" + }, + "error": { + "title": "Échec du chargement de l'appel vidéo", + "announcement": "Houston, nous avons un problème", + "timeout": "Délai d'attente dépassé - l'appel vidéo n'a pas pu se charger en 15 secondes", + "crashed": "Webview a planté", + "maxRetriesReached": "Échec du chargement après plusieurs tentatives", + "reload": "Recharger l'appel vidéo" + } + }, "selfxss": { "title": "Stop !", "description": "Il s'agit d'une fonctionnalité de navigateur destinée aux développeurs. Si quelqu'un vous a dit de copier-coller quelque chose ici pour activer une fonctionnalité Rocket.Chat ou \"pirater\" le compte de quelqu'un, il s'agit d'une arnaque et ça lui donnera accès à votre compte Rocket.Chat.", @@ -342,5 +404,31 @@ "noWindowsFound": "Aucune fenêtre trouvée", "cancel": "Annuler", "share": "Partager" + }, + "serverInfo": { + "title": "Informations du Serveur", + "urlLabel": "URL :", + "versionLabel": "Version :", + "unknown": "Inconnu", + "exchangeUrlLabel": "URL Outlook Exchange :", + "supportedVersionsTitle": "Versions Prise en Charge", + "statusLabel": "Statut :", + "status": { + "loading": "Chargement...", + "error": "Échec du chargement", + "loaded": "Chargé", + "idle": "Inactif", + "from": "depuis {{source}}" + }, + "supported": { + "unknown": "Inconnu", + "expiring": "Expiration proche", + "yes": "Oui", + "no": "Non" + }, + "expiration": { + "label": "Expiration :", + "expiresOn": "Expire le {{date}}" + } } } diff --git a/src/i18n/hu.i18n.json b/src/i18n/hu.i18n.json index b8dec232ff..91c5105d04 100644 --- a/src/i18n/hu.i18n.json +++ b/src/i18n/hu.i18n.json @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Frissítések keresése indításkor", "noUpdatesAvailable": "Nincsenek elérhető frissítések.", "copyright": "Copyright {{copyright}}", - "errorWhenLookingForUpdates": "Hiba történt a frissítések keresésekor" + "errorWhenLookingForUpdates": "Hiba történt a frissítések keresésekor", + "updateChannel": { + "label": "Frissítési csatorna", + "latest": "Stabil", + "beta": "Béta", + "alpha": "Alfa (kísérleti)" + } }, "addServer": { "title": "Kiszolgáló hozzáadása", @@ -125,6 +131,28 @@ "yes": "Engedélyezés", "no": "Mégse" }, + "mediaPermission": { + "title": "Médiaengedély szükséges", + "message": "A(z) {{- permissionType}} hozzáférés jelenleg le van tiltva a rendszerbeállításokban.", + "detail": "A videohívási funkciók engedélyezéséhez engedélyezze a hozzáférést a rendszer adatvédelmi beállításaiban, majd indítsa újra az alkalmazást.", + "openSettings": "Beállítások megnyitása", + "cancel": "Mégse", + "microphone": "Mikrofon", + "camera": "Kamera", + "both": "Mikrofon és kamera" + }, + "microphonePermissionDenied": { + "title": "Mikrofon hozzáférés szükséges", + "message": "Mikrofon hozzáférés szükséges ehhez: {{- actionType}}. Ellenőrizze a rendszerbeállításokat.", + "detail": "A mikrofon hozzáférést a rendszer adatvédelmi beállításaiban engedélyezheti.", + "openSettings": "Beállítások megnyitása", + "cancel": "Mégse", + "actionTypes": { + "initiateCall": "hívások indítása", + "answerCall": "hívások megválaszolása", + "recordMessage": "üzenetek rögzítése" + } + }, "outlookCalendar": { "title": "Outlook naptár", "encryptionUnavailableTitle": "A titkosítás nem érhető el", @@ -194,6 +222,10 @@ "title": "Beállítások", "general": "Általános", "certificates": "Tanúsítványok", + "developer": "Fejlesztő", + "sections": { + "logging": "Naplózás" + }, "options": { "report": { "title": "Hibák jelentése a fejlesztőknek", @@ -211,6 +243,11 @@ "title": "Hardveres gyorsítás", "description": "Engedélyezi a hardveres gyorsítás használatát, ha elérhető. Az alkalmazás újratöltődik a megváltoztatásakor." }, + "videoCallScreenCaptureFallback": { + "title": "Tartalék képernyőrögzítés a videohívásokhoz", + "description": "A Windows Graphics Capture funkció letiltása, hogy a képernyőmegosztás működjön a távoli asztal munkameneteiben. Az alkalmazás újraindul, ha megváltoztatja ezt a beállítást.", + "forcedDescription": "Jelenleg érvényben van, mert az alkalmazás távoli asztali munkamenetet észlelt. A kapcsoló mostantól a jövőbeli indításokat vezérli, amikor helyileg fut." + }, "internalVideoChatWindow": { "title": "Videocsevegés megnyitása az alkalmazásablakban", "description": "Ha engedélyezve van, akkor a videócsevegés az alkalmazás ablakában nyílik meg az alapértelmezett böngésző helyett. Azonban a Google Meet és a Jitsi esetében a képernyő rögzítése nem támogatott az Electron alkalmazásokban, így azok ettől a beállítástól függetlenül mindig a böngészőben nyílnak meg.", @@ -222,11 +259,13 @@ }, "menubar": { "title": "Menüsáv", - "description": "Menüsáv megjelenítése az ablak tetején." + "description": "Menüsáv megjelenítése az ablak tetején.", + "disabledHint": "Nem lehet letiltani a menüsort, ha az oldalsáv le van tiltva. A beállítások elérhetetlenné válnának." }, "sidebar": { "title": "Oldalsáv", - "description": "Oldalsáv megjelenítése az ablak bal oldalán a kiszolgálók listájával, a letöltésekkel és a beállításokkal." + "description": "Oldalsáv megjelenítése az ablak bal oldalán a kiszolgálók listájával, a letöltésekkel és a beállításokkal.", + "disabledHint": "Nem lehet letiltani az oldalsávot, ha a menüsor le van tiltva. A beállítások elérhetetlenné válnának." }, "trayIcon": { "title": "Tálcaikon", @@ -254,7 +293,22 @@ }, "videoCallWindowPersistence": { "title": "A videohívás-ablak pozíciójának megjegyzése", - "description": "A videohívás-ablakok pozíciójának és méretének mentése és helyreállítása a munkamenetek között" + "description": "A videohívás-ablakok pozíciójának és méretének mentése és helyreállítása a munkamenetek között." + }, + "transparentWindow": { + "title": "Átlátszó ablak hatás", + "description": "Natív vibrálás vagy átlátszóság hatás engedélyezése az ablakhoz. Újraindítást igényel az alkalmazáshoz." + }, + "themeAppearance": { + "title": "Téma", + "description": "Az alkalmazás színtémájának kiválasztása.", + "auto": "Rendszer követése", + "light": "Világos", + "dark": "Sötét" + }, + "verboseOutlookLogging": { + "title": "Részletes Outlook naptár naplózás", + "description": "Részletes Exchange/NTLM hibakeresési naplók engedélyezése az Outlook naptár integrációs problémáinak elhárításához." } } }, @@ -279,6 +333,7 @@ "close": "Bezárás", "copy": "&Másolás", "cut": "&Kivágás", + "developerMode": "Fejlesztői mód", "disableGpu": "GPU letiltása", "documentation": "Dokumentáció", "downloads": "Letöltések", @@ -310,6 +365,10 @@ "showTrayIcon": "Tálcaikon", "toggleDevTools": "&Fejlesztői eszközök ki- és bekapcsolása", "openConfigFolder": "&Beállítások mappa megnyitása", + "openLogViewer": "&Naplómegjelenítő megnyitása", + "videoCallDevTools": "Videohívás &fejlesztői eszközeinek megnyitása", + "videoCallTools": "Videohívási eszközök", + "videoCallDevToolsAutoOpen": "Fejlesztői eszközök automatikus megnyitása", "undo": "&Visszavonás", "unhide": "Összes megjelenítése", "viewMenu": "&Nézet", @@ -322,6 +381,21 @@ "announcement": "Houston, baj van!", "reload": "Újratöltés" }, + "videoCall": { + "loading": { + "initial": "Videohívás betöltése…", + "reloading": "Videohívás újratöltése…", + "description": "Várjon, amíg kapcsolódunk a videohíváshoz" + }, + "error": { + "title": "A videohívást nem sikerült betölteni", + "announcement": "Houston, baj van!", + "timeout": "Betöltési időtúllépés – a videohívást nem sikerült betölteni 15 másodpercen belül", + "crashed": "A webnézet összeomlott", + "maxRetriesReached": "Nem sikerült betölteni többszöri próbálkozás után sem", + "reload": "Videohívás újratöltése" + } + }, "unsupportedServer": { "title": "A(z) {{instanceDomain}} a Rocket.Chat nem támogatott verzióját futtatja", "announcement": "Egy rendszergazdának frissítenie kell a munkaterületet egy támogatott verzióra ahhoz, hogy újra engedélyezze a hozzáférést a mobil és asztali alkalmazásokból.", @@ -344,7 +418,9 @@ "clearCache": "Gyorsítótár törlése", "clearStorageData": "Tárolóadatok törlése", "copyCurrentUrl": "Jelenlegi URL másolása", - "reloadClearingCache": "Kényszerített újratöltés" + "reloadClearingCache": "Kényszerített újratöltés", + "serverInfo": "Kiszolgálóinformációk", + "supportedVersionsInfo": "Támogatott verziók információi" }, "tooltips": { "unreadMessage": "{{- count}} olvasatlan üzenet", @@ -393,5 +469,154 @@ "noWindowsFound": "Nem találhatók ablakok", "cancel": "Mégse", "share": "Megosztás" + }, + "logging": { + "context": { + "processTypes": { + "main": "Főfolyamat", + "renderer": "Megjelenítő folyamat", + "preload": "Előtöltő folyamat", + "rendererRoot": "Főablak", + "webview": "Kiszolgáló webnézete", + "videoCall": "Videohívás ablaka" + }, + "components": { + "auth": "Hitelesítés", + "connection": "Kapcsolat", + "notification": "Értesítés", + "outlook": "Outlook naptár", + "videoCall": "Videohívás", + "download": "Letöltés", + "spellCheck": "Helyesírás-ellenőrzés", + "general": "Általános" + }, + "serverInfo": { + "anonymous": "Névtelen kiszolgáló", + "local": "Helyi folyamat", + "unknown": "Ismeretlen kiszolgáló" + } + }, + "status": { + "moduleNotAvailable": "A tároló modul nem érhető el a kiszolgáló környezetének leképezésénél", + "importFailed": "A tároló importálása nem sikerült" + }, + "errors": { + "configurationFailed": "Nem sikerült beállítani az elektronnaplót", + "storeUnavailable": "A tároló modul nem érhető el a kiszolgáló környezetének leképezésénél", + "webContentsLoggingFailed": "Nem sikerült beállítani a webtartalmak naplózását", + "rendererLogFailed": "Nem sikerült naplózni a megjelenítőből", + "consoleOverrideFailed": "Nem sikerült felülbírálni a konzol metódusait" + }, + "messages": { + "serverContext": "{{- serverNumber}}. kiszolgáló", + "logContext": "Naplózási környezet: {{- context}}" + } + }, + "logViewer": { + "title": "Naplómegjelenítő", + "aria": { + "logIcon": "Naplómegjelenítő ikon", + "entriesCount": "{{count}} naplóbejegyzés megjelenítve" + }, + "fileInfo": { + "custom": "Egyéni", + "entries": "{{count}} bejegyzés", + "entriesOfTotal": "{{count}} / {{total}} bejegyzés", + "noEntries": "Nincsenek bejegyzések" + }, + "buttons": { + "openLogFile": "Naplófájl megnyitása", + "defaultLog": "Alapértelmezett napló", + "refresh": "Frissítés", + "autoRefresh": "Automatikus frissítés", + "stopAutoRefresh": "Automatikus frissítés leállítása", + "copy": "Másolás", + "save": "Mentés", + "clear": "Törlés", + "close": "Bezárás", + "clearFilters": "Szűrők törlése" + }, + "controls": { + "showContext": "Környezet megjelenítése", + "autoScrollToTop": "Automatikus görgetés a tetejére" + }, + "placeholders": { + "loadAmount": "Betöltés mennyisége", + "searchLogs": "Naplók keresése…", + "level": "Szint", + "context": "Környezet", + "exchangeDebug": "Exchange hibakeresési szűrő" + }, + "filters": { + "entryLimit": { + "last100": "Utolsó 100 bejegyzés", + "last500": "Utolsó 500 bejegyzés", + "last1000": "Utolsó 1000 bejegyzés", + "last5000": "Utolsó 5000 bejegyzés", + "all": "Összes bejegyzés" + }, + "level": { + "all": "Összes szint", + "debug": "Hibakeresés", + "info": "Információ", + "warn": "Figyelmeztetés", + "error": "Hiba", + "verbose": "Részletes" + }, + "context": { + "all": "Összes környezet", + "main": "Főfolyamat", + "renderer": "Megjelenítő", + "webview": "Webnézet", + "videocall": "Videohívás", + "outlook": "Outlook naptár", + "auth": "Hitelesítés", + "updates": "Frissítések", + "notifications": "Értesítések", + "servers": "Kiszolgálók", + "ipc": "IPC-kommunikáció" + }, + "exchangeDebug": { + "all": "Összes napló", + "success": "Csak sikeresek", + "failure": "Csak sikertelenek", + "ntlmFlow": "NTLM hitelesítés", + "exchangeComm": "Exchange kommunikáció", + "sslCerts": "SSL- vagy tanúsítványproblémák", + "networkErrors": "Hálózati problémák", + "successFactors": "Sikertényezők", + "outlookCalendar": "Outlook naptár" + } + }, + "messages": { + "noLogsFound": "Nem találhatók naplók", + "adjustFilters": "Próbálja meg beállítani a szűrőket, vagy frissítse a naplókat" + } + }, + "serverInfo": { + "title": "Kiszolgálóinformációk", + "urlLabel": "URL:", + "versionLabel": "Verzió:", + "unknown": "Ismeretlen", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Támogatott verziók", + "statusLabel": "Állapot:", + "status": { + "loading": "Betöltés…", + "error": "Nem sikerült betölteni", + "loaded": "Betöltve", + "idle": "Üresjárat", + "from": "innen: {{source}}" + }, + "supported": { + "unknown": "Ismeretlen", + "expiring": "Lejáró", + "yes": "Igen", + "no": "Nem" + }, + "expiration": { + "label": "Lejárat:", + "expiresOn": "Lejár ekkor: {{date}}" + } } } diff --git a/src/i18n/it-IT.i18n.json b/src/i18n/it-IT.i18n.json index 6f31cf5a2e..16b27dc5cd 100644 --- a/src/i18n/it-IT.i18n.json +++ b/src/i18n/it-IT.i18n.json @@ -1 +1,43 @@ -{ } \ No newline at end of file +{ + "settings": { + "options": { + "transparentWindow": { + "title": "Effetto finestra trasparente", + "description": "Abilita l'effetto nativo di vibrazione/trasparenza per la finestra. Richiede il riavvio per applicare." + }, + "themeAppearance": { + "title": "Tema", + "description": "Scegli il tema colore per l'applicazione.", + "auto": "Segui sistema", + "light": "Chiaro", + "dark": "Scuro" + } + } + }, + "serverInfo": { + "title": "Informazioni sul Server", + "urlLabel": "URL:", + "versionLabel": "Versione:", + "unknown": "Sconosciuto", + "exchangeUrlLabel": "URL Outlook Exchange:", + "supportedVersionsTitle": "Versioni Supportate", + "statusLabel": "Stato:", + "status": { + "loading": "Caricamento...", + "error": "Caricamento fallito", + "loaded": "Caricato", + "idle": "Inattivo", + "from": "da {{source}}" + }, + "supported": { + "unknown": "Sconosciuto", + "expiring": "In scadenza", + "yes": "Sì", + "no": "No" + }, + "expiration": { + "label": "Scadenza:", + "expiresOn": "Scade il {{date}}" + } + } +} diff --git a/src/i18n/ja.i18n.json b/src/i18n/ja.i18n.json index f9e1468aa0..03866875a6 100644 --- a/src/i18n/ja.i18n.json +++ b/src/i18n/ja.i18n.json @@ -79,6 +79,43 @@ "title": "更新をスキップ", "message": "次回の更新プログラムが利用可能になったときにお知らせします。\n気になる場合は[バージョン情報]メニューから更新プログラムを確認できます。", "ok": "OK" + }, + "mediaPermission": { + "title": "メディア許可が必要", + "message": "{{- permissionType}}へのアクセスは現在システム設定で無効になっています。", + "detail": "ビデオ通話機能を有効にするには、システムのプライバシー設定でアクセスを許可してからアプリケーションを再起動してください。", + "openSettings": "設定を開く", + "cancel": "キャンセル", + "microphone": "マイク", + "camera": "カメラ", + "both": "マイクとカメラ" + }, + "microphonePermissionDenied": { + "title": "マイクアクセスが必要", + "message": "{{- actionType}}にはマイクアクセスが必要です。システム設定を確認してください。", + "detail": "システムのプライバシー設定でマイクアクセスを有効にできます。", + "openSettings": "設定を開く", + "cancel": "キャンセル", + "actionTypes": { + "initiateCall": "通話を行う", + "answerCall": "通話に応答する", + "recordMessage": "メッセージを録音する" + } + } + }, + "settings": { + "options": { + "transparentWindow": { + "title": "透明ウィンドウ効果", + "description": "ウィンドウのネイティブなビブランシー/透明効果を有効にします。適用するには再起動が必要です。" + }, + "themeAppearance": { + "title": "テーマ", + "description": "アプリケーションのカラーテーマを選択します。", + "auto": "システムに従う", + "light": "ライト", + "dark": "ダーク" + } } }, "error": { @@ -102,6 +139,7 @@ "close": "閉じる", "copy": "コピー (&C)", "cut": "切り取り (&T)", + "developerMode": "開発者モード", "documentation": "ドキュメント", "editMenu": "編集 (&E)", "fileMenu": "ファイル (&F)", @@ -135,6 +173,21 @@ "announcement": "ヒューストン、我々は問題を抱えています", "reload": "再読み込み" }, + "videoCall": { + "loading": { + "initial": "ビデオ通話を読み込み中...", + "reloading": "ビデオ通話を再読み込み中...", + "description": "ビデオ通話に接続中です。しばらくお待ちください" + }, + "error": { + "title": "ビデオ通話の読み込みに失敗しました", + "announcement": "ヒューストン、問題が発生しました", + "timeout": "タイムアウト - ビデオ通話が15秒以内に読み込めませんでした", + "crashed": "Webviewがクラッシュしました", + "maxRetriesReached": "複数回の試行後も読み込みに失敗しました", + "reload": "ビデオ通話を再読み込み" + } + }, "sidebar": { "addNewServer": "新しいサーバーを追加", "item": { @@ -176,5 +229,31 @@ "noWindowsFound": "ウィンドウが見つかりません", "cancel": "キャンセル", "share": "共有" + }, + "serverInfo": { + "title": "サーバー情報", + "urlLabel": "URL:", + "versionLabel": "バージョン:", + "unknown": "不明", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "サポートされているバージョン", + "statusLabel": "ステータス:", + "status": { + "loading": "読み込み中...", + "error": "読み込みに失敗しました", + "loaded": "読み込み済み", + "idle": "アイドル", + "from": "{{source}}から" + }, + "supported": { + "unknown": "不明", + "expiring": "期限切れ間近", + "yes": "はい", + "no": "いいえ" + }, + "expiration": { + "label": "有効期限:", + "expiresOn": "{{date}}に期限切れ" + } } -} \ No newline at end of file +} diff --git a/src/i18n/nb-NO.i18n.json b/src/i18n/nb-NO.i18n.json index 6f31cf5a2e..fa62230921 100644 --- a/src/i18n/nb-NO.i18n.json +++ b/src/i18n/nb-NO.i18n.json @@ -1 +1,43 @@ -{ } \ No newline at end of file +{ + "settings": { + "options": { + "transparentWindow": { + "title": "Gjennomsiktig vinduseffekt", + "description": "Aktiver innfødt vibrasjon/gjennomsiktighetseffekt for vinduet. Krever omstart for å bruke." + }, + "themeAppearance": { + "title": "Tema", + "description": "Velg fargtemaet for applikasjonen.", + "auto": "Følg system", + "light": "Lys", + "dark": "Mørk" + } + } + }, + "serverInfo": { + "title": "Serverinformasjon", + "urlLabel": "URL:", + "versionLabel": "Versjon:", + "unknown": "Ukjent", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Støttede versjoner", + "statusLabel": "Status:", + "status": { + "loading": "Laster...", + "error": "Kunne ikke laste", + "loaded": "Lastet", + "idle": "Inaktiv", + "from": "fra {{source}}" + }, + "supported": { + "unknown": "Ukjent", + "expiring": "Utløper snart", + "yes": "Ja", + "no": "Nei" + }, + "expiration": { + "label": "Utløpsdato:", + "expiresOn": "Utløper {{date}}" + } + } +} diff --git a/src/i18n/nn.i18n.json b/src/i18n/nn.i18n.json index 6f31cf5a2e..9bb5f263de 100644 --- a/src/i18n/nn.i18n.json +++ b/src/i18n/nn.i18n.json @@ -1 +1,43 @@ -{ } \ No newline at end of file +{ + "settings": { + "options": { + "transparentWindow": { + "title": "Gjennomsiktig vinduseffekt", + "description": "Aktiver innfødd vibrasjon/gjennomsiktighetseffekt for vinduet. Krever omstart for å bruke." + }, + "themeAppearance": { + "title": "Tema", + "description": "Velg fargtemaet for applikasjonen.", + "auto": "Følg system", + "light": "Lys", + "dark": "Mørk" + } + } + }, + "serverInfo": { + "title": "Serverinformasjon", + "urlLabel": "URL:", + "versionLabel": "Versjon:", + "unknown": "Ukjent", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Støttede versjoner", + "statusLabel": "Status:", + "status": { + "loading": "Lastar...", + "error": "Kunne ikkje laste", + "loaded": "Lasta", + "idle": "Inaktiv", + "from": "frå {{source}}" + }, + "supported": { + "unknown": "Ukjent", + "expiring": "Utløper snart", + "yes": "Ja", + "no": "Nei" + }, + "expiration": { + "label": "Utløpsdato:", + "expiresOn": "Utløper {{date}}" + } + } +} diff --git a/src/i18n/no.i18n.json b/src/i18n/no.i18n.json index e4e3737d0f..9c947decb4 100644 --- a/src/i18n/no.i18n.json +++ b/src/i18n/no.i18n.json @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Se etter oppdateringer ved start", "noUpdatesAvailable": "Ingen oppdateringer er tilgjengelige.", "copyright": "Opphavsrett {{copyright}}", - "errorWhenLookingForUpdates": "Det har oppstått en feil under søk etter oppdateringer" + "errorWhenLookingForUpdates": "Det har oppstått en feil under søk etter oppdateringer", + "updateChannel": { + "label": "Oppdater kanal", + "latest": "Stabil", + "beta": "Beta", + "alpha": "Alfa (eksperimentell)" + } }, "addServer": { "title": "Legg til server", @@ -125,6 +131,28 @@ "yes": "Tillate", "no": "Kansellere" }, + "mediaPermission": { + "title": "Medietillatelse påkrevd", + "message": "Tilgang til {{- permissionType}} er for øyeblikket deaktivert i systeminnstillingene dine.", + "detail": "For å aktivere videosamtalefunksjoner, vennligst tillat tilgang i systemets personverninnstillinger og start deretter applikasjonen på nytt.", + "openSettings": "Åpne innstillinger", + "cancel": "Avbryt", + "microphone": "Mikrofon", + "camera": "Kamera", + "both": "Mikrofon og Kamera" + }, + "microphonePermissionDenied": { + "title": "Mikrofontilgang påkrevd", + "message": "Mikrofontilgang kreves for å {{- actionType}}. Vennligst sjekk systeminnstillingene dine.", + "detail": "Du kan aktivere mikrofontilgang i systemets personverninnstillinger.", + "openSettings": "Åpne innstillinger", + "cancel": "Avbryt", + "actionTypes": { + "initiateCall": "foreta samtaler", + "answerCall": "ta imot samtaler", + "recordMessage": "spille inn meldinger" + } + }, "outlookCalendar": { "title": "Outlook-kalender", "encryptionUnavailableTitle": "Kryptering utilgjengelig", @@ -211,9 +239,14 @@ "title": "Maskinvareakselerasjon", "description": "Muliggjør maskinvareakselerasjon når tilgjengelig. Applikasjonen vil lastes inn på nytt ved endring." }, + "videoCallScreenCaptureFallback": { + "title": "Reserveopptak for videosamtaler", + "description": "Deaktiverer Windows Graphics Capture slik at deling virker i RDP-økter. Appen starter på nytt når du endrer dette valget.", + "forcedDescription": "Allerede aktivert fordi appen oppdaget en RDP-økt. Bryteren styrer nå oppførsel ved fremtidige lokale oppstarter." + }, "internalVideoChatWindow": { "title": "Åpne videochat ved hjelp av applikasjonsvinduet", - "description": "Når innstilt, vil Videochat åpnes ved hjelp av et programvindu, ellers vil standardnettleseren brukes. Google Meet deleskjerm støttes ikke i Electron-applikasjoner, så denne konfigurasjonen endrer ikke Meet-anropsadferd, som åpnes i nettleseren.", + "description": "Når innstilt, vil Videochat åpnes ved hjelp av et programvindu, ellers vil standardnettleseren brukes. Google Meet og Jitsi skjermopptak støttes ikke i Electron-applikasjoner, så denne konfigurasjonen endrer ikke anropsadferd, som åpnes i nettleseren.", "masDescription": "Dette alternativet er deaktivert når det er installert fra Mac App Store, av sikkerhetsgrunner vil det åpne Video Chat ved å bruke nettleseren som standard." }, "minimizeOnClose": { @@ -222,16 +255,25 @@ }, "menubar": { "title": "Menylinje", - "description": "Vis menylinjen øverst i vinduet." + "description": "Vis menylinjen øverst i vinduet.", + "disabledHint": "Kan ikke deaktivere menylinjen når sidefeltet er deaktivert. Innstillinger ville bli utilgjengelige." }, "sidebar": { "title": "Sidefelt", - "description": "Vis sidefeltet til venstre i vinduet med serverlisten, nedlastinger og innstillinger." + "description": "Vis sidefeltet til venstre i vinduet med serverlisten, nedlastinger og innstillinger.", + "disabledHint": "Kan ikke deaktivere sidefeltet når menylinjen er deaktivert. Innstillinger ville bli utilgjengelige." }, "trayIcon": { "title": "Systemstatusikon", "description": "Vis ikonet på systemstatusfeltet. Hvis systemstatusikonet er aktivt, vil appen være skjult ved lukke. Ellers avsluttes programmet." }, + "availableBrowsers": { + "title": "Standard nettleser", + "description": "Velg hvilken nettleser som skal åpne eksterne lenker fra denne appen. Systemstandard bruker operativsysteminnstillingene dine.", + "systemDefault": "Systemstandard", + "loading": "Laster inn nettlesere ...", + "current": "Bruker for øyeblikket:" + }, "clearPermittedScreenCaptureServers": { "title": "Fjern skjermopptakstillatelser", "description": "Fjern skjermopptakstillatelsene på videosamtaler som ble valgt for ikke å spørre igjen." @@ -244,6 +286,21 @@ "title": "NTLM-påloggingsinformasjon", "description": "Tillat at NTLM-påloggingsinformasjon brukes når du kobler til en server.", "domains": "Domener som vil bruke påloggingsinformasjonen. Atskilt med komma. Bruk * for å matche alle domener." + }, + "videoCallWindowPersistence": { + "title": "Husk posisjonen til videosamtalevinduet", + "description": "Lagre og gjenopprett posisjonen og størrelsen på videosamtalevinduer mellom økter" + }, + "transparentWindow": { + "title": "Gjennomsiktig vinduseffekt", + "description": "Aktiver innfødt vibrans/gjennomsiktighetseffekt for vinduet. Krever omstart for å brukes." + }, + "themeAppearance": { + "title": "Tema", + "description": "Velg fargetemaet for applikasjonen.", + "auto": "Følg system", + "light": "Lys", + "dark": "Mørk" } } }, @@ -268,6 +325,7 @@ "close": "Lukke", "copy": "Kopiere", "cut": "Kutt", + "developerMode": "Utviklermodus", "disableGpu": "Deaktiver GPU", "documentation": "Dokumentasjon", "downloads": "Nedlastinger", @@ -299,6 +357,9 @@ "showTrayIcon": "Systemstatusikon", "toggleDevTools": "Slå på Utviklerverktøy", "openConfigFolder": "Åpne konfigurasjonsmappe", + "videoCallDevTools": "Åpne videosamtale og utviklerverktøy", + "videoCallTools": "Verktøy for videosamtaler", + "videoCallDevToolsAutoOpen": "Automatisk åpning av utviklerverktøy", "undo": "Angre", "unhide": "Vis alle", "viewMenu": "Visning", @@ -311,6 +372,22 @@ "announcement": "Houston, vi har et problem", "reload": "Last inn på nytt" }, + "videoCall": { + "loading": { + "initial": "Laster videosamtale...", + "reloading": "Laster videosamtale på nytt...", + "description": "Vennligst vent mens vi kobler til videosamtalen" + }, + "error": { + "title": "Kunne ikke laste videosamtale", + "announcement": "Houston, vi har et problem", + "timeout": "Tidsavbrudd - videosamtalen kunne ikke lastes innen 15 sekunder", + "crashed": "Webview krasjet", + "maxRetriesReached": "Kunne ikke laste etter flere forsøk", + "reload": "Last videosamtale på nytt", + "noUrl": "Ingen videosamtale-URL oppgitt" + } + }, "unsupportedServer": { "title": "{{instanceDomain}} kjører en versjon av Rocket.Chat som ikke støttes", "announcement": "En administrator må oppdatere serveren til en støttet versjon for å gjenaktivere tilgang fra mobil- og skrivebordsapper.", @@ -333,7 +410,9 @@ "clearCache": "Tøm buffer", "clearStorageData": "Fjern lagringsdata", "copyCurrentUrl": "Kopier gjeldende URL", - "reloadClearingCache": "Tving omlasting" + "reloadClearingCache": "Tving omlasting", + "serverInfo": "Serverinfo", + "supportedVersionsInfo": "Informasjon om støttede versjoner" }, "tooltips": { "unreadMessage": "{{- count}} ulest melding", @@ -370,5 +449,44 @@ "unreadMessage": "Uleste meldinger", "unreadMention": "Uleste omtaler", "noUnreadMessage": "Ingen uleste meldinger" + }, + "screenSharing": { + "permissionDenied": "Tillatelse til skjermopptak nektet", + "permissionRequired": "Tillatelse til skjermopptak kreves for å dele skjermen din.", + "permissionInstructions": "Vennligst aktiver det i systeminnstillingene dine og prøv på nytt.", + "openSystemPreferences": "Åpne systeminnstillinger", + "title": "Del skjermen din", + "entireScreen": "Hele skjermen din", + "applicationWindow": "Programvindu", + "noScreensFound": "Ingen skjermer funnet", + "noWindowsFound": "Ingen vinduer funnet", + "cancel": "Kansellere", + "share": "Dele" + }, + "serverInfo": { + "title": "Serverinformasjon", + "urlLabel": "URL:", + "versionLabel": "Versjon:", + "unknown": "Ukjent", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Støttede versjoner", + "statusLabel": "Status:", + "status": { + "loading": "Laster...", + "error": "Kunne ikke laste", + "loaded": "Lastet", + "idle": "Inaktiv", + "from": "fra {{source}}" + }, + "supported": { + "unknown": "Ukjent", + "expiring": "Utløper snart", + "yes": "Ja", + "no": "Nei" + }, + "expiration": { + "label": "Utløpsdato:", + "expiresOn": "Utløper {{date}}" + } } -} \ No newline at end of file +} diff --git a/src/i18n/pl.i18n.json b/src/i18n/pl.i18n.json index 4238d6292a..e5e78c2be3 100644 --- a/src/i18n/pl.i18n.json +++ b/src/i18n/pl.i18n.json @@ -22,7 +22,13 @@ "checkUpdatesOnStart": "Sprawdzanie aktualizacji przy uruchomieniu", "noUpdatesAvailable": "Brak dostępnych aktualizacji.", "copyright": "Prawa autorskie {{copyright}}", - "errorWhenLookingForUpdates": "Wystąpił błąd podczas wyszukiwania aktualizacji" + "errorWhenLookingForUpdates": "Wystąpił błąd podczas wyszukiwania aktualizacji", + "updateChannel": { + "label": "Kanał aktualizacji", + "latest": "Stabilny", + "beta": "Beta", + "alpha": "Alfa (eksperymentalna)" + } }, "addServer": { "title": "Dodaj Serwer", @@ -85,6 +91,28 @@ "announcement": "Wybierz Certyfikat", "select": "Wybierz", "validDates": "Ważny od {{-validStart,}} do {{-validExpiry,}}" + }, + "mediaPermission": { + "title": "Wymagane Uprawnienie Mediów", + "message": "Dostęp do {{- permissionType}} jest obecnie wyłączony w ustawieniach systemu.", + "detail": "Aby włączyć funkcje rozmów wideo, proszę zezwolić na dostęp w ustawieniach prywatności systemu, a następnie uruchomić ponownie aplikację.", + "openSettings": "Otwórz Ustawienia", + "cancel": "Anuluj", + "microphone": "mikrofonu", + "camera": "kamery", + "both": "mikrofonu i kamery" + }, + "microphonePermissionDenied": { + "title": "Wymagany dostęp do mikrofonu", + "message": "Do {{- actionType}} wymagany jest dostęp do mikrofonu. Sprawdź ustawienia systemowe.", + "detail": "Możesz włączyć dostęp do mikrofonu w ustawieniach prywatności systemu.", + "openSettings": "Otwórz ustawienia", + "cancel": "Anuluj", + "actionTypes": { + "initiateCall": "wykonywania połączeń", + "answerCall": "odbierania połączeń", + "recordMessage": "nagrywania wiadomości" + } } }, "settings": { @@ -101,6 +129,17 @@ "systemDefault": "Domyślna systemu", "loading": "Ładowanie przeglądarek...", "current": "Aktualnie używana:" + }, + "transparentWindow": { + "title": "Efekt przezroczystego okna", + "description": "Włącz natywny efekt wibracji/przezroczystości dla okna. Wymaga ponownego uruchomienia, aby zastosować." + }, + "themeAppearance": { + "title": "Motyw", + "description": "Wybierz motyw kolorystyczny aplikacji.", + "auto": "Zgodnie z systemem", + "light": "Jasny", + "dark": "Ciemny" } } }, @@ -125,6 +164,7 @@ "close": "Zamknij", "copy": "&Kopiuj", "cut": "&Wytnij", + "developerMode": "Tryb programisty", "documentation": "Dokumentacja", "editMenu": "&Edycja", "fileMenu": "&Plik", @@ -159,9 +199,25 @@ }, "loadingError": { "title": "Błąd ładowania serwera", - "announcement": "Hjuston, mamy problem", + "announcement": "Houston, mamy problem", "reload": "Przeładuj" }, + "videoCall": { + "loading": { + "initial": "Ładowanie połączenia wideo...", + "reloading": "Ponowne ładowanie połączenia wideo...", + "description": "Proszę czekać, łączymy z połączeniem wideo" + }, + "error": { + "title": "Nie udało się załadować połączenia wideo", + "announcement": "Houston, mamy problem", + "timeout": "Przekroczono czas oczekiwania - nie udało się załadować połączenia wideo w ciągu 15 sekund", + "crashed": "Webview się zawiesił", + "maxRetriesReached": "Nie udało się załadować po wielu próbach", + "reload": "Przeładuj połączenie wideo", + "noUrl": "Nie podano adresu URL połączenia wideo" + } + }, "selfxss": { "title": "Stop!", "description": "To jest funkcja przeglądarki dedykowana programistom. Jeśli ktoś powiedział Ci, by skopiować i wkleić coś tutaj aby włączyć funcjonalność Rocket.Chata lub \"zhackować\" czyjeś konto, to jest to oszustwo a Ty dasz temu komuś dostęp do swojego konta Rocket.Chat.", @@ -214,5 +270,31 @@ "noWindowsFound": "Nie znaleziono okien", "cancel": "Anuluj", "share": "Udostępnij" + }, + "serverInfo": { + "title": "Informacje o serwerze", + "urlLabel": "URL:", + "versionLabel": "Wersja:", + "unknown": "Nieznana", + "exchangeUrlLabel": "URL Outlook Exchange:", + "supportedVersionsTitle": "Obsługiwane wersje", + "statusLabel": "Status:", + "status": { + "loading": "Ładowanie...", + "error": "Nie udało się załadować", + "loaded": "Załadowano", + "idle": "Bezczynny", + "from": "z {{source}}" + }, + "supported": { + "unknown": "Nieznana", + "expiring": "Wygasa", + "yes": "Tak", + "no": "Nie" + }, + "expiration": { + "label": "Wygaśnięcie:", + "expiresOn": "Wygasa {{date}}" + } } -} \ No newline at end of file +} diff --git a/src/i18n/pt-BR.i18n.json b/src/i18n/pt-BR.i18n.json index 18b20c6ff1..ec3fb09df7 100644 --- a/src/i18n/pt-BR.i18n.json +++ b/src/i18n/pt-BR.i18n.json @@ -1,6 +1,6 @@ { "contextMenu": { - "cut": "Cor&tar", + "cut": "Recor&tar", "copy": "&Copiar", "paste": "Co&lar", "selectAll": "&Selecionar tudo", @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Verificar Atualizações ao Abrir", "noUpdatesAvailable": "Não há atualizações disponíveis.", "copyright": "{{copyright}} Todos os direitos reservados.", - "errorWhenLookingForUpdates": "Ocorreu um erro ao procurar por atualizações" + "errorWhenLookingForUpdates": "Ocorreu um erro ao procurar por atualizações", + "updateChannel": { + "label": "Canal de atualização", + "latest": "Estável", + "beta": "Beta", + "alpha": "Alpha (Experimental)" + } }, "addServer": { "title": "Adicionar Servidor", @@ -125,6 +131,28 @@ "yes": "Permitir", "no": "Cancelar" }, + "mediaPermission": { + "title": "Permissão de Mídia Necessária", + "message": "O acesso ao {{- permissionType}} está atualmente desabilitado nas configurações do seu sistema.", + "detail": "Para ativar os recursos de videochamada, permita o acesso nas configurações de privacidade do seu sistema e reinicie a aplicação.", + "openSettings": "Abrir Configurações", + "cancel": "Cancelar", + "microphone": "Microfone", + "camera": "Câmera", + "both": "Microfone e Câmera" + }, + "microphonePermissionDenied": { + "title": "Acesso ao microfone necessário", + "message": "O acesso ao microfone é necessário para {{- actionType }}. Verifique as configurações do sistema.", + "detail": "Você pode ativar o acesso ao microfone nas configurações de privacidade do sistema.", + "openSettings": "Abrir configurações", + "cancel": "Cancelar", + "actionTypes": { + "initiateCall": "fazer chamadas", + "answerCall": "atender chamadas", + "recordMessage": "gravar mensagens" + } + }, "supportedVersion": { "title": "Versão de workspace não suportada" } @@ -197,6 +225,11 @@ "title": "Aceleração de hardware", "description": "Ativa aceleração de hardware quando disponível. O aplicativo será reiniciado ao ser alterado." }, + "videoCallScreenCaptureFallback": { + "title": "Fallback de captura para videochamadas", + "description": "Desativa o Windows Graphics Capture para que o compartilhamento funcione em sessões RDP. O aplicativo reinicia ao mudar esta opção.", + "forcedDescription": "Atualmente aplicado porque o aplicativo detectou uma sessão RDP. O alternador define o comportamento das próximas execuções locais." + }, "internalVideoChatWindow": { "title": "Abrir chat em video em uma janela da aplicação", "description": "Se ativado, o Chat de Vídeo será aberto na janela do aplicativo em vez do navegador padrão. No entanto, para Google Meet e Jitsi, a gravação de tela não é suportada em aplicativos Electron, então eles sempre serão abertos no navegador independentemente desta configuração.", @@ -208,11 +241,13 @@ }, "menubar": { "title": "Barra de menus", - "description": "Mostra a barra de menus no topo da aplicação" + "description": "Mostra a barra de menus no topo da aplicação", + "disabledHint": "Não é possível desativar a barra de menus quando a barra lateral está desativada. As configurações se tornariam inacessíveis." }, "sidebar": { "title": "Barra lateral", - "description": "Mostra a barra na lateral da janela com a lista de servidores, downloads e configurações." + "description": "Mostra a barra na lateral da janela com a lista de servidores, downloads e configurações.", + "disabledHint": "Não é possível desativar a barra lateral quando a barra de menus está desativada. As configurações se tornariam inacessíveis." }, "trayIcon": { "title": "Ícone da bandeja", @@ -241,6 +276,17 @@ "videoCallWindowPersistence": { "title": "Lembrar posição da janela de videochamada", "description": "Salvar e restaurar a posição e o tamanho das janelas de videochamada entre as sessões" + }, + "transparentWindow": { + "title": "Efeito de janela transparente", + "description": "Ativar efeito nativo de vibração/transparência para a janela. Requer reinicialização para aplicar." + }, + "themeAppearance": { + "title": "Tema", + "description": "Escolha o tema de cores para o aplicativo.", + "auto": "Seguir sistema", + "light": "Claro", + "dark": "Escuro" } } }, @@ -264,7 +310,8 @@ "clearTrustedCertificates": "Limpar certificados confiáveis", "close": "Fechar", "copy": "&Copiar", - "cut": "Cor&tar", + "cut": "Recor&tar", + "developerMode": "Modo desenvolvedor", "disableGpu": "Desabilitar GPU", "documentation": "Documentação", "downloads": "Downloads", @@ -307,6 +354,21 @@ "announcement": "Houston, nós temos um problema", "reload": "Recarregar" }, + "videoCall": { + "loading": { + "initial": "Carregando chamada de vídeo...", + "reloading": "Recarregando chamada de vídeo...", + "description": "Por favor aguarde enquanto nos conectamos à chamada de vídeo" + }, + "error": { + "title": "Falha ao carregar chamada de vídeo", + "announcement": "Houston, temos um problema", + "timeout": "Tempo limite excedido - a chamada de vídeo não pôde carregar em 15 segundos", + "crashed": "Webview travou", + "maxRetriesReached": "Falha ao carregar após múltiplas tentativas", + "reload": "Recarregar chamada de vídeo" + } + }, "unsupportedServer": { "title": "{{instanceDomain}} está executando uma versão não suportada do Rocket.Chat", "announcement": "Um administrador precisa atualizar o workspace para uma versão suportada para reativar o acesso aos aplicativos móveis e desktop.", @@ -378,5 +440,31 @@ "noWindowsFound": "Nenhuma janela encontrada", "cancel": "Cancelar", "share": "Compartilhar" + }, + "serverInfo": { + "title": "Informações do Servidor", + "urlLabel": "URL:", + "versionLabel": "Versão:", + "unknown": "Desconhecido", + "exchangeUrlLabel": "URL do Outlook Exchange:", + "supportedVersionsTitle": "Versões Suportadas", + "statusLabel": "Status:", + "status": { + "loading": "Carregando...", + "error": "Falha ao carregar", + "loaded": "Carregado", + "idle": "Inativo", + "from": "de {{source}}" + }, + "supported": { + "unknown": "Desconhecido", + "expiring": "Expirando", + "yes": "Sim", + "no": "Não" + }, + "expiration": { + "label": "Expiração:", + "expiresOn": "Expira em {{date}}" + } } -} \ No newline at end of file +} diff --git a/src/i18n/resources.ts b/src/i18n/resources.ts index dcac469712..a176a7d43a 100644 --- a/src/i18n/resources.ts +++ b/src/i18n/resources.ts @@ -5,6 +5,8 @@ export default { 'en': (): Promise => import('./en.i18n.json'), 'es': (): Promise => import('./es.i18n.json'), 'fi': (): Promise => import('./fi.i18n.json'), + 'sv': (): Promise => import('./sv.i18n.json'), + 'no': (): Promise => import('./no.i18n.json'), 'fr': (): Promise => import('./fr.i18n.json'), 'hu': (): Promise => import('./hu.i18n.json'), 'it-IT': (): Promise => import('./it-IT.i18n.json'), diff --git a/src/i18n/ru.i18n.json b/src/i18n/ru.i18n.json index 57cd567052..d9d46956af 100644 --- a/src/i18n/ru.i18n.json +++ b/src/i18n/ru.i18n.json @@ -24,7 +24,13 @@ "checkUpdatesOnStart": "Проверять обновления при запуске", "noUpdatesAvailable": "Нет обновлений.", "copyright": "Все права защищены {{copyright}}", - "errorWhenLookingForUpdates": "При поиске обновлений произошла ошибка" + "errorWhenLookingForUpdates": "При поиске обновлений произошла ошибка", + "updateChannel": { + "label": "Канал обновлений", + "latest": "Стабильный", + "beta": "Бета", + "alpha": "Альфа (Экспериментальный)" + } }, "addServer": { "title": "Добавить сервер", @@ -109,6 +115,28 @@ "dontAskAgain": "Всегда разрешать делать снимки экрана для видеовызовов с этого сервера", "yes": "Разрешить", "no": "Отмена" + }, + "mediaPermission": { + "title": "Требуется Разрешение на Медиа", + "message": "Доступ к {{- permissionType}} в настоящее время отключен в настройках вашей системы.", + "detail": "Для включения функций видеозвонков разрешите доступ в настройках конфиденциальности вашей системы и перезапустите приложение.", + "openSettings": "Открыть Настройки", + "cancel": "Отмена", + "microphone": "Микрофон", + "camera": "Камера", + "both": "Микрофон и Камера" + }, + "microphonePermissionDenied": { + "title": "Требуется доступ к микрофону", + "message": "Для {{- actionType}} требуется доступ к микрофону. Пожалуйста, проверьте настройки системы.", + "detail": "Вы можете включить доступ к микрофону в настройках конфиденциальности вашей системы.", + "openSettings": "Открыть настройки", + "cancel": "Отмена", + "actionTypes": { + "initiateCall": "совершать звонки", + "answerCall": "отвечать на звонки", + "recordMessage": "записывать сообщения" + } } }, "downloads": { @@ -184,6 +212,11 @@ "title": "Аппаратное ускорение", "description": "Включает использование аппаратного ускорения, если оно доступно. Приложение будет перезапущено при изменении этой настройки." }, + "videoCallScreenCaptureFallback": { + "title": "Резервный захват экрана для видеозвонков", + "description": "Отключает Windows Graphics Capture, чтобы демонстрация экрана работала в RDP-сессиях. Приложение перезапускается при изменении настройки.", + "forcedDescription": "Сейчас включено принудительно, потому что приложение обнаружило сеанс удалённого рабочего стола. Переключатель будет управлять будущими запусками при работе локально." + }, "internalVideoChatWindow": { "title": "Открывать видеочат в окне приложения", "description": "Если этот параметр включен, видеочат будет открываться в окне приложения вместо браузера по умолчанию. Однако для Google Meet и Jitsi запись экрана не поддерживается в приложениях Electron, поэтому они всегда будут открываться в браузере независимо от этой настройки.", @@ -195,11 +228,13 @@ }, "menubar": { "title": "Главное меню", - "description": "Показать строку меню в верхней части окна." + "description": "Показать строку меню в верхней части окна.", + "disabledHint": "Нельзя отключить строку меню, когда боковая панель уже отключена. Настройки станут недоступными." }, "sidebar": { "title": "Боковая панель", - "description": "Показать боковую панель в левой части окна со списком серверов, загрузками и настройками." + "description": "Показать боковую панель в левой части окна со списком серверов, загрузками и настройками.", + "disabledHint": "Нельзя отключить боковую панель, когда строка меню уже отключена. Настройки станут недоступными." }, "trayIcon": { "title": "Значок в трее", @@ -224,6 +259,17 @@ "videoCallWindowPersistence": { "title": "Запоминать положение окна видеозвонка", "description": "Сохранять и восстанавливать положение и размер окон видеозвонков между сеансами" + }, + "transparentWindow": { + "title": "Эффект прозрачного окна", + "description": "Включить нативный эффект вибрации/прозрачности для окна. Требуется перезапуск для применения." + }, + "themeAppearance": { + "title": "Тема", + "description": "Выберите цветовую тему для приложения.", + "auto": "Следовать системе", + "light": "Светлая", + "dark": "Тёмная" } } }, @@ -248,6 +294,7 @@ "close": "Закрыть", "copy": "&Копировать", "cut": "В&ырезать", + "developerMode": "Режим разработчика", "disableGpu": "Отключить GPU", "documentation": "Документация", "downloads": "Загрузки", @@ -289,6 +336,21 @@ "announcement": "Хьюстон, у нас проблемы", "reload": "Перезагрузить" }, + "videoCall": { + "loading": { + "initial": "Загрузка видеозвонка...", + "reloading": "Перезагрузка видеозвонка...", + "description": "Пожалуйста, подождите, пока мы подключаемся к видеозвонку" + }, + "error": { + "title": "Не удалось загрузить видеозвонок", + "announcement": "Хьюстон, у нас проблема", + "timeout": "Превышено время ожидания - видеозвонок не удалось загрузить за 15 секунд", + "crashed": "Webview завис", + "maxRetriesReached": "Не удалось загрузить после нескольких попыток", + "reload": "Перезагрузить видеозвонок" + } + }, "selfxss": { "title": "Стоп!", "description": "Это функция браузера, предназначенная для разработчиков. Если кто-то сказал вам скопировать-вставить что-то сюда, чтобы включить функцию Rocket.Chat или \"взломать\" чей-то аккаунт, это мошенничество и даст им доступ к вашему аккаунту Rocket.Chat.", @@ -345,5 +407,31 @@ "noWindowsFound": "Окна не найдены", "cancel": "Отмена", "share": "Демонстрировать" + }, + "serverInfo": { + "title": "Информация о сервере", + "urlLabel": "URL:", + "versionLabel": "Версия:", + "unknown": "Неизвестно", + "exchangeUrlLabel": "URL Outlook Exchange:", + "supportedVersionsTitle": "Поддерживаемые версии", + "statusLabel": "Статус:", + "status": { + "loading": "Загрузка...", + "error": "Ошибка загрузки", + "loaded": "Загружено", + "idle": "Неактивно", + "from": "из {{source}}" + }, + "supported": { + "unknown": "Неизвестно", + "expiring": "Истекает", + "yes": "Да", + "no": "Нет" + }, + "expiration": { + "label": "Истечение срока:", + "expiresOn": "Истекает {{date}}" + } } } diff --git a/src/i18n/se.i18n.json b/src/i18n/se.i18n.json deleted file mode 100644 index 6f31cf5a2e..0000000000 --- a/src/i18n/se.i18n.json +++ /dev/null @@ -1 +0,0 @@ -{ } \ No newline at end of file diff --git a/src/i18n/sv.i18n.json b/src/i18n/sv.i18n.json new file mode 100644 index 0000000000..515dbc0319 --- /dev/null +++ b/src/i18n/sv.i18n.json @@ -0,0 +1,490 @@ +{ + "contextMenu": { + "cut": "Cu&t", + "copy": "&Kopiera", + "paste": "&Klistra in", + "selectAll": "Välj &alla", + "undo": "&Ångra", + "redo": "&Gör om", + "spelling": "Stavning", + "spellingLanguages": "Stavningsspråk", + "moreSpellingSuggestions": "Fler stavningsförslag", + "noSpellingSuggestions": "Inga förslag", + "copyLinkAddress": "Kopiera länkadressen", + "copyLinkText": "Kopiera länktext", + "openLink": "Öppna länken", + "saveImageAs": "Spara bilden som...", + "copyImage": "Kopiera bilden" + }, + "dialog": { + "about": { + "title": "Om {{- appName}}", + "version": "Version: <1>{{-version}}1>", + "checkUpdates": "Sök efter uppdateringar", + "checkUpdatesOnStart": "Sök efter uppdateringar vid start", + "noUpdatesAvailable": "Inga uppdateringar är tillgängliga.", + "copyright": "Upphovsrätt {{copyright}}", + "errorWhenLookingForUpdates": "Ett fel har inträffat vid sökning efter uppdateringar", + "updateChannel": { + "label": "Uppdatera Kanal", + "latest": "Stabil", + "beta": "Beta", + "alpha": "Alpha (experimentell)" + } + }, + "addServer": { + "title": "Lägg till server", + "message": "Vill du lägga till \"{{- host}}\" till din lista över servrar?", + "add": "Lägg till", + "cancel": "Avbryt" + }, + "addServerError": { + "title": "Ogiltigt servernamn", + "message": "Servern \"{{- host}}\" kunde inte valideras och lades därför inte till." + }, + "certificateError": { + "title": "Certifikatsfel", + "message": "Litar du på certifikat från \"{{- issuerName}}\"? Klicka bara på \"Ja\" om du verkligen litar på det här certifikatet. Om du är osäker bör du kontakta ditt IT- eller säkerhetsteam innan du klickar på \"Ja\".", + "yes": "Ja", + "no": "Nej" + }, + "clearCache": { + "announcement": "Tvinga omladdning", + "title": "Vill du behålla din inloggningssession?", + "message": "Om du tar bort din inloggningssession loggas du ut och ombeds att ange dina inloggningsuppgifter igen.", + "keepLoginData": "Behåll inloggningssessionen", + "deleteLoginData": "Radera inloggningssessionen", + "clearingWait": "Vänligen vänta", + "cancel": "Avbryt" + }, + "downloadRemoval": { + "title": "Är du säker?", + "message": "Vill du ta bort denna nedladdning?", + "yes": "Ja", + "no": "Nej" + }, + "resetAppData": { + "title": "Återställ appdata", + "message": "Detta kommer att logga ut dig från alla dina team och återställa appen till dess ursprungliga inställningar. Detta kan inte ångras.", + "yes": "Ja", + "cancel": "Avbryt" + }, + "clearPermittedScreenCaptureServers": { + "title": "Rensa tillåtna servrar för skärmdumpning", + "message": "Detta kommer att rensa alla behörigheter för skärmdumpsservrar, vilket gör att de ber om tillstånd igen. Detta kan inte ångras.", + "yes": "Ja", + "cancel": "Avbryt" + }, + "screenshare": { + "title": "Dela din skärm", + "announcement": "Välj en skärm att dela" + }, + "update": { + "title": "Uppdatering finns tillgänglig", + "announcement": "En ny uppdatering finns tillgänglig", + "message": "En ny version av Rocket.Chat Desktop App är tillgänglig!", + "currentVersion": "Nuvarande version:", + "newVersion": "Ny version:", + "install": "Installera uppdatering", + "remindLater": "Påminn mig senare", + "skip": "Hoppa över denna version" + }, + "updateDownloading": { + "title": "Nedladdning av uppdatering", + "message": "Du kommer att få ett meddelande när uppdateringen är klar att installeras", + "ok": "OK" + }, + "updateInstallLater": { + "title": "Installera senare", + "message": "Uppdateringen kommer att installeras när du lämnar appen", + "ok": "OK" + }, + "updateReady": { + "title": "Uppdatering klar för installation", + "message": "Uppdateringen har laddats ner", + "installNow": "Installera nu", + "installLater": "Installera senare" + }, + "updateSkip": { + "title": "Hoppa över uppdatering", + "message": "Vi kommer att meddela dig när nästa uppdatering är tillgänglig.\nOm du ändrar dig kan du kontrollera om det finns uppdateringar i menyn Om.", + "ok": "OK" + }, + "selectClientCertificate": { + "announcement": "Välj certifikat", + "select": "Välj", + "validDates": "Gäller från {{-validStart,}} till {{-validExpiry,}}" + }, + "openingExternalProtocol": { + "title": "Länk med anpassat protokoll", + "message": "{{- protocol }} länken kräver en extern applikation.", + "detail": "Den begärda länken är {{- url }}. Vill du fortsätta?", + "dontAskAgain": "Öppna alltid den här typen av länkar i den tillhörande appen", + "yes": "Ja", + "no": "Nej" + }, + "allowVideoCallCaptureScreen": { + "title": "Videosamtalet försöker fånga din skärm", + "message": "Videosamtalet begär tillstånd att fånga din skärm.", + "detail": "Videosamtalet från servern {{- url }} kräver tillstånd för att du ska kunna dela din skärm med andra.", + "dontAskAgain": "Tillåt alltid videosamtal från denna server att fånga din skärm", + "yes": "Tillåt", + "no": "Avbryt" + }, + "mediaPermission": { + "title": "Tillstånd för media krävs", + "message": "{{- permissionType}} är för närvarande avaktiverad i dina systeminställningar.", + "detail": "För att aktivera videosamtalsfunktioner måste du tillåta åtkomst i systemets sekretessinställningar och sedan starta om programmet.", + "openSettings": "Öppna inställningar", + "cancel": "Avbryt", + "microphone": "Mikrofon", + "camera": "Kamera", + "both": "Mikrofon och kamera" + }, + "microphonePermissionDenied": { + "title": "Mikrofonåtkomst krävs", + "message": "Mikrofonåtkomst krävs för att {{- actionType}}. Kontrollera dina systeminställningar.", + "detail": "Du kan aktivera mikrofonåtkomst i datorns integritetsinställningar.", + "openSettings": "Öppna inställningar", + "cancel": "Avbryt", + "actionTypes": { + "initiateCall": "ringa samtal", + "answerCall": "svara på samtal", + "recordMessage": "spela in meddelanden" + } + }, + "outlookCalendar": { + "title": "Outlook-kalender", + "encryptionUnavailableTitle": "Kryptering är inte tillgänglig", + "encryptionUnavailable": "Ditt operativsystem stöder inte kryptering.\nDina inloggningsuppgifter lagras i klartext.", + "field_required": "Detta fält är obligatoriskt", + "remember_credentials": "Kom ihåg mina inloggningsuppgifter", + "cancel": " Avbryt", + "submit": "Logga in" + }, + "supportedVersion": { + "title": "Versionen av arbetsytan stöds inte" + } + }, + "downloads": { + "title": "Nedladdningar", + "notifications": { + "downloadFinished": "Nedladdning klar", + "downloadInterrupted": "Nedladdning avbruten", + "downloadCancelled": "Nedladdning avbruten", + "downloadFailed": "Nedladdning misslyckades", + "downloadExpired": "Nedladdning har löpt ut", + "downloadExpiredMessage": "Försök ladda ner från källan igen." + }, + "filters": { + "search": "Sök", + "server": "Server", + "mimeType": "Typ", + "status": "Status", + "clear": "Rensa filter", + "all": "Alla", + "mimes": { + "images": "Bilder", + "videos": "Videor", + "audios": "Ljudfiler", + "texts": "Texter", + "files": "Filer" + }, + "statuses": { + "paused": "Pausad", + "cancelled": "Avbruten" + } + }, + "item": { + "cancel": "Avbryt", + "copyLink": "Kopiera länk", + "errored": "Nedladdning avbruten", + "pause": "Pausa", + "progressSize": "{{receivedBytes, byteSize}} av {{totalBytes, byteSize}} ({{ratio, percentage}})", + "remove": "Ta bort från listan", + "resume": "Återuppta", + "retry": "Försök igen", + "showInFolder": "Visa i mapp" + }, + "showingResults": "Visar resultat {{first}} - {{last}} av {{count}}" + }, + "certificatesManager": { + "title": "Certifikatsansvarig", + "trustedCertificates": "Betrodda certifikat", + "notTrustedCertificates": "Inte betrodda certifikat", + "item": { + "domain": "Domän", + "actions": "Åtgärder", + "remove": "Ta bort" + } + }, + "settings": { + "title": "Inställningar", + "general": "Allmänt", + "certificates": "Certifikat", + "options": { + "report": { + "title": "Rapportera fel till utvecklare", + "description": "Rapportera fel anonymt till utvecklarna. Delad information inkluderar appens versionsnummer, typ av operativsystem, server-URL, enhetsspråk och feltyp. Inget innehåll eller användarnamn delas.", + "masDescription": "Detta alternativ är inaktiverat när det installeras från Mac App Store, felen kommer att rapporteras via Mac App Stores felrapporteringsprocess." + }, + "flashFrame": { + "title": "Aktivera Flash Frame", + "titleDarwin": "Toggle Dock Bounce vid varning", + "description": "Blinkar fönstret för att dra till sig användarens uppmärksamhet.", + "onLinux": "Vissa Linux-distributioner har inte stöd för den här funktionen.", + "descriptionDarwin": "Appikonen studsar i dockan för att dra till sig användarens uppmärksamhet." + }, + "hardwareAcceleration": { + "title": "Hårdvaruacceleration", + "description": "Aktiverar användning av hårdvaruacceleration när sådan finns tillgänglig. Programmet laddas om vid ändring." + }, + "videoCallScreenCaptureFallback": { + "title": "Reservläge för videosamtal", + "description": "Stänger av Windows Graphics Capture så att delning fungerar i RDP-sessioner. Appen startas om när du ändrar valet.", + "forcedDescription": "Redan aktiverat eftersom appen upptäckte en RDP-session. Vippbrytaren anger beteendet vid kommande lokala starter." + }, + "internalVideoChatWindow": { + "title": "Öppna videochatt i programfönstret", + "description": "Om den är aktiverad öppnas videochatten i programmets fönster i stället för i standardwebbläsaren. För Google Meet och Jitsi stöds dock inte skärminspelning i Electron-applikationer, så de kommer alltid att öppnas i webbläsaren oavsett denna inställning.", + "masDescription": "Detta alternativ är inaktiverat när det installeras från Mac App Store. Av säkerhetsskäl öppnas Video Chat alltid i webbläsaren som standard." + }, + "minimizeOnClose": { + "title": "Minimera vid stängning", + "description": "När appen stängs kommer den att minimeras, annars kommer applikationen avslutas. Tray Icon måste inaktiveras för att detta ska gälla." + }, + "menubar": { + "title": "Menyfält", + "description": "Visa menyfältet högst upp i fönstret.", + "disabledHint": "Kan inte inaktivera menyfältet när sidofältet är inaktiverat. Inställningar skulle bli otillgängliga." + }, + "sidebar": { + "title": "Sidofält", + "description": "Visa sidofältet till vänster i fönstret med serverlistan, nedladdningar och inställningar.", + "disabledHint": "Kan inte inaktivera sidofältet när menyfältet är inaktiverat. Inställningar skulle bli otillgängliga." + }, + "trayIcon": { + "title": "Ikon för brickan", + "description": "Visa ikonen i systemfältet. Om ikonen är aktiv kommer appen att döljas i facket när den stängs. Annars avslutas programmet." + }, + "availableBrowsers": { + "title": "Standardwebbläsare", + "description": "Välj vilken webbläsare som ska öppna externa länkar från den här appen. Systemstandard använder inställningarna för ditt operativsystem.", + "systemDefault": "Systemstandard", + "loading": "Laddar webbläsare...", + "current": "Använder för närvarande:" + }, + "clearPermittedScreenCaptureServers": { + "title": "Rensa behörigheter för skärmdumpning", + "description": "Ta bort skärmdumpstillstånden som valts för att inte fråga igen vid videosamtal." + }, + "allowScreenCaptureOnVideoCalls": { + "title": "Tillåt skärmdump under videosamtal", + "description": "Tillåt skärmdump vid videosamtal. Förfrågan om tillstånd kommer vid varje videosamtal." + }, + "ntlmCredentials": { + "title": "NTLM-autentiseringsuppgifter", + "description": "Tillåt att NTLM-autentiseringsuppgifter används vid anslutning till en server.", + "domains": "Domäner som kommer använda autentiseringsuppgifterna. Separerade med kommatecken. Använd * för att matcha alla domäner." + }, + "videoCallWindowPersistence": { + "title": "Kom ihåg fönstrets position för videosamtal", + "description": "Spara och återställ position och storlek för videosamtalsfönster mellan sessioner" + }, + "transparentWindow": { + "title": "Genomskinlig fönstereffekt", + "description": "Aktivera inbyggd vibrans/genomskinlighetseffekt för fönstret. Kräver omstart för att tillämpa." + }, + "themeAppearance": { + "title": "Tema", + "description": "Välj färgtemat för applikationen.", + "auto": "Följ system", + "light": "Ljus", + "dark": "Mörk" + } + } + }, + "error": { + "authNeeded": "Autentisering krävs, försök {{- auth}}", + "connectTimeout": "Timeout vid anslutningsförsök", + "differentCertificate": "Certifikatet skiljer sig från det tidigare.\n\n {{- detail}}", + "noValidServerFound": "Ingen giltig server hittades på URL:en", + "offline": "Kontrollera din Internetanslutning!" + }, + "landing": { + "invalidUrl": "Ogiltig webbadress", + "validating": "Validerar...", + "inputUrl": "Ange URL till din server", + "connect": "Anslut" + }, + "menus": { + "about": "Om {{- appName}}", + "addNewServer": "Lägg till &ny server", + "back": "&Tillbaka", + "clearTrustedCertificates": "Rensa betrodda certifikat", + "close": "Stäng", + "copy": "&Kopiera", + "cut": "Klipp ut", + "developerMode": "Utvecklarläge", + "disableGpu": "Avaktivera GPU", + "documentation": "Dokumentation", + "downloads": "Nedladdningar", + "settings": "Inställningar", + "editMenu": "&Editera", + "fileMenu": "&Fil", + "forward": "&Framåt", + "helpMenu": "&Hjälp", + "hide": "Göm {{- appName}}", + "hideOthers": "Dölj andra", + "learnMore": "Läs mer om detta", + "minimize": "Minimera", + "openDevTools": "Öppna &utvecklarverktyg", + "openDevToolsOnAllWindows": "Öppna &utvecklarverktyg för alla fönster", + "paste": "&klistra in", + "quit": "Avsluta {{- appName}}", + "redo": "Gör om", + "reload": "Ladda om", + "reloadClearingCache": "Tvinga fram omladdning", + "reportIssue": "Rapportera problem", + "resetAppData": "Återställ appdata", + "resetZoom": "Återställ zoom", + "selectAll": "Välj alla", + "services": "Tjänster", + "showFullScreen": "Fullskärm", + "showMenuBar": "Menyfält", + "showOnUnreadMessage": "Visa vid olästa meddelanden", + "showServerList": "Serverlista", + "showTrayIcon": "Ikon för brickan", + "toggleDevTools": "Toggle &DevTools", + "openConfigFolder": "Öppna mappen &Konfiguration", + "videoCallDevTools": "Öppna videosamtal &DevTools", + "videoCallTools": "Verktyg för videosamtal", + "videoCallDevToolsAutoOpen": "Auto-öppna utvecklarverktyg", + "undo": "Ångra", + "unhide": "Visa alla", + "viewMenu": "&Visa", + "windowMenu": "&Fönster", + "zoomIn": "Zooma in", + "zoomOut": "Zooma ut" + }, + "loadingError": { + "title": "Servern kunde inte laddas", + "announcement": "Houston, vi har ett problem", + "reload": "Ladda om" + }, + "videoCall": { + "loading": { + "initial": "Laddar videosamtal...", + "reloading": "Laddar om videosamtal...", + "description": "Vänta medan vi ansluter till videosamtalet" + }, + "error": { + "title": "Videosamtalet kunde inte laddas", + "announcement": "Houston, vi har ett problem", + "timeout": "Tidsgräns överskriden - videosamtalet kunde inte laddas inom 15 sekunder", + "crashed": "Webview kraschade", + "maxRetriesReached": "Kunde inte ladda efter flera försök", + "reload": "Ladda om videosamtal" + } + }, + "unsupportedServer": { + "title": "{{instanceDomain}} kör en version av Rocket.Chat som inte stöds", + "announcement": "En administratör måste uppdatera arbetsytan till en version som stöds för att återaktivera åtkomst från mobil- och skrivbordsappar.", + "moreInformation": "Läs mer om detta" + }, + "selfxss": { + "title": "Sluta!", + "description": "Detta är en webbläsarfunktion avsedd för utvecklare. Om någon sa till dig att kopiera och klistra in något här för att aktivera en Rocket.Chat-funktion eller \"hacka\" någons konto, är det en bluff och kommer att ge dem tillgång till ditt Rocket.Chat-konto.", + "moreInfo": "Se https://go.rocket.chat/i/xss för mer information." + }, + "sidebar": { + "addNewServer": "Lägg till ny server", + "downloads": "Nedladdningar", + "settings": "Inställningar", + "menuTitle": "Anpassa och kontrollera appen", + "item": { + "reload": "Ladda om", + "remove": "Ta bort", + "openDevTools": "Öppna utvecklarverktyg", + "clearCache": "Rensa cacheminnet", + "clearStorageData": "Rensa lagringsdata", + "copyCurrentUrl": "Kopiera aktuell URL", + "reloadClearingCache": "Tvinga fram omladdning", + "serverInfo": "Serverinformation", + "supportedVersionsInfo": "Information om versioner som stöds" + }, + "tooltips": { + "unreadMessage": "{{- count}} olästa meddelanden", + "unreadMessages": "{{- count}} olästa meddelanden", + "userNotLoggedIn": "Inte inloggad", + "addWorkspace": "Lägg till arbetsyta ({{shortcut}}+N)", + "settingsMenu": "Anpassa och kontrollera appen" + } + }, + "touchBar": { + "formatting": "Formatering", + "selectServer": "Välj server" + }, + "tray": { + "menu": { + "show": "Visa", + "hide": "Göm", + "quit": "Avsluta" + }, + "tooltip": { + "noUnreadMessage": "{{- appName}}: inget oläst meddelande", + "unreadMention": "{{- appName}}: du har ett oläst meddelande/direktmeddelande", + "unreadMention_plural": "{{- appName}}: du har {{- count}} olästa omnämnanden/direkt meddelanden", + "unreadMessage": "{{- appName}}: du har olästa meddelanden" + }, + "balloon": { + "stillRunning": { + "title": "{{- appName}} är fortfarande igång", + "content": "{{- appName }} är inställd på att fortsätta köras i systemfältet/notifikationsområdet." + } + } + }, + "taskbar": { + "unreadMessage": "Olästa meddelanden", + "unreadMention": "Olästa omnämnanden", + "noUnreadMessage": "Inga olästa meddelanden" + }, + "screenSharing": { + "permissionDenied": "Tillstånd för skärminspelning har nekats", + "permissionRequired": "Tillstånd för skärminspelning krävs för att dela din skärm.", + "permissionInstructions": "Aktivera det i dina systeminställningar och försök igen.", + "title": "Dela din skärm", + "entireScreen": "Hela skärmen", + "applicationWindow": "Applikationsfönster", + "noScreensFound": "Inga skärmar hittades", + "noWindowsFound": "Inga fönster hittades", + "cancel": "Avbryt", + "share": "Dela" + }, + "serverInfo": { + "title": "Serverinformation", + "urlLabel": "URL:", + "versionLabel": "Version:", + "unknown": "Okänd", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Versioner som stöds", + "statusLabel": "Status:", + "status": { + "loading": "Laddar...", + "error": "Laddning misslyckades", + "loaded": "Laddad", + "idle": "Viloläge", + "from": "från {{source}}" + }, + "supported": { + "unknown": "Okänd", + "expiring": "Upphör snart", + "yes": "Ja", + "no": "Nej" + }, + "expiration": { + "label": "Utgångsdatum:", + "expiresOn": "Upphör {{date}}" + } + } +} diff --git a/src/i18n/tr-TR.i18n.json b/src/i18n/tr-TR.i18n.json index 035f76d7d8..bc12d59669 100644 --- a/src/i18n/tr-TR.i18n.json +++ b/src/i18n/tr-TR.i18n.json @@ -79,6 +79,28 @@ "title": "Güncellemeyi Atla", "message": "Bir sonraki güncellemenin ne zaman yapılacağını size bildireceğiz. Fikrinizi değiştirirseniz Hakkında menüsünden güncellemeleri manuel kontrol edebilirsiniz.", "ok": "TAMAM" + }, + "mediaPermission": { + "title": "Medya İzni Gerekli", + "message": "{{- permissionType}} erişimi şu anda sistem ayarlarınızda devre dışı.", + "detail": "Video arama özelliklerini etkinleştirmek için lütfen sistem gizlilik ayarlarınızda erişime izin verin ve ardından uygulamayı yeniden başlatın.", + "openSettings": "Ayarları Aç", + "cancel": "İptal", + "microphone": "Mikrofon", + "camera": "Kamera", + "both": "Mikrofon ve Kamera" + }, + "microphonePermissionDenied": { + "title": "Mikrofon Erişimi Gerekli", + "message": "{{- actionType}} için mikrofon erişimi gereklidir. Lütfen sistem ayarlarınızı kontrol edin.", + "detail": "Mikrofon erişimini sistem gizlilik ayarlarınızdan etkinleştirebilirsiniz.", + "openSettings": "Ayarları Aç", + "cancel": "İptal", + "actionTypes": { + "initiateCall": "aramalar yapmak", + "answerCall": "aramaları yanıtlamak", + "recordMessage": "mesaj kaydetmek" + } } }, "settings": { @@ -95,6 +117,17 @@ "systemDefault": "Sistem Varsayılanı", "loading": "Tarayıcılar yükleniyor...", "current": "Şu anda kullanılan:" + }, + "transparentWindow": { + "title": "Şeffaf pencere efekti", + "description": "Pencere için yerel titreşim/şeffaflık efektini etkinleştir. Uygulamak için yeniden başlatma gerektirir." + }, + "themeAppearance": { + "title": "Tema", + "description": "Uygulama için renk temasını seçin.", + "auto": "Sistemi takip et", + "light": "Açık", + "dark": "Koyu" } } }, @@ -119,6 +152,7 @@ "close": "Kapat", "copy": "&Kopyala", "cut": "Kes", + "developerMode": "Geliştirici Modu", "documentation": "Dökümantasyon", "editMenu": "&Düzenle", "fileMenu": "&Dosya", @@ -152,6 +186,21 @@ "announcement": "Houston, bir sorunumuz var", "reload": "Yeniden yükle" }, + "videoCall": { + "loading": { + "initial": "Video görüşme yükleniyor...", + "reloading": "Video görüşme yeniden yükleniyor...", + "description": "Video görüşmeye bağlanırken lütfen bekleyin" + }, + "error": { + "title": "Video görüşme yüklenemedi", + "announcement": "Houston, bir problemimiz var", + "timeout": "Zaman aşımı - video görüşme 15 saniye içinde yüklenemedi", + "crashed": "Webview çöktü", + "maxRetriesReached": "Birden fazla deneme sonrası yükleme başarısız", + "reload": "Video görüşmeyi yeniden yükle" + } + }, "selfxss": { "title": "Dur!", "description": "Bu, geliştiricilere yönelik bir tarayıcı özelliğidir. Birisi size bir Rocket.Chat özelliğini etkinleştirmeniz veya herhangi bir şeyi kopyalayıp yapıştırmanızı söylediyse, bu bir aldatmacadır ve onlara Rocket.Chat hesabınıza erişme izni verir.", @@ -198,5 +247,31 @@ "noWindowsFound": "Pencere bulunamadı", "cancel": "İptal et", "share": "Paylaş" + }, + "serverInfo": { + "title": "Sunucu Bilgileri", + "urlLabel": "URL:", + "versionLabel": "Sürüm:", + "unknown": "Bilinmiyor", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "Desteklenen Sürümler", + "statusLabel": "Durum:", + "status": { + "loading": "Yükleniyor...", + "error": "Yükleme başarısız", + "loaded": "Yüklendi", + "idle": "Boşta", + "from": "{{source}} kaynağından" + }, + "supported": { + "unknown": "Bilinmiyor", + "expiring": "Süresi doluyor", + "yes": "Evet", + "no": "Hayır" + }, + "expiration": { + "label": "Son Kullanma:", + "expiresOn": "{{date}} tarihinde sona eriyor" + } } -} \ No newline at end of file +} diff --git a/src/i18n/uk-UA.i18n.json b/src/i18n/uk-UA.i18n.json index d3a307c084..5035048cb1 100644 --- a/src/i18n/uk-UA.i18n.json +++ b/src/i18n/uk-UA.i18n.json @@ -99,6 +99,17 @@ "systemDefault": "Системний за замовчуванням", "loading": "Завантаження браузерів...", "current": "Зараз використовується:" + }, + "transparentWindow": { + "title": "Ефект прозорого вікна", + "description": "Увімкнути нативний ефект вібрації/прозорості для вікна. Потрібен перезапуск для застосування." + }, + "themeAppearance": { + "title": "Тема", + "description": "Виберіть колірну тему для програми.", + "auto": "Слідувати системі", + "light": "Світла", + "dark": "Темна" } } }, @@ -122,6 +133,7 @@ "close": "Закрити", "copy": "Ко&піювати", "cut": "В&ирізати", + "developerMode": "Режим розробника", "documentation": "Документація", "editMenu": "&Редагувати", "fileMenu": "&Файл", @@ -159,6 +171,21 @@ "announcement": "Г'юстон, у нас проблеми", "reload": "Перезавантажити" }, + "videoCall": { + "loading": { + "initial": "Завантаження відеодзвінка...", + "reloading": "Перезавантаження відеодзвінка...", + "description": "Будь ласка, зачекайте, поки ми підключаємося до відеодзвінка" + }, + "error": { + "title": "Не вдалося завантажити відеодзвінок", + "announcement": "Х'юстон, у нас проблема", + "timeout": "Перевищено час очікування - відеодзвінок не вдалося завантажити за 15 секунд", + "crashed": "Webview зависла", + "maxRetriesReached": "Не вдалося завантажити після кількох спроб", + "reload": "Перезавантажити відеодзвінок" + } + }, "selfxss": { "title": "Стій!", "description": "Ця функція браузера призначена для розробників. Якщо хтось сказав вам, скопіюйте щось тут, щоб увімкнути функцію Rocket.Chat чи \"зламати\" чужий обліковий запис — це шахрайство і Ви надасте доступ до вашого облікового запису Rocket.Chat.", @@ -211,5 +238,31 @@ "noWindowsFound": "Вікна не знайдено", "cancel": "Скасувати", "share": "Демонструвати" + }, + "serverInfo": { + "title": "Інформація про сервер", + "urlLabel": "URL:", + "versionLabel": "Версія:", + "unknown": "Невідомо", + "exchangeUrlLabel": "URL Outlook Exchange:", + "supportedVersionsTitle": "Підтримувані версії", + "statusLabel": "Статус:", + "status": { + "loading": "Завантаження...", + "error": "Помилка завантаження", + "loaded": "Завантажено", + "idle": "Неактивно", + "from": "з {{source}}" + }, + "supported": { + "unknown": "Невідомо", + "expiring": "Термін дії закінчується", + "yes": "Так", + "no": "Ні" + }, + "expiration": { + "label": "Термін дії:", + "expiresOn": "Закінчується {{date}}" + } } -} \ No newline at end of file +} diff --git a/src/i18n/zh-CN.i18n.json b/src/i18n/zh-CN.i18n.json index 0c4e380659..bd504c8ef1 100644 --- a/src/i18n/zh-CN.i18n.json +++ b/src/i18n/zh-CN.i18n.json @@ -80,6 +80,28 @@ "title": "忽略更新", "message": "我们将会在下次有新的更新版本的时候通知您\n如果您改变主意想安装此处更新,您可以从「关于」的选项中检查更新。", "ok": "好" + }, + "mediaPermission": { + "title": "需要媒体权限", + "message": "{{- permissionType}}访问目前在您的系统设置中已被禁用。", + "detail": "要启用视频通话功能,请在您的系统隐私设置中允许访问,然后重新启动应用程序。", + "openSettings": "打开设置", + "cancel": "取消", + "microphone": "麦克风", + "camera": "摄像头", + "both": "麦克风和摄像头" + }, + "microphonePermissionDenied": { + "title": "需要麦克风访问权限", + "message": "需要麦克风访问权限才能{{- actionType}}。请检查您的系统设置。", + "detail": "您可以在系统的隐私设置中启用麦克风访问权限。", + "openSettings": "打开设置", + "cancel": "取消", + "actionTypes": { + "initiateCall": "拨打电话", + "answerCall": "接听电话", + "recordMessage": "录制消息" + } } }, "downloads": { @@ -132,6 +154,17 @@ "title": "在程序窗口中打开视频通话", "description": "如果启用,视频通话将在应用程序窗口中打开,而不是在默认浏览器中打开。但是,对于Google Meet和Jitsi,Electron应用程序不支持屏幕录制,所以无论此设置如何,它们始终会在浏览器中打开。", "masDescription": "从Mac App Store安装时此选项被禁用。出于安全原因,视频通话将始终默认在浏览器中打开。" + }, + "transparentWindow": { + "title": "透明窗口效果", + "description": "启用窗口的原生模糊/透明效果。需要重启才能应用。" + }, + "themeAppearance": { + "title": "主题", + "description": "选择应用程序的颜色主题。", + "auto": "跟随系统", + "light": "浅色", + "dark": "深色" } } }, @@ -156,7 +189,8 @@ "close": "关闭", "copy": "复制 (&C)", "cut": "剪切 (&T)", - "disableGpu": "禁用GPU", + "developerMode": "开发者模式", + "disableGpu": "禁用 GPU", "documentation": "文件", "downloads": "下载", "settings": "设置", @@ -192,6 +226,21 @@ "announcement": "稍等一下,我们遇到问题了", "reload": "重新載入" }, + "videoCall": { + "loading": { + "initial": "正在加载视频通话...", + "reloading": "正在重新加载视频通话...", + "description": "请稍候,我们正在连接到视频通话" + }, + "error": { + "title": "视频通话加载失败", + "announcement": "休斯顿,我们遇到了问题", + "timeout": "加载超时 - 视频通话无法在15秒内加载", + "crashed": "网页视图崩溃了", + "maxRetriesReached": "多次尝试后加载失败", + "reload": "重新加载视频通话" + } + }, "sidebar": { "addNewServer": "新增服务器", "downloads": "下载", @@ -237,5 +286,31 @@ "noWindowsFound": "未找到窗口", "cancel": "取消", "share": "共享" + }, + "serverInfo": { + "title": "服务器信息", + "urlLabel": "URL:", + "versionLabel": "版本:", + "unknown": "未知", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "支持的版本", + "statusLabel": "状态:", + "status": { + "loading": "加载中...", + "error": "加载失败", + "loaded": "已加载", + "idle": "空闲", + "from": "来自 {{source}}" + }, + "supported": { + "unknown": "未知", + "expiring": "即将过期", + "yes": "是", + "no": "否" + }, + "expiration": { + "label": "过期时间:", + "expiresOn": "将于 {{date}} 过期" + } } -} \ No newline at end of file +} diff --git a/src/i18n/zh-TW.i18n.json b/src/i18n/zh-TW.i18n.json index c7b383cc0f..eeb5001bf9 100644 --- a/src/i18n/zh-TW.i18n.json +++ b/src/i18n/zh-TW.i18n.json @@ -96,6 +96,17 @@ "systemDefault": "系統預設", "loading": "正在載入瀏覽器...", "current": "目前使用:" + }, + "transparentWindow": { + "title": "透明視窗效果", + "description": "啟用視窗的原生模糊/透明效果。需要重新啟動才能套用。" + }, + "themeAppearance": { + "title": "主題", + "description": "選擇應用程式的顏色主題。", + "auto": "跟隨系統", + "light": "淺色", + "dark": "深色" } } }, @@ -120,6 +131,7 @@ "close": "關閉", "copy": "複製 (&C)", "cut": "剪下 (&T)", + "developerMode": "開發者模式", "documentation": "文件", "editMenu": "編輯 (&E)", "fileMenu": "檔案 (&F)", @@ -194,5 +206,31 @@ "noWindowsFound": "未找到視窗", "cancel": "取消", "share": "共享" + }, + "serverInfo": { + "title": "伺服器資訊", + "urlLabel": "URL:", + "versionLabel": "版本:", + "unknown": "未知", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "支援的版本", + "statusLabel": "狀態:", + "status": { + "loading": "載入中...", + "error": "載入失敗", + "loaded": "已載入", + "idle": "閒置", + "from": "來自 {{source}}" + }, + "supported": { + "unknown": "未知", + "expiring": "即將過期", + "yes": "是", + "no": "否" + }, + "expiration": { + "label": "過期時間:", + "expiresOn": "將於 {{date}} 過期" + } } -} \ No newline at end of file +} diff --git a/src/i18n/zh.i18n.json b/src/i18n/zh.i18n.json index 6f31cf5a2e..85cb500525 100644 --- a/src/i18n/zh.i18n.json +++ b/src/i18n/zh.i18n.json @@ -1 +1,43 @@ -{ } \ No newline at end of file +{ + "settings": { + "options": { + "transparentWindow": { + "title": "透明窗口效果", + "description": "启用窗口的原生模糊/透明效果。需要重启才能应用。" + }, + "themeAppearance": { + "title": "主题", + "description": "选择应用程序的颜色主题。", + "auto": "跟随系统", + "light": "浅色", + "dark": "深色" + } + } + }, + "serverInfo": { + "title": "服务器信息", + "urlLabel": "URL:", + "versionLabel": "版本:", + "unknown": "未知", + "exchangeUrlLabel": "Outlook Exchange URL:", + "supportedVersionsTitle": "支持的版本", + "statusLabel": "状态:", + "status": { + "loading": "加载中...", + "error": "加载失败", + "loaded": "已加载", + "idle": "空闲", + "from": "来自 {{source}}" + }, + "supported": { + "unknown": "未知", + "expiring": "即将过期", + "yes": "是", + "no": "否" + }, + "expiration": { + "label": "过期时间:", + "expiresOn": "将于 {{date}} 过期" + } + } +} diff --git a/src/injected.ts b/src/injected.ts index cbc88a8ed1..2bc3c80b5e 100644 --- a/src/injected.ts +++ b/src/injected.ts @@ -1,14 +1,5 @@ import type { NotificationAction } from 'electron'; -import type { RocketChatDesktopAPI } from './servers/preload/api'; - -declare global { - // eslint-disable-next-line @typescript-eslint/naming-convention - interface Window { - RocketChatDesktop: RocketChatDesktopAPI; - } -} - console.log('[Rocket.Chat Desktop] Injected.ts'); const resolveWithExponentialBackoff = ( @@ -37,15 +28,50 @@ const resolveWithExponentialBackoff = ( const tryRequire = (path: string) => resolveWithExponentialBackoff(() => window.require(path)); +let startRetryCount = 0; +let totalRetryTime = 0; +const MAX_RETRY_TIME = 30000; // Maximum 30 seconds total retry time +const INITIAL_RETRY_DELAY = 1000; // Start with 1 second + +// eslint-disable-next-line complexity const start = async () => { console.log('[Rocket.Chat Desktop] Injected.ts start fired'); if (typeof window.require !== 'function') { console.log('[Rocket.Chat Desktop] window.require is not defined'); - console.log('[Rocket.Chat Desktop] Inject start - retrying in 1 seconds'); - setTimeout(start, 1000); + + if (totalRetryTime >= MAX_RETRY_TIME) { + console.error( + `[Rocket.Chat Desktop] Maximum retry time (${MAX_RETRY_TIME}ms) reached. window.require is still not available.` + ); + console.log( + '[Rocket.Chat Desktop] Triggering force reload with cache clear to recover...' + ); + // Trigger force reload with cache clear to recover + window.RocketChatDesktop.reloadServer(); + return; + } + + startRetryCount++; + const retryDelay = Math.min( + INITIAL_RETRY_DELAY * Math.pow(1.5, startRetryCount - 1), + 5000 + ); // Cap at 5 seconds per retry + + // Ensure we don't exceed max total time + const actualDelay = Math.min(retryDelay, MAX_RETRY_TIME - totalRetryTime); + totalRetryTime += actualDelay; + + console.log( + `[Rocket.Chat Desktop] Inject start - retry ${startRetryCount} in ${actualDelay}ms (total time: ${totalRetryTime}ms)` + ); + setTimeout(start, actualDelay); return; } + // Reset retry counters on successful require detection + startRetryCount = 0; + totalRetryTime = 0; + const { Info: serverInfo = {} } = await tryRequire( '/app/utils/rocketchat.info' ); @@ -63,10 +89,16 @@ const start = async () => { version1: string, version2: string ): boolean { - const v1 = version1.match(/\d+/g)?.map(Number) || []; - const v2 = version2.match(/\d+/g)?.map(Number) || []; + // Extract only the core version number (before any suffix like -develop, -rc, etc.) + const cleanVersion1 = version1.split('-')[0]; + const cleanVersion2 = version2.split('-')[0]; + + const v1 = cleanVersion1.split('.').map(Number); + const v2 = cleanVersion2.split('.').map(Number); - for (let i = 0; i < 3; i++) { + // Compare each version part + const maxLength = Math.max(v1.length, v2.length); + for (let i = 0; i < maxLength; i++) { const n1 = v1[i] || 0; const n2 = v2[i] || 0; @@ -78,7 +110,7 @@ const start = async () => { } } - return true; + return true; // Equal versions } const userPresenceModulePath = versionIsGreaterOrEqualsTo( @@ -104,144 +136,92 @@ const start = async () => { return '/app/utils'; })(); - const { Meteor } = await tryRequire('meteor/meteor'); - const { Session } = await tryRequire('meteor/session'); - const { Tracker } = await tryRequire('meteor/tracker'); - const { UserPresence } = await tryRequire(userPresenceModulePath); - const { settings } = await tryRequire(settingsModulePath); - const { getUserPreference } = await tryRequire(utilsModulePath); - - window.RocketChatDesktop.setUrlResolver(Meteor.absoluteUrl); - - navigator.clipboard.writeText = async (...args) => - window.RocketChatDesktop.writeTextToClipboard(...args); - - Tracker.autorun(() => { - const unread = Session.get('unread'); - window.RocketChatDesktop.setBadge(unread); - }); - - Tracker.autorun(() => { - const { url, defaultUrl } = settings.get('Assets_favicon') || {}; - window.RocketChatDesktop.setFavicon(url || defaultUrl); - }); - - const open = window.open.bind(window); - - Tracker.autorun(() => { - const serverMainVersion = serverInfo.version.split('.')[0]; - - // Server version above 5.0.0 will change the way the jitsi integration is handled, now we have video provider as an app - // if the server is above 5.1.1 it will use window.RocketChatDesktop?.openInternalVideoChatWindow to open the video call - if (serverMainVersion < 5) { - const jitsiDomain = settings.get('Jitsi_Domain') || ''; - + // Load core modules with individual error handling (non-blocking) + let Meteor: any = null; + let Session: any = null; + let Tracker: any = null; + let settings: any = null; + let getUserPreference: any = null; + let UserPresence: any = null; + + // Load modules asynchronously without blocking + const loadModule = async ( + modulePath: string, + moduleName: string, + setter: (value: any) => void, + propertyName?: string + ) => { + try { + const module = await tryRequire(modulePath); + const value = propertyName ? module[propertyName] : module; + setter(value); console.log( - '[Rocket.Chat Desktop] window.open for Jitsi overloaded', - jitsiDomain + `[Rocket.Chat Desktop] ${moduleName} module loaded successfully` + ); + } catch (error) { + console.log( + `[Rocket.Chat Desktop] Failed to load ${moduleName} module:`, + error ); - window.open = (url, name, features = '') => { - if ( - !process.mas && - window.RocketChatDesktop.getInternalVideoChatWindowEnabled() && - typeof url === 'string' && - jitsiDomain.length > 0 && - url.includes(jitsiDomain) - ) { - console.log('[Rocket.Chat Desktop] window.open for Jitsi fired'); - return open(url, 'Video Call', `scrollbars=true,${features}`); - } - - return open(url, name, features); - }; } - }); - - if (!versionIsGreaterOrEqualsTo(serverInfo.version, '6.4.0')) { - Tracker.autorun(() => { - const { url, defaultUrl } = settings.get('Assets_background') || {}; - window.RocketChatDesktop.setBackground(url || defaultUrl); - }); - } - - Tracker.autorun(() => { - const userToken = Meteor._localStorage.getItem('Meteor.loginToken'); - const userId = Meteor.userId(); - const outlookCalendarEnabled = settings.get('Outlook_Calendar_Enabled'); - const outlookExchangeUrl = settings.get('Outlook_Calendar_Exchange_Url'); - if (!userToken || !userId || !outlookCalendarEnabled || !outlookExchangeUrl) - return; - window.RocketChatDesktop.setUserToken(userToken, userId); + }; - window.RocketChatDesktop.setOutlookExchangeUrl(outlookExchangeUrl, userId); + // Start loading all modules in parallel (non-blocking) + loadModule('meteor/meteor', 'Meteor', (value) => { + Meteor = value.Meteor; }); - - Tracker.autorun(() => { - const userToken = Meteor._localStorage.getItem('Meteor.loginToken'); - const userId = Meteor.userId(); - const outlookCalendarEnabled = settings.get('Outlook_Calendar_Enabled'); - const outlookExchangeUrl = settings.get('Outlook_Calendar_Exchange_Url'); - if (!userToken || !userId || !outlookCalendarEnabled || !outlookExchangeUrl) - return; - window.RocketChatDesktop.setUserToken(userToken, userId); - - window.RocketChatDesktop.setOutlookExchangeUrl(outlookExchangeUrl, userId); + loadModule('meteor/session', 'Session', (value) => { + Session = value.Session; }); - - Tracker.autorun(() => { - const siteName = settings.get('Site_Name'); - window.RocketChatDesktop.setTitle(siteName); + loadModule('meteor/tracker', 'Tracker', (value) => { + Tracker = value.Tracker; }); - - Tracker.autorun(() => { - const userId = Meteor.userId(); - window.RocketChatDesktop.setUserLoggedIn(userId !== null); + loadModule(settingsModulePath, 'Settings', (value) => { + settings = value.settings; }); - - Tracker.autorun(() => { - const { gitCommitHash } = Meteor; - if (!gitCommitHash) return; - window.RocketChatDesktop.setGitCommitHash(gitCommitHash); + loadModule(utilsModulePath, 'Utils', (value) => { + getUserPreference = value.getUserPreference; }); - - Tracker.autorun(() => { - const uid = Meteor.userId(); - if (!uid) return; - const themeAppearance: string = getUserPreference(uid, 'themeAppearence'); - if ( - ['dark', 'light', 'auto', 'high-contrast'].includes( - themeAppearance as any - ) - ) { - window.RocketChatDesktop.setUserThemeAppearance( - themeAppearance as 'auto' | 'dark' | 'light' | 'high-contrast' - ); - } + loadModule(userPresenceModulePath, 'UserPresence', (value) => { + UserPresence = value.UserPresence; }); - Tracker.autorun(() => { - const uid = Meteor.userId(); - if (!uid) return; - const isAutoAwayEnabled: unknown = getUserPreference(uid, 'enableAutoAway'); - const idleThreshold: unknown = getUserPreference(uid, 'idleTimeLimit'); + // Initialize non-module dependent features immediately + navigator.clipboard.writeText = async (...args) => + window.RocketChatDesktop.writeTextToClipboard(...args); - if (isAutoAwayEnabled) { - delete UserPresence.awayTime; - UserPresence.start(); - } + // Handle clicks on links to open them externally + document.addEventListener( + 'click', + (event) => { + const target = event.target as HTMLElement | null; + const link = target?.closest('a') as HTMLAnchorElement | null; + + if (!link?.href) { + return; + } - window.RocketChatDesktop.setUserPresenceDetection({ - isAutoAwayEnabled: Boolean(isAutoAwayEnabled), - idleThreshold: idleThreshold ? Number(idleThreshold) : null, - setUserOnline: (online) => { - if (!online) { - Meteor.call('UserPresence:away'); + try { + const parsedUrl = new URL(link.href); + + if (parsedUrl.origin === window.location.origin) { return; } - Meteor.call('UserPresence:online'); - }, - }); - }); + + if ( + parsedUrl.protocol === 'http:' || + parsedUrl.protocol === 'https:' || + parsedUrl.protocol === 'afp:' + ) { + event.preventDefault(); + void window.RocketChatDesktop?.openExternal(parsedUrl.toString()); + } + } catch { + // Invalid URL, ignore + } + }, + true + ); console.log('[Rocket.Chat Desktop] Injected.ts replaced Notification'); @@ -432,6 +412,206 @@ const start = async () => { } }; + // Track which features have been setup to avoid duplicates + const setupFlags = { + urlResolver: false, + badgeUpdates: false, + faviconUpdates: false, + jitsiIntegration: false, + backgroundSettings: false, + outlookIntegration: false, + titleUpdates: false, + userLoginDetection: false, + gitCommitHash: false, + themeAppearance: false, + userPresence: false, + }; + + // Setup reactive features that depend on modules (with polling) + // eslint-disable-next-line complexity + const setupReactiveFeatures = () => { + if (Meteor?.absoluteUrl && !setupFlags.urlResolver) { + window.RocketChatDesktop.setUrlResolver(Meteor.absoluteUrl); + setupFlags.urlResolver = true; + } + + if (Tracker && Session && !setupFlags.badgeUpdates) { + Tracker.autorun(() => { + const unread = Session.get('unread'); + window.RocketChatDesktop.setBadge(unread); + }); + setupFlags.badgeUpdates = true; + } + + if (Tracker && settings && !setupFlags.faviconUpdates) { + Tracker.autorun(() => { + const { url, defaultUrl } = settings.get('Assets_favicon') || {}; + window.RocketChatDesktop.setFavicon(url || defaultUrl); + }); + setupFlags.faviconUpdates = true; + } + + if (Tracker && settings && !setupFlags.jitsiIntegration) { + const open = window.open.bind(window); + + Tracker.autorun(() => { + const serverMainVersion = serverInfo.version.split('.')[0]; + + // Server version above 5.0.0 will change the way the jitsi integration is handled, now we have video provider as an app + // if the server is above 5.1.1 it will use window.RocketChatDesktop?.openInternalVideoChatWindow to open the video call + if (serverMainVersion < 5) { + const jitsiDomain = settings.get('Jitsi_Domain') || ''; + + console.log( + '[Rocket.Chat Desktop] window.open for Jitsi overloaded', + jitsiDomain + ); + window.open = (url, name, features = '') => { + if ( + !process.mas && + window.RocketChatDesktop.getInternalVideoChatWindowEnabled() && + typeof url === 'string' && + jitsiDomain.length > 0 && + url.includes(jitsiDomain) + ) { + console.log('[Rocket.Chat Desktop] window.open for Jitsi fired'); + return open(url, 'Video Call', `scrollbars=true,${features}`); + } + + return open(url, name, features); + }; + } + }); + setupFlags.jitsiIntegration = true; + } + + if ( + !versionIsGreaterOrEqualsTo(serverInfo.version, '6.4.0') && + Tracker && + settings && + !setupFlags.backgroundSettings + ) { + Tracker.autorun(() => { + const { url, defaultUrl } = settings.get('Assets_background') || {}; + window.RocketChatDesktop.setBackground(url || defaultUrl); + }); + setupFlags.backgroundSettings = true; + } + + // Helper function to get Outlook settings based on server version + const getOutlookSettings = () => { + if (!Meteor || !settings) return {}; + const userToken = Meteor._localStorage?.getItem('Meteor.loginToken'); + + if (!versionIsGreaterOrEqualsTo(serverInfo.version, '7.8.0')) { + // Pre-7.8.0: Use global server settings + return { + userToken, + userId: Meteor.userId?.(), + outlookCalendarEnabled: settings.get('Outlook_Calendar_Enabled'), + outlookExchangeUrl: settings.get('Outlook_Calendar_Exchange_Url'), + }; + } + // 7.8.0+: Use user-specific settings + const user = Meteor.user?.(); + const outlookSettings = user?.settings?.calendar?.outlook; + return { + userToken, + userId: user?._id, + outlookCalendarEnabled: outlookSettings?.Enabled, + outlookExchangeUrl: outlookSettings?.Exchange_Url, + }; + }; + + if (Tracker && !setupFlags.outlookIntegration) { + Tracker.autorun(() => { + const { + userToken, + userId, + outlookCalendarEnabled, + outlookExchangeUrl, + } = getOutlookSettings(); + + if ( + !userToken || + !userId || + !outlookCalendarEnabled || + !outlookExchangeUrl + ) { + return; + } + + window.RocketChatDesktop.setUserToken(userToken, userId); + window.RocketChatDesktop.setOutlookExchangeUrl( + outlookExchangeUrl, + userId + ); + }); + setupFlags.outlookIntegration = true; + } + + if (Tracker && settings && !setupFlags.titleUpdates) { + Tracker.autorun(() => { + const siteName = settings.get('Site_Name'); + window.RocketChatDesktop.setTitle(siteName); + }); + setupFlags.titleUpdates = true; + } + + if (Tracker && Meteor && !setupFlags.userLoginDetection) { + Tracker.autorun(() => { + const userId = Meteor.userId(); + window.RocketChatDesktop.setUserLoggedIn(userId !== null); + }); + setupFlags.userLoginDetection = true; + } + + if (Tracker && Meteor && !setupFlags.gitCommitHash) { + Tracker.autorun(() => { + const { gitCommitHash } = Meteor; + if (!gitCommitHash) return; + window.RocketChatDesktop.setGitCommitHash(gitCommitHash); + }); + setupFlags.gitCommitHash = true; + } + + if (Tracker && Meteor && getUserPreference && !setupFlags.userPresence) { + Tracker.autorun(() => { + const uid = Meteor.userId(); + if (!uid) return; + const isAutoAwayEnabled: unknown = getUserPreference( + uid, + 'enableAutoAway' + ); + const idleThreshold: unknown = getUserPreference(uid, 'idleTimeLimit'); + + if (isAutoAwayEnabled && UserPresence) { + delete UserPresence.awayTime; + UserPresence.start(); + } + + window.RocketChatDesktop.setUserPresenceDetection({ + isAutoAwayEnabled: Boolean(isAutoAwayEnabled), + idleThreshold: idleThreshold ? Number(idleThreshold) : null, + setUserOnline: (online) => { + if (!online && Meteor.call) { + Meteor.call('UserPresence:away'); + return; + } + if (Meteor.call) { + Meteor.call('UserPresence:online'); + } + }, + }); + }); + setupFlags.userPresence = true; + } + }; + + // Call setupReactiveFeatures immediately and then periodically check for new modules + setupReactiveFeatures(); + setInterval(setupReactiveFeatures, 1000); // Check every second for newly loaded modules + console.log('[Rocket.Chat Desktop] Injected'); }; diff --git a/src/ipc/channels.ts b/src/ipc/channels.ts index 98e1c5c356..6809359e26 100644 --- a/src/ipc/channels.ts +++ b/src/ipc/channels.ts @@ -24,12 +24,45 @@ type ChannelToArgsMap = { 'server-view/get-url': () => Server['url'] | undefined; 'server-view/ready': () => void; 'server-view/open-url-on-browser': (url: string) => void; - 'video-call-window/open-window': (url: string) => void; + 'video-call-window/open-window': ( + url: string, + options?: { + providerName?: string; + credentials?: { userId: string; authToken: string }; + } + ) => void; 'video-call-window/open-url': (url: string) => void; 'video-call-window/web-contents-id': (webContentsId: number) => void; - 'video-call-window/open-screen-picker': () => void; + 'video-call-window/open-screen-picker': () => { success: boolean }; 'video-call-window/screen-sharing-source-responded': (source: string) => void; 'video-call-window/screen-recording-is-permission-granted': () => boolean; + 'video-call-window/close-requested': () => { success: boolean }; + 'video-call-window/open-webview-dev-tools': () => boolean; + 'video-call-window/test-ipc': () => { success: boolean; timestamp: number }; + 'video-call-window/handshake': () => { success: boolean; timestamp: number }; + 'video-call-window/renderer-ready': () => { success: boolean }; + 'video-call-window/request-url': () => { + success: boolean; + url: string | null; + autoOpenDevtools: boolean; + }; + 'video-call-window/url-received': () => { success: boolean }; + 'video-call-window/webview-created': () => { success: boolean }; + 'video-call-window/webview-loading': () => { success: boolean }; + 'video-call-window/webview-ready': () => { success: boolean }; + 'video-call-window/webview-failed': (error: string) => { success: boolean }; + 'video-call-window/get-credentials': () => { + userId: string; + authToken: string; + serverUrl: string; + } | null; + 'video-call-window/get-language': () => { + success: boolean; + language: string; + }; + 'video-call-window/prewarm-capturer-cache': () => { + success: boolean; + }; 'jitsi-desktop-capturer-get-sources': ( args: [options: Electron.SourcesOptions, jitsiDomain: string] ) => Electron.DesktopCapturerSource[]; @@ -38,7 +71,7 @@ type ChannelToArgsMap = { ) => Electron.DesktopCapturerSource[]; 'outlook-calendar/get-events': (date: Date) => OutlookEventsResponse; 'outlook-calendar/set-exchange-url': (url: string, userId: string) => void; - 'outlook-calendar/has-credentials': () => Promise; + 'outlook-calendar/has-credentials': () => boolean; 'outlook-calendar/clear-credentials': () => void; 'outlook-calendar/set-user-token': (token: string, userId: string) => void; 'document-viewer/open-window': ( @@ -46,6 +79,59 @@ type ChannelToArgsMap = { format: string, options: any ) => void; + 'log-viewer-window/open-window': () => void; + 'log-viewer-window/close-requested': () => void; + 'log-viewer-window/select-log-file': () => { + success: boolean; + filePath?: string; + fileName?: string; + canceled?: boolean; + error?: string; + }; + 'log-viewer-window/read-logs': (options?: { + filePath?: string; + limit?: number | 'all'; + }) => { + success: boolean; + logs?: string; + filePath?: string; + fileName?: string; + isDefaultLog?: boolean; + lastModifiedTime?: number; + error?: string; + }; + 'log-viewer-window/stat-log': (options?: { filePath?: string }) => { + success: boolean; + lastModifiedTime?: number; + size?: number; + error?: string; + }; + 'log-viewer-window/read-logs-tail': (options: { + fromByte: number; + filePath?: string; + }) => { + success: boolean; + logs?: string; + newSize?: number; + lastModifiedTime?: number; + error?: string; + }; + 'log-viewer-window/confirm-clear-logs': () => boolean; + 'log-viewer-window/clear-logs': () => { success: boolean; error?: string }; + 'log-viewer-window/save-logs': (options: { + content: string; + defaultFileName: string; + }) => { + success: boolean; + filePath?: string; + canceled?: boolean; + error?: string; + }; + 'log-viewer-window/get-server-mapping': () => { + success: boolean; + mapping: Record; + }; + 'open-external': (url: string) => void; }; export type Channel = keyof ChannelToArgsMap; diff --git a/src/ipc/renderer.ts b/src/ipc/renderer.ts index 4289bcd463..7b53ef2ac7 100644 --- a/src/ipc/renderer.ts +++ b/src/ipc/renderer.ts @@ -39,3 +39,77 @@ export const invoke = ( channel: N, ...args: Parameters> ): Promise>> => ipcRenderer.invoke(channel, ...args); + +export interface IRetryOptions { + /** Maximum number of retry attempts (default: 3) */ + maxAttempts?: number; + /** Delay between retries in milliseconds (default: 1000) */ + retryDelay?: number; + /** Whether to log retry attempts (default: true) */ + logRetries?: boolean; + /** Custom retry condition - return true to retry, false to give up */ + shouldRetry?: (error: any, attempt: number) => boolean; +} + +export const invokeWithRetry = ( + channel: N, + retryOptions: IRetryOptions = {}, + ...args: Parameters> +): Promise>> => { + const { + maxAttempts = 3, + retryDelay = 1000, + logRetries = true, + shouldRetry = () => true, + } = retryOptions; + + const attemptInvoke = async ( + attempt: number + ): Promise>> => { + try { + const result = await ipcRenderer.invoke(channel, ...args); + + // Check if result indicates failure (for channels that return success flags) + if ( + result && + typeof result === 'object' && + 'success' in result && + result.success === false + ) { + throw new Error(`IPC call failed: ${channel} returned success: false`); + } + + return result; + } catch (error) { + const isLastAttempt = attempt >= maxAttempts; + + if (logRetries) { + console.log( + `IPC call failed: ${channel} (attempt ${attempt}/${maxAttempts})`, + error + ); + } + + if (isLastAttempt || !shouldRetry(error, attempt)) { + if (logRetries) { + console.error( + `IPC call giving up: ${channel} after ${attempt} attempts`, + error + ); + } + throw error; + } + + if (logRetries) { + console.log( + `IPC call retrying: ${channel} in ${retryDelay}ms... (attempt ${attempt + 1}/${maxAttempts})` + ); + } + + await new Promise((resolve) => setTimeout(resolve, retryDelay)); + return attemptInvoke(attempt + 1); + } + }; + + return attemptInvoke(1); +}; diff --git a/src/jitsi/ipc.ts b/src/jitsi/ipc.ts index b1fde26986..f1cbc7e792 100644 --- a/src/jitsi/ipc.ts +++ b/src/jitsi/ipc.ts @@ -10,7 +10,7 @@ let firstAskPermission = true; export const handleJitsiDesktopCapturerGetSources = () => { handle( 'jitsi-desktop-capturer-get-sources', - async (_event, [opts, jitsiDomain]) => { + async (_webContents, [opts, jitsiDomain]) => { if (permitted) return desktopCapturer.getSources(opts); if (dontAskAgain) return []; diff --git a/src/logViewerWindow/LogEntry.tsx b/src/logViewerWindow/LogEntry.tsx new file mode 100644 index 0000000000..6d59cdf228 --- /dev/null +++ b/src/logViewerWindow/LogEntry.tsx @@ -0,0 +1,138 @@ +import { Box, Badge, Chip } from '@rocket.chat/fuselage'; +import { useMemo } from 'react'; + +import { type LogLevel, type LogEntryType } from './types'; + +const SERVER_TAG_REGEX = /\bserver-\d+\b/; + +const getLevelColor = ( + level: LogLevel +): 'danger' | 'warning' | 'primary' | 'secondary' | 'ghost' => { + switch (level) { + case 'error': + return 'danger'; + case 'warn': + return 'warning'; + case 'info': + return 'primary'; + case 'debug': + return 'secondary'; + case 'verbose': + return 'ghost'; + default: + return 'ghost'; + } +}; + +const getLevelTextColor = (level: LogLevel): string => { + switch (level) { + case 'error': + return 'danger'; + case 'warn': + return 'default'; + case 'info': + return 'info'; + case 'debug': + return 'default'; + case 'verbose': + return 'default'; + default: + return 'default'; + } +}; + +const getLevelBorderColor = (level: LogLevel): string => { + switch (level) { + case 'error': + return 'var(--rcx-color-stroke-highlight)'; + case 'warn': + return 'var(--rcx-color-stroke-highlight)'; + case 'info': + return 'var(--rcx-color-stroke-highlight)'; + case 'debug': + return 'var(--rcx-color-stroke-light)'; + case 'verbose': + return 'var(--rcx-color-stroke-light)'; + default: + return 'var(--rcx-color-stroke-light)'; + } +}; + +export const LogEntry = ({ + entry, + showContext, + showServer, + serverMapping, +}: { + entry: LogEntryType; + showContext: boolean; + showServer: boolean; + serverMapping: Record; +}) => { + const { serverTag, serverDisplayName, contextWithoutServer } = useMemo(() => { + const match = entry.context.match(SERVER_TAG_REGEX); + const tag = match?.[0] || ''; + const displayName = tag ? serverMapping[tag] || tag : ''; + const ctxWithout = tag + ? entry.context + .replace(SERVER_TAG_REGEX, '') + .replace(/\s{2,}/g, ' ') + .trim() + : entry.context; + return { + serverTag: tag, + serverDisplayName: displayName, + contextWithoutServer: ctxWithout, + }; + }, [entry.context, serverMapping]); + + return ( + + + {entry.timestamp} + + + + {entry.level.toUpperCase()} + + + {showServer && serverTag && ( + + {serverDisplayName} + + )} + {showContext && contextWithoutServer && ( + + {contextWithoutServer} + + )} + + {entry.message} + + + ); +}; diff --git a/src/logViewerWindow/constants.ts b/src/logViewerWindow/constants.ts new file mode 100644 index 0000000000..de3d4cf6a5 --- /dev/null +++ b/src/logViewerWindow/constants.ts @@ -0,0 +1,18 @@ +/** + * Constants for Log Viewer window + */ + +/** Auto-refresh interval in milliseconds */ +export const AUTO_REFRESH_INTERVAL_MS = 2000; + +/** Delay before scrolling to ensure Virtuoso is ready */ +export const SCROLL_DELAY_MS = 100; + +/** Window size as multiplier of screen size */ +export const WINDOW_SIZE_MULTIPLIER = 0.8; + +/** Debounce delay for search filter in milliseconds */ +export const SEARCH_DEBOUNCE_MS = 300; + +/** Virtuoso overscan count for smooth scrolling */ +export const VIRTUOSO_OVERSCAN = 50; diff --git a/src/logViewerWindow/ipc.ts b/src/logViewerWindow/ipc.ts new file mode 100644 index 0000000000..c791f12e7e --- /dev/null +++ b/src/logViewerWindow/ipc.ts @@ -0,0 +1,502 @@ +import fs, { createWriteStream } from 'fs'; +import path from 'path'; +import { promisify } from 'util'; + +import archiver from 'archiver'; +import type { Event } from 'electron'; +import { app, BrowserWindow, screen, dialog } from 'electron'; +import i18next from 'i18next'; + +import { packageJsonInformation } from '../app/main/app'; +import { handle } from '../ipc/main'; +import { select } from '../store'; +import type { RootState } from '../store/rootReducer'; +import { getRootWindow } from '../ui/main/rootWindow'; +import { WINDOW_SIZE_MULTIPLIER } from './constants'; + +const t = i18next.t.bind(i18next); + +const readFile = promisify(fs.readFile); +const writeFile = promisify(fs.writeFile); + +let logViewerWindow: BrowserWindow | null = null; +const allowedLogPaths = new Set(); + +const getLogFilePath = (): string => { + const logsPath = app.getPath('logs'); + return path.join(logsPath, 'main.log'); +}; + +const validateLogFilePath = ( + filePath: string +): { valid: boolean; error?: string } => { + const normalizedPath = path.normalize(filePath); + + if (filePath.includes('..') || normalizedPath.includes('..')) { + return { valid: false, error: 'Path traversal not allowed' }; + } + + if (!path.isAbsolute(normalizedPath)) { + return { valid: false, error: 'Only absolute paths are allowed' }; + } + + const ext = path.extname(normalizedPath).toLowerCase(); + if (ext !== '.log' && ext !== '.txt') { + return { valid: false, error: 'Only .log and .txt files are allowed' }; + } + + return { valid: true }; +}; + +const LOG_ENTRY_REGEX = /^\[([^\]]+)\]\s+\[([^\]]+)\]/; + +const getLastNEntries = ( + content: string, + limit: number +): { content: string; totalEntries: number } => { + if (limit <= 0) { + return { content: '', totalEntries: 0 }; + } + + const lines = content.split(/\r?\n/); + const entryStartIndices: number[] = []; + + lines.forEach((line, index) => { + if (LOG_ENTRY_REGEX.test(line)) { + entryStartIndices.push(index); + } + }); + + const totalEntries = entryStartIndices.length; + + if (totalEntries === 0) { + return { content: lines.slice(-limit).join('\n'), totalEntries: 0 }; + } + + const startEntryIndex = Math.max(0, totalEntries - limit); + const startLineIndex = entryStartIndices[startEntryIndex]; + + return { + content: lines.slice(startLineIndex).join('\n'), + totalEntries, + }; +}; + +export const openLogViewerWindow = async (): Promise => { + if (logViewerWindow && !logViewerWindow.isDestroyed()) { + logViewerWindow.focus(); + return; + } + + const mainWindow = await getRootWindow(); + const winBounds = await mainWindow.getNormalBounds(); + + const centeredWindowPosition = { + x: winBounds.x + winBounds.width / 2, + y: winBounds.y + winBounds.height / 2, + }; + + const actualScreen = screen.getDisplayNearestPoint({ + x: centeredWindowPosition.x, + y: centeredWindowPosition.y, + }); + + const width = Math.round( + actualScreen.workAreaSize.width * WINDOW_SIZE_MULTIPLIER + ); + const height = Math.round( + actualScreen.workAreaSize.height * WINDOW_SIZE_MULTIPLIER + ); + const x = Math.round( + (actualScreen.workArea.width - width) / 2 + actualScreen.workArea.x + ); + const y = Math.round( + (actualScreen.workArea.height - height) / 2 + actualScreen.workArea.y + ); + + logViewerWindow = new BrowserWindow({ + width, + height, + x, + y, + title: 'Log Viewer - Rocket.Chat', + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + }, + show: false, + }); + + logViewerWindow.loadFile( + path.join(app.getAppPath(), 'app/log-viewer-window.html') + ); + + logViewerWindow.once('ready-to-show', () => { + logViewerWindow?.setTitle( + `Log Viewer - ${packageJsonInformation.productName}` + ); + logViewerWindow?.show(); + }); + + logViewerWindow.on('closed', () => { + logViewerWindow = null; + allowedLogPaths.clear(); + }); + + logViewerWindow.webContents.on( + 'will-navigate', + (event: Event, url: string) => { + if (!url.startsWith('file://')) { + event.preventDefault(); + } + } + ); + + logViewerWindow.webContents.setWindowOpenHandler(() => { + return { action: 'deny' }; + }); +}; + +export const startLogViewerWindowHandler = (): void => { + handle('log-viewer-window/open-window', openLogViewerWindow); + + handle('log-viewer-window/close-requested', async () => { + logViewerWindow?.close(); + }); + + handle('log-viewer-window/select-log-file', async () => { + try { + if (!logViewerWindow || logViewerWindow.isDestroyed()) { + return { success: false, error: 'Log viewer window not found' }; + } + + const result = await dialog.showOpenDialog(logViewerWindow, { + title: 'Select Log File', + filters: [ + { name: 'Log Files', extensions: ['log', 'txt'] }, + { name: 'All Files', extensions: ['*'] }, + ], + properties: ['openFile'], + }); + + if (result.canceled || result.filePaths.length === 0) { + return { success: false, canceled: true }; + } + + const selectedPath = result.filePaths[0]; + const validation = validateLogFilePath(selectedPath); + if (!validation.valid) { + return { success: false, error: validation.error }; + } + const normalizedPath = path.normalize(selectedPath); + allowedLogPaths.add(normalizedPath); + return { + success: true, + filePath: normalizedPath, + fileName: path.basename(normalizedPath), + }; + } catch (error) { + console.error('Failed to select log file:', error); + return { success: false, error: (error as Error).message }; + } + }); + + handle( + 'log-viewer-window/read-logs', + async (_, options?: { filePath?: string; limit?: number | 'all' }) => { + try { + let logPath: string; + if (options?.filePath) { + const validation = validateLogFilePath(options.filePath); + if (!validation.valid) { + return { success: false, error: validation.error }; + } + const normalizedPath = path.normalize(options.filePath); + const defaultLogPath = path.normalize(getLogFilePath()); + if ( + normalizedPath !== defaultLogPath && + !allowedLogPaths.has(normalizedPath) + ) { + return { + success: false, + error: + 'Log file not authorized. Please select it via the file dialog first.', + }; + } + logPath = normalizedPath; + } else { + logPath = getLogFilePath(); + } + const limit = options?.limit; + + if (!fs.existsSync(logPath)) { + if (!options?.filePath) { + const logDir = path.dirname(logPath); + if (!fs.existsSync(logDir)) { + fs.mkdirSync(logDir, { recursive: true }); + } + await writeFile(logPath, ''); + } else { + return { + success: false, + error: 'Selected log file does not exist', + }; + } + } + + let logContent: string; + let totalEntries: number | undefined; + const fileContent = await readFile(logPath, 'utf-8'); + + if (limit === 'all' || !limit) { + logContent = fileContent; + } else { + const result = getLastNEntries(fileContent, limit); + logContent = result.content; + totalEntries = result.totalEntries; + } + + const stats = fs.statSync(logPath); + const lastModifiedTime = stats.mtime.getTime(); + + return { + success: true, + logs: logContent, + filePath: logPath, + fileName: path.basename(logPath), + isDefaultLog: !options?.filePath, + lastModifiedTime, + fileSize: stats.size, + totalEntries, + }; + } catch (error) { + console.error('Failed to read log file:', error); + return { success: false, error: (error as Error).message }; + } + } + ); + + handle( + 'log-viewer-window/stat-log', + async (_, options?: { filePath?: string }) => { + try { + let logPath: string; + if (options?.filePath) { + const validation = validateLogFilePath(options.filePath); + if (!validation.valid) { + return { success: false, error: validation.error }; + } + const normalizedPath = path.normalize(options.filePath); + const defaultLogPath = path.normalize(getLogFilePath()); + if ( + normalizedPath !== defaultLogPath && + !allowedLogPaths.has(normalizedPath) + ) { + return { + success: false, + error: + 'Log file not authorized. Please select it via the file dialog first.', + }; + } + logPath = normalizedPath; + } else { + logPath = getLogFilePath(); + } + + if (!fs.existsSync(logPath)) { + return { success: false, error: 'Log file does not exist' }; + } + + const stats = fs.statSync(logPath); + return { + success: true, + lastModifiedTime: stats.mtime.getTime(), + size: stats.size, + }; + } catch (error) { + console.error('Failed to stat log file:', error); + return { success: false, error: (error as Error).message }; + } + } + ); + + handle( + 'log-viewer-window/read-logs-tail', + async (_, options: { fromByte: number; filePath?: string }) => { + try { + let logPath: string; + if (options.filePath) { + const validation = validateLogFilePath(options.filePath); + if (!validation.valid) { + return { success: false, error: validation.error }; + } + const normalizedPath = path.normalize(options.filePath); + const defaultLogPath = path.normalize(getLogFilePath()); + if ( + normalizedPath !== defaultLogPath && + !allowedLogPaths.has(normalizedPath) + ) { + return { + success: false, + error: + 'Log file not authorized. Please select it via the file dialog first.', + }; + } + logPath = normalizedPath; + } else { + logPath = getLogFilePath(); + } + + if (!fs.existsSync(logPath)) { + return { success: false, error: 'Log file does not exist' }; + } + + const stats = fs.statSync(logPath); + const rawFromByte = Number(options.fromByte); + const fromByte = + Number.isFinite(rawFromByte) && rawFromByte >= 0 + ? Math.min(Math.floor(rawFromByte), stats.size) + : 0; + + if (fromByte >= stats.size) { + return { + success: true, + logs: '', + newSize: stats.size, + lastModifiedTime: stats.mtime.getTime(), + }; + } + + const chunks: Buffer[] = []; + await new Promise((resolve, reject) => { + const stream = fs.createReadStream(logPath, { + start: fromByte, + encoding: undefined, + }); + stream.on('data', (chunk: Buffer) => chunks.push(chunk)); + stream.on('end', () => resolve()); + stream.on('error', (err) => reject(err)); + }); + + const newContent = Buffer.concat(chunks).toString('utf-8'); + + return { + success: true, + logs: newContent, + newSize: stats.size, + lastModifiedTime: stats.mtime.getTime(), + }; + } catch (error) { + console.error('Failed to read log tail:', error); + return { success: false, error: (error as Error).message }; + } + } + ); + + handle('log-viewer-window/confirm-clear-logs', async () => { + if (!logViewerWindow || logViewerWindow.isDestroyed()) { + return false; + } + + const { response } = await dialog.showMessageBox(logViewerWindow, { + type: 'warning', + buttons: [t('dialog.clearLogs.yes'), t('dialog.clearLogs.cancel')], + defaultId: 1, + title: t('dialog.clearLogs.title'), + message: t('dialog.clearLogs.message'), + detail: t('dialog.clearLogs.detail'), + }); + + return response === 0; + }); + + handle('log-viewer-window/clear-logs', async () => { + try { + const logPath = getLogFilePath(); + await writeFile(logPath, ''); + return { success: true }; + } catch (error) { + console.error('Failed to clear log file:', error); + return { success: false, error: (error as Error).message }; + } + }); + + handle( + 'log-viewer-window/save-logs', + async (_, options: { content: string; defaultFileName: string }) => { + try { + if (!logViewerWindow || logViewerWindow.isDestroyed()) { + return { success: false, error: 'Log viewer window not found' }; + } + + const result = await dialog.showSaveDialog(logViewerWindow, { + title: 'Save Log File', + defaultPath: options.defaultFileName, + filters: [ + { name: 'ZIP Files', extensions: ['zip'] }, + { name: 'All Files', extensions: ['*'] }, + ], + }); + + if (result.canceled || !result.filePath) { + return { success: false, canceled: true }; + } + + await new Promise((resolve, reject) => { + const output = createWriteStream(result.filePath!); + const archive = archiver('zip', { + zlib: { level: 9 }, + }); + + output.on('close', () => { + resolve(); + }); + + output.on('error', (err) => { + reject(err); + }); + + archive.on('error', (err) => { + reject(err); + }); + + archive.pipe(output); + + const baseName = options.defaultFileName + .replace(/\.zip$/i, '') + .replace(/\.log$/i, ''); + const logFileName = `${baseName}.log`; + + archive.append(options.content, { name: logFileName }); + + archive.finalize(); + }); + + return { + success: true, + filePath: result.filePath, + }; + } catch (error) { + console.error('Failed to save log file:', error); + return { success: false, error: (error as Error).message }; + } + } + ); + + handle('log-viewer-window/get-server-mapping', async () => { + try { + const servers = select((state: RootState) => state.servers); + if (!servers || !Array.isArray(servers)) { + return { success: true, mapping: {} }; + } + const mapping: Record = {}; + servers.forEach((server: any, index: number) => { + const key = `server-${index + 1}`; + mapping[key] = server.title || server.url || key; + }); + return { success: true, mapping }; + } catch { + return { success: true, mapping: {} }; + } + }); +}; diff --git a/src/logViewerWindow/log-viewer-window.tsx b/src/logViewerWindow/log-viewer-window.tsx new file mode 100644 index 0000000000..6dc7326bab --- /dev/null +++ b/src/logViewerWindow/log-viewer-window.tsx @@ -0,0 +1,98 @@ +import { PaletteStyleTag } from '@rocket.chat/fuselage'; +import type { Themes } from '@rocket.chat/fuselage/dist/components/PaletteStyleTag/types/themes'; +import i18next from 'i18next'; +import { useState, useEffect } from 'react'; +import { createRoot } from 'react-dom/client'; +import { initReactI18next, I18nextProvider } from 'react-i18next'; + +import { fallbackLng, interpolation } from '../i18n/common'; +import '../logging/preload'; +import resources from '../i18n/resources'; +import LogViewerWindow from './logViewerWindow'; + +const useSystemTheme = (): Themes => { + const [theme, setTheme] = useState(() => + window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' + ); + + useEffect(() => { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handleChange = (e: MediaQueryListEvent) => { + setTheme(e.matches ? 'dark' : 'light'); + }; + + mediaQuery.addEventListener('change', handleChange); + return () => mediaQuery.removeEventListener('change', handleChange); + }, []); + + return theme; +}; + +const ThemedLogViewerWindow = () => { + const theme = useSystemTheme(); + + return ( + <> + + + > + ); +}; + +const detectLocale = (): string => { + const browserLang = navigator.language; + if (!browserLang) return fallbackLng; + + // Try exact match first (e.g., "pt-BR" → "pt-BR") + if (browserLang in resources) return browserLang; + + // Try language code only (e.g., "fr-FR" → "fr") + const [langCode] = browserLang.split('-'); + if (langCode && langCode in resources) return langCode; + + return fallbackLng; +}; + +const setupI18n = async () => { + const lng = detectLocale(); + + const resourceBundles: Record = { + [fallbackLng]: { + translation: await resources[fallbackLng](), + }, + }; + + // Load detected locale if different from fallback + if (lng !== fallbackLng && lng in resources) { + resourceBundles[lng] = { + translation: await resources[lng as keyof typeof resources](), + }; + } + + await i18next.use(initReactI18next).init({ + lng, + fallbackLng, + resources: resourceBundles, + interpolation, + initImmediate: true, + }); +}; + +const start = async () => { + await setupI18n(); + + const rootElement = document.getElementById('root'); + + if (!rootElement) { + throw new Error('Root element not found'); + } + + const root = createRoot(rootElement); + root.render( + + + + ); +}; + +start().catch(console.error); diff --git a/src/logViewerWindow/logViewerWindow.tsx b/src/logViewerWindow/logViewerWindow.tsx new file mode 100644 index 0000000000..050d5802da --- /dev/null +++ b/src/logViewerWindow/logViewerWindow.tsx @@ -0,0 +1,969 @@ +import { + Box, + SearchInput, + Icon, + Button, + ButtonGroup, + Select, + Tile, + Throbber, + CheckBox, +} from '@rocket.chat/fuselage'; +import { + useLocalStorage, + useDebouncedValue, +} from '@rocket.chat/fuselage-hooks'; +import { ipcRenderer } from 'electron'; +import type { ChangeEvent, Key } from 'react'; +import { useState, useMemo, useCallback, useEffect, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import type { VirtuosoHandle } from 'react-virtuoso'; +import { Virtuoso } from 'react-virtuoso'; + +import { LogEntry } from './LogEntry'; +import { + AUTO_REFRESH_INTERVAL_MS, + SCROLL_DELAY_MS, + SEARCH_DEBOUNCE_MS, + VIRTUOSO_OVERSCAN, +} from './constants'; +import { + type LogLevel, + type LogEntryType, + type ReadLogsResponse, + type ReadLogsTailResponse, + type SaveLogsResponse, + type SelectFileResponse, + type ClearLogsResponse, + parseLogLevel, +} from './types'; + +const LOG_LINE_REGEX = /^\[([^\]]+)\]\s+\[([^\]]+)\]\s*(.*)$/; +const CONTEXT_REGEX = /^(\[[^\]]+\](?:\s*\[[^\]]+\])*)\s*(.*)$/; + +const formatFileSize = (bytes: number): string => { + if (bytes === 0) return '0 B'; + if (bytes < 1024) return `${bytes} B`; + const kb = bytes / 1024; + if (kb < 1024) return `${kb.toFixed(1)} KB`; + const mb = kb / 1024; + return `${mb.toFixed(1)} MB`; +}; + +function LogViewerWindow() { + const { t } = useTranslation(); + const [searchFilter, setSearchFilter] = useState(''); + const debouncedSearchFilter = useDebouncedValue( + searchFilter, + SEARCH_DEBOUNCE_MS + ); + const [logEntries, setLogEntries] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [isStreaming, setIsStreaming] = useState(false); + const virtuosoRef = useRef(null); + const lastModifiedTimeRef = useRef(undefined); + const lastKnownSizeRef = useRef(0); + const isAutoScrollingRef = useRef(false); + const [autoScroll, setAutoScroll] = useState(true); + const [userHasScrolled, setUserHasScrolled] = useState(false); + const [showContext, setShowContext] = useState(true); + const [showServer, setShowServer] = useState(true); + const [serverMapping, setServerMapping] = useState>( + {} + ); + const [fileInfo, setFileInfo] = useState<{ + size: string; + totalEntries: number; + totalEntriesInFile?: number; + lastModified: string; + dateRange: string; + lastModifiedTime?: number; + } | null>(null); + const [currentLogFile, setCurrentLogFile] = useState<{ + filePath?: string; + fileName: string; + isDefaultLog: boolean; + }>({ + fileName: 'main.log', + isDefaultLog: true, + }); + + const entryLimitOptions = useMemo<[string, string][]>( + () => [ + ['100', t('logViewer.filters.entryLimit.last100')], + ['500', t('logViewer.filters.entryLimit.last500')], + ['1000', t('logViewer.filters.entryLimit.last1000')], + ['5000', t('logViewer.filters.entryLimit.last5000')], + ['all', t('logViewer.filters.entryLimit.all')], + ], + [t] + ); + + const [entryLimit, setEntryLimit] = + useState<(typeof entryLimitOptions)[number][0]>('100'); + + const handleSearchFilterChange = useCallback( + (event: ChangeEvent) => { + setSearchFilter(event.target.value); + }, + [setSearchFilter] + ); + + const levelFilterOptions = useMemo<[LogLevel | 'all', string][]>( + () => [ + ['all', t('logViewer.filters.level.all')], + ['debug', t('logViewer.filters.level.debug')], + ['info', t('logViewer.filters.level.info')], + ['warn', t('logViewer.filters.level.warn')], + ['error', t('logViewer.filters.level.error')], + ['verbose', t('logViewer.filters.level.verbose')], + ], + [t] + ); + + const [levelFilter, setLevelFilter] = useLocalStorage< + (typeof levelFilterOptions)[number][0] + >('log-level', 'all'); + + const handleLevelFilterChange = useCallback( + (value: Key) => { + setLevelFilter(String(value) as LogLevel | 'all'); + }, + [setLevelFilter] + ); + + const contextFilterOptions = useMemo<[string, string][]>( + () => [ + ['all', t('logViewer.filters.context.all')], + ['main', t('logViewer.filters.context.main')], + ['renderer', t('logViewer.filters.context.renderer')], + ['webview', t('logViewer.filters.context.webview')], + ['videocall', t('logViewer.filters.context.videocall')], + ['outlook', t('logViewer.filters.context.outlook')], + ['auth', t('logViewer.filters.context.auth')], + ['updates', t('logViewer.filters.context.updates')], + ['notifications', t('logViewer.filters.context.notifications')], + ['servers', t('logViewer.filters.context.servers')], + ['ipc', t('logViewer.filters.context.ipc')], + ], + [t] + ); + + const [contextFilter, setContextFilter] = useLocalStorage< + (typeof contextFilterOptions)[number][0] + >('log-context', 'all'); + + const handleContextFilterChange = useCallback( + (value: Key) => { + setContextFilter(String(value)); + }, + [setContextFilter] + ); + + const serverFilterOptions = useMemo<[string, string][]>(() => { + const options: [string, string][] = [ + ['all', t('logViewer.filters.server.all')], + ]; + Object.entries(serverMapping).forEach(([key, name]) => { + const num = key.replace('server-', ''); + options.push([ + key, + `${t('logViewer.filters.server.label')} ${num} — ${name}`, + ]); + }); + return options; + }, [serverMapping, t]); + + const [serverFilter, setServerFilter] = useLocalStorage( + 'log-server', + 'all' + ); + + // Reset persisted server filter if the stored value no longer exists + useEffect(() => { + const validKeys = serverFilterOptions.map(([key]) => key); + if (!validKeys.includes(serverFilter)) { + setServerFilter('all'); + } + }, [serverFilter, serverFilterOptions, setServerFilter]); + + const handleServerFilterChange = useCallback( + (value: Key) => { + setServerFilter(String(value)); + }, + [setServerFilter] + ); + + const handleEntryLimitChange = useCallback( + (value: Key) => { + setEntryLimit(String(value)); + }, + [setEntryLimit] + ); + + const handleClearAll = useCallback((): void => { + setSearchFilter(''); + setLevelFilter('all'); + setContextFilter('all'); + setServerFilter('all'); + setEntryLimit('100'); + }, [ + setSearchFilter, + setLevelFilter, + setContextFilter, + setServerFilter, + setEntryLimit, + ]); + + const parseLogLines = useCallback((logText: string): LogEntryType[] => { + if (!logText || logText.trim() === '') { + return []; + } + const lines = logText.split(/\r?\n/).filter((line: string) => line.trim()); + const entries: LogEntryType[] = []; + let currentEntry: LogEntryType | null = null; + + lines.forEach((line, _index) => { + const match = line.match(LOG_LINE_REGEX); + + if (match) { + const [, timestamp, level, rest] = match; + + const contextMatch = rest.match(CONTEXT_REGEX); + const context = contextMatch?.[1] || ''; + const message = contextMatch?.[2] || rest; + + if (currentEntry) { + entries.push(currentEntry); + } + + currentEntry = { + id: `log-${entries.length}`, + timestamp, + level: parseLogLevel(level), + context: context.replace(/[\[\]]/g, ' ').trim(), + message: message.trim(), + raw: line, + }; + } else if (currentEntry && line.trim()) { + currentEntry.message += `\n${line}`; + currentEntry.raw += `\n${line}`; + } + }); + + if (currentEntry) { + entries.push(currentEntry); + } + + return entries.reverse(); + }, []); + + const loadLogs = useCallback(async () => { + setIsLoading(true); + try { + const response = (await ipcRenderer.invoke( + 'log-viewer-window/read-logs', + { + limit: 'all', + filePath: currentLogFile.isDefaultLog + ? undefined + : currentLogFile.filePath, + } + )) as ReadLogsResponse; + if (response?.success && response.logs !== undefined) { + const parsedLogs = parseLogLines(response.logs); + setLogEntries(parsedLogs); + + setCurrentLogFile({ + filePath: response.filePath, + fileName: response.fileName || 'main.log', + isDefaultLog: response.isDefaultLog ?? true, + }); + + const logText = response.logs; + const sizeInBytes = new Blob([logText]).size; + const sizeFormatted = formatFileSize(sizeInBytes); + + const timestamps = parsedLogs + .map((entry) => new Date(entry.timestamp)) + .filter((date) => !isNaN(date.getTime())); + const oldestDate = + timestamps.length > 0 + ? new Date(Math.min(...timestamps.map((d) => d.getTime()))) + : null; + const newestDate = + timestamps.length > 0 + ? new Date(Math.max(...timestamps.map((d) => d.getTime()))) + : null; + + let dateRange = t('logViewer.fileInfo.noEntries'); + if (oldestDate && newestDate) { + if (oldestDate.toDateString() === newestDate.toDateString()) { + dateRange = `${oldestDate.toLocaleTimeString()} - ${newestDate.toLocaleTimeString()}`; + } else { + dateRange = `${oldestDate.toLocaleString()} - ${newestDate.toLocaleString()}`; + } + } + + if (response.fileSize !== undefined) { + lastKnownSizeRef.current = response.fileSize; + } + + setFileInfo({ + size: sizeFormatted, + totalEntries: parsedLogs.length, + totalEntriesInFile: response.totalEntries, + lastModified: new Date().toLocaleString(), + dateRange, + lastModifiedTime: response.lastModifiedTime, + }); + } else { + console.error('Failed to load logs:', response?.error); + setFileInfo(null); + } + } catch (error) { + console.error('Failed to load logs:', error); + setFileInfo(null); + } finally { + setIsLoading(false); + } + }, [parseLogLines, currentLogFile.filePath, currentLogFile.isDefaultLog, t]); + + const filteredLogs = useMemo(() => { + const filtered = logEntries.filter((entry) => { + const matchesSearch = + !debouncedSearchFilter || + entry.message + .toLowerCase() + .includes(debouncedSearchFilter.toLowerCase()) || + entry.context + .toLowerCase() + .includes(debouncedSearchFilter.toLowerCase()); + + const matchesLevel = levelFilter === 'all' || entry.level === levelFilter; + + const matchesContext = + contextFilter === 'all' || + entry.context + .toLowerCase() + .split(/\s+/) + .some((tag) => tag.startsWith(contextFilter.toLowerCase())); + + const matchesServer = + serverFilter === 'all' || + entry.context + .toLowerCase() + .split(/\s+/) + .some((tag) => tag === serverFilter); + + return matchesSearch && matchesLevel && matchesContext && matchesServer; + }); + + // Apply entry limit as a display cap on filtered results + if (entryLimit !== 'all') { + const limit = parseInt(entryLimit); + if (filtered.length > limit) { + return filtered.slice(0, limit); + } + } + + return filtered; + }, [ + logEntries, + debouncedSearchFilter, + levelFilter, + contextFilter, + serverFilter, + entryLimit, + ]); + + useEffect(() => { + lastModifiedTimeRef.current = fileInfo?.lastModifiedTime; + }, [fileInfo?.lastModifiedTime]); + + // Fetch server-N → workspace name mapping from the main process + useEffect(() => { + const fetchMapping = async () => { + try { + const response = (await ipcRenderer.invoke( + 'log-viewer-window/get-server-mapping' + )) as { success: boolean; mapping: Record }; + if (response?.success) { + setServerMapping(response.mapping); + } + } catch { + // Non-critical: mapping not available yet + } + }; + fetchMapping(); + const interval = setInterval(fetchMapping, 30_000); + return () => clearInterval(interval); + }, []); + + useEffect(() => { + loadLogs(); + }, [loadLogs, currentLogFile.filePath, currentLogFile.isDefaultLog]); + + const checkForUpdates = useCallback(async () => { + if (!isStreaming || !currentLogFile.isDefaultLog) return; + + try { + const statResponse = (await ipcRenderer.invoke( + 'log-viewer-window/stat-log', + { filePath: undefined } + )) as { success: boolean; lastModifiedTime?: number; size?: number }; + + if (!statResponse?.success || !statResponse.lastModifiedTime) return; + + const currentModTime = lastModifiedTimeRef.current; + if (!currentModTime || statResponse.lastModifiedTime <= currentModTime) + return; + + const currentSize = statResponse.size ?? 0; + const previousSize = lastKnownSizeRef.current; + + // File was truncated/rotated (or cleared) — full re-read + if (currentSize < previousSize) { + loadLogs(); + return; + } + + // No new bytes — just a timestamp change (e.g. chmod) + if (currentSize === previousSize) { + lastModifiedTimeRef.current = statResponse.lastModifiedTime; + return; + } + + // Incremental read: only fetch new bytes appended since last read + const tailResponse = (await ipcRenderer.invoke( + 'log-viewer-window/read-logs-tail', + { fromByte: previousSize } + )) as ReadLogsTailResponse; + + if (tailResponse?.success && tailResponse.logs) { + const newEntries = parseLogLines(tailResponse.logs); + if (newEntries.length > 0) { + setLogEntries((prev) => { + // newEntries are already reversed (newest first) + // Prepend them to existing entries + return [...newEntries, ...prev]; + }); + + setFileInfo((prev) => + prev + ? { + ...prev, + totalEntries: prev.totalEntries + newEntries.length, + totalEntriesInFile: + (prev.totalEntriesInFile ?? 0) + newEntries.length, + lastModified: new Date().toLocaleString(), + lastModifiedTime: tailResponse.lastModifiedTime, + } + : prev + ); + } + + if (tailResponse.newSize !== undefined) { + lastKnownSizeRef.current = tailResponse.newSize; + } + lastModifiedTimeRef.current = tailResponse.lastModifiedTime; + } + } catch (error) { + console.error('Failed to check for updates:', error); + } + }, [isStreaming, currentLogFile.isDefaultLog, loadLogs, parseLogLines]); + + useEffect(() => { + if (!isStreaming || !currentLogFile.isDefaultLog) return; + + const interval = setInterval(checkForUpdates, AUTO_REFRESH_INTERVAL_MS); + return () => clearInterval(interval); + }, [isStreaming, currentLogFile.isDefaultLog, checkForUpdates]); + + useEffect(() => { + if (autoScroll) { + setUserHasScrolled(false); + } + }, [autoScroll]); + + useEffect(() => { + if ( + autoScroll && + !userHasScrolled && + logEntries.length > 0 && + virtuosoRef.current + ) { + const timeoutId = setTimeout(() => { + isAutoScrollingRef.current = true; + if (virtuosoRef.current && autoScroll && !userHasScrolled) { + virtuosoRef.current.scrollToIndex({ + index: 0, + behavior: 'auto', + }); + } + isAutoScrollingRef.current = false; + }, SCROLL_DELAY_MS); + + return () => clearTimeout(timeoutId); + } + return undefined; + }, [logEntries, autoScroll, userHasScrolled]); + + const handleScroll = useCallback(() => { + if (isAutoScrollingRef.current) return; + if (autoScroll && !userHasScrolled) { + setUserHasScrolled(true); + } + }, [autoScroll, userHasScrolled]); + + const renderLogEntry = useCallback( + (_index: number, entry: LogEntryType) => { + return ( + + ); + }, + [showContext, showServer, serverMapping] + ); + + const handleOpenLogFile = useCallback(async () => { + try { + const response = (await ipcRenderer.invoke( + 'log-viewer-window/select-log-file' + )) as SelectFileResponse; + if (response?.success && response.filePath) { + setLogEntries([]); + setFileInfo(null); + + setIsStreaming(false); + + setCurrentLogFile({ + filePath: response.filePath, + fileName: response.fileName || 'custom.log', + isDefaultLog: false, + }); + } + } catch (error) { + console.error('Failed to open log file:', error); + } + }, []); + + const handleOpenDefaultLog = useCallback(() => { + setLogEntries([]); + setFileInfo(null); + + setCurrentLogFile({ + filePath: undefined, + fileName: 'main.log', + isDefaultLog: true, + }); + }, []); + + const handleRefresh = useCallback(() => { + loadLogs(); + }, [loadLogs]); + + const handleClearLogs = useCallback(async () => { + if (!currentLogFile.isDefaultLog) { + return; + } + try { + const confirmed = await ipcRenderer.invoke( + 'log-viewer-window/confirm-clear-logs' + ); + if (!confirmed) return; + + const response = (await ipcRenderer.invoke( + 'log-viewer-window/clear-logs' + )) as ClearLogsResponse; + if (response?.success) { + lastKnownSizeRef.current = 0; + loadLogs(); + } + } catch (error) { + console.error('Failed to clear logs:', error); + } + }, [currentLogFile.isDefaultLog, loadLogs]); + + const handleToggleStreaming = useCallback(() => { + setIsStreaming(!isStreaming); + }, [isStreaming]); + + const handleCopyLogs = useCallback(() => { + const logText = filteredLogs.map((entry) => entry.raw).join('\n'); + navigator.clipboard.writeText(logText).catch((error) => { + console.error('Failed to copy logs to clipboard:', error); + }); + }, [filteredLogs]); + + const handleSaveLogs = useCallback(async () => { + try { + const logText = filteredLogs.map((entry) => entry.raw).join('\n'); + const timestamp = new Date() + .toISOString() + .slice(0, 19) + .replace(/:/g, '-'); + const response = (await ipcRenderer.invoke( + 'log-viewer-window/save-logs', + { + content: logText, + defaultFileName: `rocketchat_${timestamp}.zip`, + } + )) as SaveLogsResponse; + + if (response?.success) { + console.log('Logs saved successfully to:', response.filePath); + } else if (response?.error) { + console.error('Failed to save logs:', response.error); + } + } catch (error) { + console.error('Failed to save logs:', error); + } + }, [filteredLogs]); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.ctrlKey || e.metaKey) && e.key === 'f') { + e.preventDefault(); + const searchInput = document.querySelector( + 'input[type="search"]' + ) as HTMLInputElement; + searchInput?.focus(); + } + if ((e.ctrlKey || e.metaKey) && e.key === 's') { + e.preventDefault(); + handleSaveLogs(); + } + if (e.key === 'Escape' && searchFilter) { + setSearchFilter(''); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [searchFilter, setSearchFilter, handleSaveLogs]); + + const handleClose = useCallback(() => { + ipcRenderer.invoke('log-viewer-window/close-requested'); + }, []); + + return ( + + + + + + {t('logViewer.title')} + + + + + + {currentLogFile.fileName} + {!currentLogFile.isDefaultLog && + ` (${t('logViewer.fileInfo.custom')})`} + + + {fileInfo && ( + + + + + {fileInfo.totalEntriesInFile && + fileInfo.totalEntriesInFile !== fileInfo.totalEntries + ? t('logViewer.fileInfo.entriesOfTotal', { + count: fileInfo.totalEntries, + total: fileInfo.totalEntriesInFile, + }) + : t('logViewer.fileInfo.entries', { + count: fileInfo.totalEntries, + })} + + + + + {fileInfo.size} + + + + {fileInfo.dateRange} + + + )} + + + + + + {t('logViewer.buttons.openLogFile')} + + {!currentLogFile.isDefaultLog && ( + + + {t('logViewer.buttons.defaultLog')} + + )} + + + {t('logViewer.buttons.refresh')} + + + + {isStreaming + ? t('logViewer.buttons.stopAutoRefresh') + : t('logViewer.buttons.autoRefresh')} + + + + {t('logViewer.buttons.copy')} + + + + {t('logViewer.buttons.save')} + + + + {t('logViewer.buttons.clear')} + + + + {t('logViewer.buttons.close')} + + + + + + + + setShowContext(!showContext)} + /> + + {t('logViewer.controls.showContext')} + + + + setShowServer(!showServer)} + /> + + {t('logViewer.controls.showServer')} + + + + { + const newAutoScroll = !autoScroll; + setAutoScroll(newAutoScroll); + if (newAutoScroll) { + setUserHasScrolled(false); + if (filteredLogs.length > 0 && virtuosoRef.current) { + setTimeout(() => { + virtuosoRef.current?.scrollToIndex({ + index: 0, + behavior: 'smooth', + }); + }, 100); + } + } + }} + /> + + {t('logViewer.controls.autoScrollToTop')} + + + + + + + + + + + + + + + + + {serverFilterOptions.length > 1 && ( + + + + )} + + {t('logViewer.buttons.clearFilters')} + + + + + + + {isLoading && ( + + + + )} + {!isLoading && filteredLogs.length === 0 && ( + + + + {t('logViewer.messages.noLogsFound')} + + + {t('logViewer.messages.adjustFilters')} + + + )} + {!isLoading && filteredLogs.length > 0 && ( + + + {t('logViewer.fileInfo.entries', { + count: filteredLogs.length, + })} + + + + )} + + + + ); +} + +export default LogViewerWindow; diff --git a/src/logViewerWindow/types.ts b/src/logViewerWindow/types.ts new file mode 100644 index 0000000000..e408b01585 --- /dev/null +++ b/src/logViewerWindow/types.ts @@ -0,0 +1,83 @@ +export type LogLevel = + | 'debug' + | 'info' + | 'warn' + | 'error' + | 'verbose' + | 'silly'; + +export interface ILogEntryType { + id: string; + timestamp: string; + level: LogLevel; + context: string; + message: string; + raw: string; +} + +export interface IReadLogsResponse { + success: boolean; + logs?: string; + filePath?: string; + fileName?: string; + isDefaultLog?: boolean; + lastModifiedTime?: number; + fileSize?: number; + totalEntries?: number; + error?: string; +} + +export interface IReadLogsTailResponse { + success: boolean; + logs?: string; + newSize?: number; + lastModifiedTime?: number; + error?: string; +} + +export interface ISaveLogsResponse { + success: boolean; + filePath?: string; + canceled?: boolean; + error?: string; +} + +export interface ISelectFileResponse { + success: boolean; + filePath?: string; + fileName?: string; + canceled?: boolean; + error?: string; +} + +export interface IClearLogsResponse { + success: boolean; + error?: string; +} + +export interface IStatLogResponse { + success: boolean; + lastModifiedTime?: number; + size?: number; + error?: string; +} + +export type LogEntryType = ILogEntryType; +export type ReadLogsResponse = IReadLogsResponse; +export type ReadLogsTailResponse = IReadLogsTailResponse; +export type SaveLogsResponse = ISaveLogsResponse; +export type SelectFileResponse = ISelectFileResponse; +export type ClearLogsResponse = IClearLogsResponse; + +export const isLogLevel = (value: unknown): value is LogLevel => { + if (typeof value !== 'string') return false; + return ['debug', 'info', 'warn', 'error', 'verbose', 'silly'].includes( + value.toLowerCase() + ); +}; + +export const parseLogLevel = (value: unknown): LogLevel => { + if (typeof value !== 'string') return 'info'; + const trimmed = value.trim().toLowerCase(); + return isLogLevel(trimmed) ? trimmed : 'info'; +}; diff --git a/src/logging/cleanup.ts b/src/logging/cleanup.ts new file mode 100644 index 0000000000..13cf93ad13 --- /dev/null +++ b/src/logging/cleanup.ts @@ -0,0 +1,52 @@ +import fs from 'fs'; +import path from 'path'; + +import { app } from 'electron'; + +const DEFAULT_RETENTION_DAYS = 30; + +const PROTECTED_FILES = new Set(['main.log', 'errors.jsonl']); + +/** + * Clean up old log files based on retention policy + * @param retentionDays - Number of days to keep logs (default 30) + */ +export const cleanupOldLogs = ( + retentionDays = DEFAULT_RETENTION_DAYS +): void => { + try { + if (!Number.isFinite(retentionDays) || retentionDays <= 0) { + console.warn('[logging] Invalid retentionDays; skipping log cleanup'); + return; + } + + const logsPath = app.getPath('logs'); + const cutoffTime = Date.now() - retentionDays * 24 * 60 * 60 * 1000; + + if (!fs.existsSync(logsPath)) return; + + const files = fs.readdirSync(logsPath); + let deletedCount = 0; + + for (const file of files) { + if (PROTECTED_FILES.has(file)) continue; + + const filePath = path.join(logsPath, file); + try { + const stats = fs.statSync(filePath); + if (stats.isFile() && stats.mtime.getTime() < cutoffTime) { + fs.unlinkSync(filePath); + deletedCount++; + } + } catch { + // Skip files we can't access + } + } + + if (deletedCount > 0) { + console.info(`[logging] Cleaned up ${deletedCount} old log file(s)`); + } + } catch (error) { + console.warn('[logging] Failed to cleanup old logs:', error); + } +}; diff --git a/src/logging/context.ts b/src/logging/context.ts new file mode 100644 index 0000000000..73b14ec4fb --- /dev/null +++ b/src/logging/context.ts @@ -0,0 +1,283 @@ +import type { WebContents } from 'electron'; + +// Define server context storage map +const serverContextMap = new Map< + number, + { serverUrl: string; serverName: string } +>(); + +const MAX_SERVER_ID = 100; +const availableServerIds: number[] = Array.from( + { length: MAX_SERVER_ID }, + (_, i) => i + 1 +); +const usedServerIds = new Map(); + +// Fallback counter for pool exhaustion - starts after MAX_SERVER_ID +let overflowServerIdCounter = MAX_SERVER_ID; + +// Define the log context interface +export interface ILogContext { + processType: string; + webContentsType?: string; + webContentsId?: number; + serverInfo?: { + url: string; + name: string; + }; + component?: string; +} + +/** + * Get process context information + */ +export const getProcessContext = (): string => { + if (typeof process === 'undefined') return 'unknown'; + + if (process.type === 'browser') { + return 'main'; + } + + if (process.type === 'renderer') { + // Try to detect renderer type based on environment + if (typeof window !== 'undefined') { + // Check if this is the root window + if ( + window.location?.pathname === '/index.html' || + window.location?.pathname === '/' + ) { + return 'renderer:root'; + } + + // Check if this is the video call window + if (window.location?.pathname === '/video-call-window.html') { + return 'renderer:videocall'; + } + + // Default to webview for other renderer processes + return 'renderer:webview'; + } + + return 'renderer:unknown'; + } + + return 'preload'; +}; + +/** + * Get server context for webContents (privacy-safe anonymous ID) + */ +export const getServerContext = (webContents?: WebContents): string => { + if (!webContents) return 'local'; + + const webContentsId = webContents.id; + + // Check if we already have a context for this webContents + if (serverContextMap.has(webContentsId)) { + return serverContextMap.get(webContentsId)!.serverName; + } + + // For main window, it's local + if (webContents.getType() === 'window') { + serverContextMap.set(webContentsId, { + serverUrl: 'local', + serverName: 'local', + }); + return 'local'; + } + + // For webviews, assign anonymous server ID from pool + if (webContents.getType() === 'webview') { + let serverId = availableServerIds.shift(); + + if (serverId === undefined) { + overflowServerIdCounter += 1; + serverId = overflowServerIdCounter; + console.warn( + `[Logging] Server ID pool exhausted (max: ${MAX_SERVER_ID}). ` + + `Allocated overflow ID: ${serverId}. ` + + `Active webviews: ${usedServerIds.size}, Available IDs: ${availableServerIds.length}` + ); + } + + usedServerIds.set(webContentsId, serverId); + const serverName = `server-${serverId}`; + serverContextMap.set(webContentsId, { + serverUrl: 'unknown', + serverName, + }); + return serverName; + } + + return 'unknown'; +}; + +/** + * Get component context based on the calling code location + */ +// eslint-disable-next-line complexity +export const getComponentContext = (captureStack = false): string => { + if (!captureStack) { + return 'general'; + } + + const error = new Error(); + const stack = error.stack || ''; + + // Analyze stack trace to determine component context (privacy-safe) + if ( + stack.includes('auth') || + stack.includes('login') || + stack.includes('Login') + ) { + return 'auth'; + } + + if ( + stack.includes('connection') || + stack.includes('websocket') || + stack.includes('network') + ) { + return 'connection'; + } + + if (stack.includes('notification') || stack.includes('Notification')) { + return 'notifications'; + } + + if (stack.includes('update') || stack.includes('Update')) { + return 'updates'; + } + + if ( + stack.includes('outlook') || + stack.includes('Outlook') || + stack.includes('outlookCalendar') || + stack.includes('getOutlookEvents') || + stack.includes('syncEventsWithRocketChatServer') + ) { + return 'outlook'; + } + + if ( + stack.includes('server') || + stack.includes('Server') || + stack.includes('servers') + ) { + return 'servers'; + } + + if ( + stack.includes('video') || + stack.includes('Video') || + stack.includes('jitsi') + ) { + return 'videocall'; + } + + if (stack.includes('preload')) { + return 'preload'; + } + + if (stack.includes('ipc') || stack.includes('IPC')) { + return 'ipc'; + } + + if ( + stack.includes('storage') || + stack.includes('Storage') || + stack.includes('store') + ) { + return 'storage'; + } + + if ( + stack.includes('ui') || + stack.includes('UI') || + stack.includes('component') + ) { + return 'ui'; + } + + if ( + stack.includes('app') || + stack.includes('App') || + stack.includes('main') + ) { + return 'app'; + } + + return 'general'; +}; + +/** + * Get complete log context + */ +export const getLogContext = ( + webContents?: WebContents, + captureComponentStack = false +): ILogContext => { + const context: ILogContext = { + processType: getProcessContext(), + }; + + if (webContents) { + context.webContentsType = webContents.getType(); + context.webContentsId = webContents.id; + const serverContext = getServerContext(webContents); + context.serverInfo = { + url: serverContext, + name: serverContext, + }; + } + + context.component = getComponentContext(captureComponentStack); + + return context; +}; + +/** + * Format context for logging + */ +export const formatLogContext = (context: ILogContext): string => { + const parts: string[] = []; + + // Always include process + parts.push(context.processType); + + // Add server context if available + if (context.serverInfo?.name && context.serverInfo.name !== 'local') { + parts.push(context.serverInfo.name); + } + + // Add component context if available + if (context.component && context.component !== 'general') { + parts.push(context.component); + } + + return `[${parts.join('] [')}]`; +}; + +/** + * Clean up server context mapping when webContents is destroyed + */ +export const cleanupServerContext = (webContentsId: number): void => { + serverContextMap.delete(webContentsId); + const serverId = usedServerIds.get(webContentsId); + if (serverId !== undefined) { + availableServerIds.push(serverId); + usedServerIds.delete(webContentsId); + } +}; + +/** + * Get current server mappings (for debugging) + */ +export const getServerMappings = (): Map< + number, + { serverUrl: string; serverName: string } +> => { + return new Map(serverContextMap); +}; + +export { MAX_SERVER_ID }; diff --git a/src/logging/dedup.ts b/src/logging/dedup.ts new file mode 100644 index 0000000000..701a962f34 --- /dev/null +++ b/src/logging/dedup.ts @@ -0,0 +1,88 @@ +/** + * Simple log deduplication: if the current message is identical to the + * last one logged, skip it. Errors are never skipped. + * + * Two integration points: + * 1. `shouldLog()` — call before IPC messages reach electron-log + * 2. `createFileHook()` — electron-log hook for main-process messages + * (file transport only, console stays verbose) + * + * Each integration point tracks its own last-key so they don't interfere. + */ +export class LogDeduplicator { + /** Key of the last IPC message that was actually written */ + private lastIpcKey = ''; + + /** Key of the last file-transport message that was actually written */ + private lastFileKey = ''; + + /** + * Build a dedup key from level + message text. + * Strips dynamic numbers (timestamps, IDs) so messages that differ + * only by a changing number still match. + */ + private makeKey(level: string, args: any[]): string { + const text = args + .map((a) => { + if (typeof a === 'string') return a; + try { + return JSON.stringify(a); + } catch { + return String(a); + } + }) + .join(' ') + .replace(/\b\d{4,}\b/g, '#') + .replace(/\b\d+\.\d+\b/g, '#'); + return `${level}|${text}`; + } + + /** + * Check whether an IPC message should be logged. + * Returns false if it is identical to the last logged message. + * Errors always return true. + */ + shouldLog(level: string, contextStr: string, args: any[]): boolean { + if (level === 'error') return true; + + const key = this.makeKey(level, [contextStr, ...args]); + if (key === this.lastIpcKey) return false; + + this.lastIpcKey = key; + return true; + } + + /** + * Electron-log hook for the file transport. + * Suppresses consecutive duplicate non-error messages. + * Console transport is unaffected. + */ + createFileHook() { + return ( + message: any, + _transport: any, + transportName?: string + ): any | null => { + if (transportName !== 'file') return message; + if (!message || message.level === 'error') return message; + + const text = + message.data + ?.map((item: any) => { + if (typeof item === 'string') return item; + try { + return JSON.stringify(item); + } catch { + return String(item); + } + }) + .join(' ') || ''; + const key = `${message.level}|${text.replace(/\b\d{4,}\b/g, '#').replace(/\b\d+\.\d+\b/g, '#')}`; + + if (key === this.lastFileKey) return null; // suppress duplicate + + this.lastFileKey = key; + return message; + }; + } +} diff --git a/src/logging/fallback.ts b/src/logging/fallback.ts new file mode 100644 index 0000000000..7bd3dc8b79 --- /dev/null +++ b/src/logging/fallback.ts @@ -0,0 +1,31 @@ +export const fallbackLog = (level: string, ...args: any[]): void => { + try { + const timestamp = new Date().toISOString(); + const message = args + .map((a) => { + if (a instanceof Error) return `${a.message}\n${a.stack}`; + if (typeof a === 'object' && a !== null) { + try { + return JSON.stringify(a); + } catch { + return String(a); + } + } + return String(a); + }) + .join(' '); + process.stderr.write( + `[${timestamp}] [${level.toUpperCase()}] [FALLBACK] ${message}\n` + ); + } catch { + /* nothing more we can do */ + } +}; + +export const logLoggingFailure = (error: unknown, context: string): void => { + fallbackLog( + 'error', + `Logging failed in ${context}:`, + error instanceof Error ? error.message : String(error) + ); +}; diff --git a/src/logging/index.ts b/src/logging/index.ts new file mode 100644 index 0000000000..3253dea587 --- /dev/null +++ b/src/logging/index.ts @@ -0,0 +1,546 @@ +import fs from 'fs'; +import path from 'path'; + +import type { WebContents } from 'electron'; +import { app, ipcMain } from 'electron'; +import log from 'electron-log'; + +import { select, watch } from '../store'; +import type { RootState } from '../store/rootReducer'; +import { + getLogContext, + formatLogContext, + cleanupServerContext, +} from './context'; +import { LogDeduplicator } from './dedup'; +import { logLoggingFailure } from './fallback'; +import { createPrivacyHook, redactSensitiveData } from './privacy'; + +// Shared dedup instance — created once, used by both file hook and IPC handler +let logDeduplicator: LogDeduplicator | null = null; + +const originalConsole = { + log: console.log.bind(console), + info: console.info.bind(console), + warn: console.warn.bind(console), + error: console.error.bind(console), + debug: console.debug.bind(console), +}; + +// Enhanced console override with context +const overrideConsole = () => { + try { + // Override console.log to use electron-log debug level with context + console.log = (...args: any[]) => { + const context = getLogContext(); + const contextStr = formatLogContext(context); + log.debug(contextStr, ...args); + }; + + // Override console.info to use electron-log info level with context + console.info = (...args: any[]) => { + const context = getLogContext(); + const contextStr = formatLogContext(context); + log.info(contextStr, ...args); + }; + + // Override console.warn to use electron-log warn level with context + console.warn = (...args: any[]) => { + const context = getLogContext(); + const contextStr = formatLogContext(context); + log.warn(contextStr, ...args); + }; + + // Override console.error to use electron-log error level with context + console.error = (...args: any[]) => { + const context = getLogContext(); + const contextStr = formatLogContext(context); + log.error(contextStr, ...args); + }; + + // Override console.debug to use electron-log debug level with context + console.debug = (...args: any[]) => { + const context = getLogContext(); + const contextStr = formatLogContext(context); + log.debug(contextStr, ...args); + }; + + // Add a way to access original console if needed + (console as any).original = originalConsole; + } catch (error) { + // If override fails, restore original console + Object.assign(console, originalConsole); + originalConsole.warn('Failed to override console methods:', error); + } +}; + +// Enhanced logging function with context +export const logWithContext = ( + level: 'debug' | 'info' | 'warn' | 'error', + webContentsInstance?: WebContents, + ...args: any[] +) => { + const context = getLogContext(webContentsInstance); + const contextStr = formatLogContext(context); + + switch (level) { + case 'debug': + log.debug(contextStr, ...args); + break; + case 'info': + log.info(contextStr, ...args); + break; + case 'warn': + log.warn(contextStr, ...args); + break; + case 'error': + log.error(contextStr, ...args); + break; + } +}; + +// Simple configuration that works in both main and renderer processes +const configureLogging = () => { + try { + // Only configure transports if they exist (main process) + if (log.transports?.console && log.transports?.file) { + // Set log level based on environment + if (process.env.NODE_ENV === 'development') { + log.transports.console.level = 'debug'; + log.transports.file.level = 'debug'; + } else { + log.transports.console.level = 'info'; + log.transports.file.level = 'info'; + } + + // Configure file transport with enhanced format + log.transports.file.maxSize = 10 * 1024 * 1024; // 10MB + log.transports.file.format = + '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}'; + // Enable async writes — batches multiple log lines into single fs.writeFile calls + (log.transports.file as any).sync = false; + // Let electron-log use its default path: ~/Library/Logs/{app name}/main.log + + // Configure console transport with enhanced format + log.transports.console.format = + '[{y}-{m}-{d} {h}:{i}:{s}.{ms}] [{level}] {text}'; + + // Use original console to prevent recursion when console is overridden + log.transports.console.writeFn = ({ message }) => { + originalConsole.log(message); + }; + + // Add privacy hook FIRST so all data is redacted before other hooks + log.hooks.push(createPrivacyHook()); + + // Add dedup hook — suppresses repeated non-error messages at the file + // transport only (console stays verbose for live debugging) + logDeduplicator = new LogDeduplicator(); + log.hooks.push(logDeduplicator.createFileHook()); + + // Add structured NDJSON logging for errors (useful for error reporting) + const errorJsonlPath = path.join(app.getPath('logs'), 'errors.jsonl'); + const MAX_ERROR_LOG_SIZE = 5 * 1024 * 1024; // 5MB + const errorJsonlBuffer: string[] = []; + const ERRORS_FLUSH_INTERVAL_MS = 10_000; + const MAX_BUFFERED_ERRORS = 50; + + const flushErrorJsonl = (sync = false) => { + if (errorJsonlBuffer.length === 0) return; + const batch = errorJsonlBuffer.splice(0); + const content = batch.join(''); + + // Check size and truncate if needed before appending + try { + if (fs.existsSync(errorJsonlPath)) { + const stats = fs.statSync(errorJsonlPath); + if (stats.size > MAX_ERROR_LOG_SIZE) { + const fileContent = fs.readFileSync(errorJsonlPath, 'utf-8'); + const lines = fileContent.split('\n'); + const halfIndex = Math.floor(lines.length / 2); + fs.writeFileSync( + errorJsonlPath, + lines.slice(halfIndex).join('\n') + ); + } + } + } catch { + // Non-critical: truncation failure + } + + if (sync) { + try { + fs.appendFileSync(errorJsonlPath, content); + } catch (err) { + originalConsole.error('Failed to write error log:', err); + } + } else { + fs.promises.appendFile(errorJsonlPath, content).catch((err) => { + originalConsole.error('Failed to write error log:', err); + }); + } + }; + + const errorFlushTimer = setInterval( + flushErrorJsonl, + ERRORS_FLUSH_INTERVAL_MS + ); + + log.hooks.push((message: any) => { + if (message.level === 'error') { + try { + const rawText = message.data?.join(' ') || ''; + errorJsonlBuffer.push( + `${JSON.stringify({ + timestamp: new Date().toISOString(), + level: message.level, + text: redactSensitiveData(rawText), + version: app.getVersion(), + })}\n` + ); + + if (errorJsonlBuffer.length >= MAX_BUFFERED_ERRORS) { + flushErrorJsonl(); + } + } catch { + // Ignore JSON logging failures + } + } + return message; + }); + + // Flush error buffer before app quits + if (process.type === 'browser') { + app.on('before-quit', () => { + flushErrorJsonl(true); + clearInterval(errorFlushTimer); + }); + } + + // Initialize for renderer processes if we're in main process + if (process.type === 'browser') { + log.initialize(); + } + + // Restrict log file permissions to owner-only (0600) + try { + const logFilePath = path.join(app.getPath('logs'), 'main.log'); + if (fs.existsSync(logFilePath)) { + fs.chmodSync(logFilePath, 0o600); + } + if (fs.existsSync(errorJsonlPath)) { + fs.chmodSync(errorJsonlPath, 0o600); + } + } catch { + // Non-critical: chmod may fail on Windows + } + } + + // Override console.log to use electron-log + overrideConsole(); + } catch (error) { + logLoggingFailure(error, 'configureLogging'); + } + + return log; +}; + +// Function to setup logging in webviews and all webContents +export const setupWebContentsLogging = () => { + if (process.type !== 'browser') return; + + try { + // Use the static import for store instead of dynamic imports + let selectFunction: typeof select | null = null; + + try { + selectFunction = select; + } catch (importError: any) { + // If store import fails, continue without server context mapping + console.warn( + '[main] [app] Store module not available for server context mapping:', + importError.message + ); + } + + // Synchronous IPC handler for preload scripts to get their server tag + ipcMain.on('log-viewer-window/get-server-tag', (event, origin: string) => { + try { + if (selectFunction && origin) { + const servers = selectFunction((state: RootState) => state.servers); + const serverIndex = + servers.findIndex( + (s: any) => s.url && origin.startsWith(s.url.replace(/\/$/, '')) + ) + 1; + if (serverIndex > 0) { + event.returnValue = `server-${serverIndex}`; + return; + } + } + } catch { + // Non-critical + } + event.returnValue = ''; + }); + + // Rate limiting for console-log IPC to prevent flooding from compromised webviews + const ipcRateLimit = new Map< + number, + { count: number; resetTime: number } + >(); + const MAX_IPC_MESSAGES_PER_SECOND = 100; + + // Listen for new webContents creation + app.on('web-contents-created', (_event, webContents) => { + // Skip if this is the main renderer process (it already has logging) + if (webContents.getType() === 'window') return; + + // For webviews and other renderer processes, inject console override + webContents.on('dom-ready', () => { + try { + // Get server URL for this webContents + // For webviews, derive from the webContents URL by matching against known servers + let serverUrl = 'unknown'; + if (selectFunction && webContents.getType() === 'webview') { + try { + const currentUrl = webContents.getURL(); + if (currentUrl) { + const servers = selectFunction( + (state: RootState) => state.servers + ); + const server = servers.find( + (s: any) => + s.url && currentUrl.startsWith(s.url.replace(/\/$/, '')) + ); + serverUrl = server?.url || 'unknown'; + } + } catch (storeError) { + logLoggingFailure( + storeError, + 'setupWebContentsLogging - store access' + ); + } + } + + // TODO: Replace executeJavaScript injection with a preload script for + // better maintainability and debuggability (see src/logging/preload.ts) + // Inject enhanced console override directly into the webContents + const consoleOverrideScript = ` + (function() { + try { + const { ipcRenderer } = require('electron'); + + // Store original console methods + const originalConsole = { + log: console.log, + info: console.info, + warn: console.warn, + error: console.error, + debug: console.debug, + }; + + // Get webContents ID and server URL for context + const webContentsId = ${webContents.id}; + const serverUrl = ${JSON.stringify(serverUrl)}; + + + // Override console methods to send to main process with context + console.log = (...args) => { + originalConsole.log(...args); + ipcRenderer.send('console-log', 'debug', webContentsId, serverUrl, ...args); + }; + + console.info = (...args) => { + originalConsole.info(...args); + ipcRenderer.send('console-log', 'info', webContentsId, serverUrl, ...args); + }; + + console.warn = (...args) => { + originalConsole.warn(...args); + ipcRenderer.send('console-log', 'warn', webContentsId, serverUrl, ...args); + }; + + console.error = (...args) => { + originalConsole.error(...args); + ipcRenderer.send('console-log', 'error', webContentsId, serverUrl, ...args); + }; + + console.debug = (...args) => { + originalConsole.debug(...args); + ipcRenderer.send('console-log', 'debug', webContentsId, serverUrl, ...args); + }; + + // Add marker to know console override is active + console.original = originalConsole; + } catch (error) { + console.error('[logging] Failed to override console in webContents:', error); + } + })(); + `; + + webContents.executeJavaScript(consoleOverrideScript).catch((err) => { + log.warn( + `[logging] Failed to inject console override into webContents ${webContents.id}:`, + err + ); + }); + } catch (error) { + logLoggingFailure( + error, + 'setupWebContentsLogging - webContents injection' + ); + } + }); + + // Clean up context and rate limit state when webContents is destroyed + webContents.on('destroyed', () => { + cleanupServerContext(webContents.id); + ipcRateLimit.delete(webContents.id); + }); + }); + + // Handle console messages from renderer processes with enhanced context + ipcMain.on( + 'console-log', + (event, level, _webContentsId, _serverUrl, ...args) => { + try { + // Use authenticated sender identity — never trust renderer-supplied IDs + const senderWebContents = event.sender; + const senderId = senderWebContents.id; + + const now = Date.now(); + let rateState = ipcRateLimit.get(senderId); + if (!rateState || now > rateState.resetTime) { + rateState = { count: 0, resetTime: now + 1000 }; + ipcRateLimit.set(senderId, rateState); + } + rateState.count++; + if (rateState.count > MAX_IPC_MESSAGES_PER_SECOND) { + return; + } + + // Create enhanced context string with server info + const context = getLogContext(senderWebContents); + let contextStr = formatLogContext(context); + + // Add server index to webview context using the authenticated + // sender's URL — never trust renderer-supplied values. + if (selectFunction && senderWebContents.getType() === 'webview') { + try { + const currentUrl = senderWebContents.getURL(); + if (currentUrl) { + const servers = selectFunction( + (state: RootState) => state.servers + ); + const serverIndex = + servers.findIndex( + (s: any) => + s.url && currentUrl.startsWith(s.url.replace(/\/$/, '')) + ) + 1; + if (serverIndex > 0) { + contextStr = contextStr.replace( + '[renderer:webview]', + `[renderer:webview] [server-${serverIndex}]` + ); + } + } + } catch { + // Non-critical: server index enrichment failed + } + } + + // Dedup non-error IPC messages before they reach electron-log + if ( + logDeduplicator && + !logDeduplicator.shouldLog(level, contextStr, args) + ) { + return; + } + + // Log with enhanced context + switch (level) { + case 'debug': + case 'verbose': + case 'silly': + log.debug(contextStr, ...args); + break; + case 'info': + log.info(contextStr, ...args); + break; + case 'warn': + log.warn(contextStr, ...args); + break; + case 'error': + log.error(contextStr, ...args); + break; + default: + log.info(contextStr, ...args); + break; + } + } catch (error) { + logLoggingFailure(error, 'console-log IPC handler'); + } + } + ); + } catch (error) { + console.warn('[main] [app] Failed to setup webContents logging:', error); + } +}; + +/** + * Set log level at runtime (useful for debugging without restart) + */ +export const setLogLevel = ( + level: 'error' | 'warn' | 'info' | 'verbose' | 'debug' | 'silly' +): void => { + if (log.transports?.console) { + log.transports.console.level = level; + } + if (log.transports?.file) { + log.transports.file.level = level; + } + console.info(`[logging] Log level changed to: ${level}`); +}; + +/** + * Get current log level + */ +export const getLogLevel = (): string => { + return (log.transports?.file?.level as string) || 'info'; +}; + +/** + * Watch the debug logging toggle in Redux and update log level at runtime. + * Called after store is initialized. + */ +export const setupDebugLoggingWatch = (): void => { + if (process.type !== 'browser') return; + + try { + const isEnabled = select((state: RootState) => state.isDebugLoggingEnabled); + if (isEnabled) { + setLogLevel('debug'); + } + + watch( + (state: RootState) => state.isDebugLoggingEnabled, + (enabled) => { + setLogLevel(enabled ? 'debug' : 'info'); + } + ); + } catch { + // Store may not be initialized yet — will apply on next toggle + } +}; + +// Export the configured logger +export const logger = configureLogging(); + +// Export electron-log for direct use if needed +export default log; + +// Export utility functions +export * from './utils'; +export * from './context'; +export * from './scopes'; +export * from './cleanup'; diff --git a/src/logging/preload.ts b/src/logging/preload.ts new file mode 100644 index 0000000000..c98bcae534 --- /dev/null +++ b/src/logging/preload.ts @@ -0,0 +1,97 @@ +import { ipcRenderer } from 'electron'; +import log from 'electron-log/renderer'; + +import { getProcessContext, getComponentContext } from './context'; + +// Function to override console methods in renderer processes with enhanced context +try { + // Get process context once + const processContext = getProcessContext(); + + // For webviews, try to get server index from main process + let serverTag = ''; + if (processContext === 'renderer:webview' && typeof window !== 'undefined') { + try { + const result = ipcRenderer.sendSync( + 'log-viewer-window/get-server-tag', + window.location?.origin || '' + ); + if (result) { + serverTag = ` [${result}]`; + } + } catch { + // Silently fail if IPC not available + } + } + + if (typeof console !== 'undefined') { + // Store original console methods + const originalConsole = { + log: console.log, + info: console.info, + warn: console.warn, + error: console.error, + debug: console.debug, + }; + + // Override console.log to use electron-log debug level with context + console.log = (...args: any[]) => { + try { + const component = getComponentContext(); + const contextStr = `[${processContext}]${serverTag}${component !== 'general' ? ` [${component}]` : ''}`; + log.debug(contextStr, ...args); + } catch { + originalConsole.log(...args); + } + }; + + // Override console.info to use electron-log info level with context + console.info = (...args: any[]) => { + try { + const component = getComponentContext(); + const contextStr = `[${processContext}]${serverTag}${component !== 'general' ? ` [${component}]` : ''}`; + log.info(contextStr, ...args); + } catch { + originalConsole.info(...args); + } + }; + + // Override console.warn to use electron-log warn level with context + console.warn = (...args: any[]) => { + try { + const component = getComponentContext(true); + const contextStr = `[${processContext}]${serverTag}${component !== 'general' ? ` [${component}]` : ''}`; + log.warn(contextStr, ...args); + } catch { + originalConsole.warn(...args); + } + }; + + // Override console.error to use electron-log error level with context + console.error = (...args: any[]) => { + try { + const component = getComponentContext(true); + const contextStr = `[${processContext}]${serverTag}${component !== 'general' ? ` [${component}]` : ''}`; + log.error(contextStr, ...args); + } catch { + originalConsole.error(...args); + } + }; + + // Override console.debug to use electron-log debug level with context + console.debug = (...args: any[]) => { + try { + const component = getComponentContext(); + const contextStr = `[${processContext}]${serverTag}${component !== 'general' ? ` [${component}]` : ''}`; + log.debug(contextStr, ...args); + } catch { + originalConsole.debug(...args); + } + }; + + // Add marker to know console override is active + (console as any).original = originalConsole; + } +} catch (error) { + // Silently fail if electron-log isn't available in this context +} diff --git a/src/logging/privacy.ts b/src/logging/privacy.ts new file mode 100644 index 0000000000..a488584d17 --- /dev/null +++ b/src/logging/privacy.ts @@ -0,0 +1,341 @@ +/** + * Privacy hooks for electron-log + * Filters sensitive data before writing to log files + * + * Strategy: + * 1. Field-based redaction (object keys) — precise, low false positives + * 2. Pattern-based redaction (string content) — catches secrets in messages + * 3. Partial masking where possible — preserves debugging utility + */ + +// ── Helpers ────────────────────────────────────────────────────────────────── + +/** Mask a value showing first/last N chars: "eyJhbG...xK9s" */ +const mask = (value: string, keep = 4): string => { + if (value.length <= keep * 2 + 3) return '[REDACTED]'; + return `${value.slice(0, keep)}...${value.slice(-keep)}`; +}; + +/** Luhn checksum — validates credit card numbers to avoid false positives */ +const luhnCheck = (digits: string): boolean => { + let sum = 0; + let alternate = false; + for (let i = digits.length - 1; i >= 0; i--) { + let n = parseInt(digits[i], 10); + if (alternate) { + n *= 2; + if (n > 9) n -= 9; + } + sum += n; + alternate = !alternate; + } + return sum % 10 === 0; +}; + +// ── Pattern-based redaction (applied to string content) ────────────────────── + +/** + * Key-value credential patterns. + * Requires the value portion to be 8+ chars to avoid matching + * short debug strings like "token expired" or "secret: no". + */ +const CREDENTIAL_KV_PATTERNS: { pattern: RegExp; label: string }[] = [ + { + pattern: /password["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'password', + }, + { pattern: /passwd["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, label: 'passwd' }, + { pattern: /secret["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, label: 'secret' }, + { + pattern: /api[_-]?key["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'api_key', + }, + { + pattern: /auth[_-]?token["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'auth_token', + }, + { + pattern: /access[_-]?token["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'access_token', + }, + { + pattern: /refresh[_-]?token["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'refresh_token', + }, + { + pattern: /x-auth-token["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'x-auth-token', + }, + { + pattern: /authorization["'\s:=]+["']?((?:\w+\s+)?[^"',}\]]{8,})/gi, + label: 'authorization', + }, + { + pattern: /client[_-]?secret["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'client_secret', + }, + { + pattern: /private[_-]?key["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'private_key', + }, + { + pattern: /webhook[_-]?url["'\s:=]+["']?([^"'\s,}\]]{8,})/gi, + label: 'webhook_url', + }, +]; + +/** Standalone token/secret patterns (not key-value) */ +const TOKEN_PATTERNS: { + pattern: RegExp; + replacer: (match: string, ...groups: string[]) => string; +}[] = [ + // Bearer tokens + { + pattern: /bearer\s+([a-zA-Z0-9._-]{20,})/gi, + replacer: (_m, token) => `Bearer ${mask(token)}`, + }, + // JWTs: three base64url segments separated by dots + { + pattern: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]+/g, + replacer: (m) => mask(m, 6), + }, + // URLs with embedded credentials: https://user:pass@host + { + pattern: /(https?:\/\/)([^:]+):([^@]+)@/gi, + replacer: (_m, proto) => `${proto}[REDACTED]:[REDACTED]@`, + }, + // Long hex strings (32+ chars) — likely API keys, hashes, tokens + { + pattern: /\b[a-fA-F0-9]{32,}\b/g, + replacer: (m) => mask(m, 6), + }, +]; + +/** PII patterns */ +const PII_PATTERNS: { pattern: RegExp; replacer: (match: string) => string }[] = + [ + // Email addresses — require 2+ char local part, exclude npm-style package scopes + // Negative lookbehind for @ prevents matching @scope/package patterns + { + pattern: /\b[A-Za-z0-9._%+-]{2,}@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, + replacer: (m) => { + // Don't redact package scopes or internal identifiers + if ( + m.includes('/') || + m.endsWith('.js') || + m.endsWith('.ts') || + m.endsWith('.log') || + /^\d+@\d+/.test(m) + ) { + return m; + } + const [local, domain] = m.split('@'); + return `${local[0]}***@${domain}`; + }, + }, + // Credit card numbers (13-19 digits, optionally separated by spaces/dashes) + { + pattern: /\b(?:\d[ -]*?){13,19}\b/g, + replacer: (m) => { + const digits = m.replace(/[^0-9]/g, ''); + if (digits.length < 13 || digits.length > 19) return m; + if (!luhnCheck(digits)) return m; // Not a valid CC — don't redact + return `****${digits.slice(-4)}`; + }, + }, + // IP addresses (IPv4) — partial mask preserving subnet + { + pattern: /\b(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\b/g, + replacer: (m) => { + // Don't redact localhost or private ranges (RFC 1918) + if ( + m === '127.0.0.1' || + m === '0.0.0.0' || + m.startsWith('192.168.') || + m.startsWith('10.') || + /^172\.(1[6-9]|2[0-9]|3[01])\./.test(m) + ) { + return m; + } + const parts = m.split('.'); + return `${parts[0]}.${parts[1]}.*.*`; + }, + }, + ]; + +// ── Field-based redaction (applied to object keys) ─────────────────────────── + +const SENSITIVE_KEY_PATTERNS = [ + 'password', + 'passwd', + 'token', + 'secret', + 'apikey', + 'api_key', + 'authorization', + 'cookie', + 'session_id', + 'sessionid', + 'credential', + 'private_key', + 'privatekey', + 'access_key', + 'accesskey', + 'client_secret', + 'webhook', +]; + +/** Keys that are safe even though they partially match sensitive patterns */ +const SAFE_KEY_PATTERNS = [ + 'token_type', + 'tokentype', + 'token_expired', + 'token_expiry', + 'session_count', + 'password_changed', + 'password_policy', + 'webhook_enabled', +]; + +const isSensitiveKey = (key: string): boolean => { + const lower = key.toLowerCase(); + // Check safe patterns first (allowlist) + if (SAFE_KEY_PATTERNS.some((safe) => lower === safe)) return false; + return SENSITIVE_KEY_PATTERNS.some((pattern) => lower.includes(pattern)); +}; + +// ── Redux state dump detection ─────────────────────────────────────────────── + +const STATE_SIGNATURE_KEYS = [ + 'apppath', + 'appversion', + 'servers', + 'clientcertificates', + 'currentview', + 'allowedjitsiServers', + 'downloads', + 'isupdatingenabled', + 'istrayiconenabled', + 'rootwindowstate', +]; + +const isStateDump = (obj: any): boolean => { + if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { + return false; + } + const keys = Object.keys(obj).map((k) => k.toLowerCase()); + const matchCount = STATE_SIGNATURE_KEYS.filter((k) => + keys.includes(k) + ).length; + return matchCount >= 3; +}; + +// ── Core redaction functions ───────────────────────────────────────────────── + +export const redactSensitiveData = (text: string): string => { + let result = text; + + // Key-value credential patterns — replace the whole match + for (const { pattern, label } of CREDENTIAL_KV_PATTERNS) { + pattern.lastIndex = 0; + result = result.replace(pattern, `${label}: [REDACTED]`); + } + + // Standalone token patterns — use custom replacers for partial masking + for (const { pattern, replacer } of TOKEN_PATTERNS) { + pattern.lastIndex = 0; + result = result.replace(pattern, replacer); + } + + // PII patterns — use custom replacers for partial masking + for (const { pattern, replacer } of PII_PATTERNS) { + pattern.lastIndex = 0; + result = result.replace(pattern, replacer); + } + + return result; +}; + +const redactObject = (obj: any, depth = 0, seen = new WeakSet()): any => { + if (obj === null || obj === undefined) return obj; + if (typeof obj === 'string') return redactSensitiveData(obj); + + // Prevent stack overflow on deeply nested objects + if (depth >= 10) return '[Nested Object Redacted]'; + + if (typeof obj === 'object') { + if (seen.has(obj)) return '[Circular]'; + seen.add(obj); + } + + if (Array.isArray(obj)) + return obj.map((item) => redactObject(item, depth + 1, seen)); + + if (typeof obj === 'object') { + if (isStateDump(obj)) { + return '[Redux State Redacted]'; + } + if (obj.state && isStateDump(obj.state)) { + const { state: _state, ...rest } = obj; + const redactedRest = redactObject(rest, depth + 1, seen); + if (typeof redactedRest === 'object' && redactedRest !== null) { + return { ...redactedRest, state: '[Redux State Redacted]' }; + } + return { state: '[Redux State Redacted]' }; + } + + const result: any = {}; + + // Error properties (message, stack, name) are non-enumerable; + // preserve them so redacted logs still contain useful diagnostics. + if (obj instanceof Error) { + if (obj.message) result.message = redactSensitiveData(obj.message); + if (obj.stack) result.stack = redactSensitiveData(obj.stack); + if (obj.name) result.name = obj.name; + } + + for (const key of Object.keys(obj)) { + if (isSensitiveKey(key)) { + const val = obj[key]; + // Partial mask string values for debugging context + result[key] = + typeof val === 'string' && val.length > 8 ? mask(val) : '[REDACTED]'; + } else { + result[key] = redactObject(obj[key], depth + 1, seen); + } + } + return result; + } + return obj; +}; + +// ── electron-log hook ──────────────────────────────────────────────────────── + +let privacyHookFailed = false; + +export const createPrivacyHook = () => { + return (message: any, _transport: any, _transportName?: string) => { + try { + const data = Array.isArray(message.data) ? message.data : [message.data]; + const sanitizedData = data.map((item: any) => { + if (typeof item === 'string') return redactSensitiveData(item); + if (typeof item === 'object' && item !== null) + return redactObject(item); + return item; + }); + return { ...message, data: sanitizedData }; + } catch { + if (!privacyHookFailed) { + privacyHookFailed = true; + process.stderr.write( + '[privacy] redaction hook threw; falling back to placeholder\n' + ); + } + return { + level: message.level, + date: message.date, + data: ['[Privacy redaction failed]'], + }; + } + }; +}; diff --git a/src/logging/scopes.ts b/src/logging/scopes.ts new file mode 100644 index 0000000000..5f476da619 --- /dev/null +++ b/src/logging/scopes.ts @@ -0,0 +1,44 @@ +import log from 'electron-log'; + +import { getProcessContext } from './context'; + +export interface IScopedLogger { + debug: (...args: any[]) => void; + info: (...args: any[]) => void; + warn: (...args: any[]) => void; + error: (...args: any[]) => void; +} + +// Cache process context at module load (doesn't change at runtime) +const processContext = `[${getProcessContext()}]`; + +/** + * Create a scoped logger with a consistent prefix + * @example + * const logger = createScopedLogger('outlook'); + * logger.info('Sync started'); // [main] [outlook] Sync started + */ +export const createScopedLogger = (scope: string): IScopedLogger => { + const scopeStr = `[${scope}]`; + return { + debug: (...args: any[]) => log.debug(processContext, scopeStr, ...args), + info: (...args: any[]) => log.info(processContext, scopeStr, ...args), + warn: (...args: any[]) => log.warn(processContext, scopeStr, ...args), + error: (...args: any[]) => log.error(processContext, scopeStr, ...args), + }; +}; + +// Pre-defined scopes for common modules +export const loggers = { + main: createScopedLogger('main'), + app: createScopedLogger('app'), + outlook: createScopedLogger('outlook'), + auth: createScopedLogger('auth'), + ipc: createScopedLogger('ipc'), + ui: createScopedLogger('ui'), + updates: createScopedLogger('updates'), + notifications: createScopedLogger('notifications'), + videoCall: createScopedLogger('videocall'), + servers: createScopedLogger('servers'), + logViewer: createScopedLogger('logviewer'), +}; diff --git a/src/logging/utils.ts b/src/logging/utils.ts new file mode 100644 index 0000000000..5cbf943561 --- /dev/null +++ b/src/logging/utils.ts @@ -0,0 +1,46 @@ +import log from 'electron-log'; + +/** + * Log function execution time + */ +const isThenable = (value: unknown): value is Promise => + value !== null && + typeof value === 'object' && + typeof (value as Promise).then === 'function'; + +export const logExecutionTime = ( + functionName: string, + fn: () => T | Promise +): T | Promise => { + const start = Date.now(); + log.debug(`Starting execution of ${functionName}`); + + try { + const result = fn(); + + if (isThenable(result)) { + return result + .then((value) => { + const duration = Date.now() - start; + log.debug(`Completed execution of ${functionName} in ${duration}ms`); + return value; + }) + .catch((error) => { + const duration = Date.now() - start; + log.error( + `Failed execution of ${functionName} in ${duration}ms`, + error + ); + throw error; + }); + } + + const duration = Date.now() - start; + log.debug(`Completed execution of ${functionName} in ${duration}ms`); + return result; + } catch (error) { + const duration = Date.now() - start; + log.error(`Failed execution of ${functionName} in ${duration}ms`, error); + throw error; + } +}; diff --git a/src/main.spec.ts b/src/main.spec.ts new file mode 100644 index 0000000000..f95bf52035 --- /dev/null +++ b/src/main.spec.ts @@ -0,0 +1,423 @@ +import type { DownloadItem } from 'electron'; +import { webContents } from 'electron'; +import electronDl from 'electron-dl'; +import { t } from 'i18next'; + +import { handleWillDownloadEvent } from './downloads/main'; +import { createNotification } from './notifications/preload'; + +// Mock all dependencies +jest.mock('electron', () => ({ + app: { + whenReady: jest.fn(() => Promise.resolve()), + addListener: jest.fn(), + }, + webContents: { + getAllWebContents: jest.fn(() => []), + }, +})); + +jest.mock('electron-dl', () => jest.fn()); + +jest.mock('i18next', () => ({ + t: jest.fn((key: string) => key), +})); + +jest.mock('./notifications/preload', () => ({ + createNotification: jest.fn(), +})); + +jest.mock('./downloads/main', () => ({ + handleWillDownloadEvent: jest.fn(() => Promise.resolve()), + setupDownloads: jest.fn(), +})); + +// Mock all other main.ts dependencies +jest.mock('./app/main/app', () => ({ + performElectronStartup: jest.fn(), + setupApp: jest.fn(), +})); + +jest.mock('./app/main/data', () => ({ + mergePersistableValues: jest.fn(() => Promise.resolve()), + watchAndPersistChanges: jest.fn(), +})); + +jest.mock('./app/main/dev', () => ({ + setUserDataDirectory: jest.fn(), +})); + +jest.mock('./store', () => ({ + createMainReduxStore: jest.fn(), +})); + +jest.mock('./servers/main', () => ({ + setupServers: jest.fn(() => Promise.resolve()), +})); + +jest.mock('./i18n/main', () => ({ + default: { + setUp: jest.fn(), + wait: jest.fn(() => Promise.resolve()), + }, +})); + +jest.mock('./ui/main/rootWindow', () => ({ + createRootWindow: jest.fn(), + showRootWindow: jest.fn(() => Promise.resolve()), + exportLocalStorage: jest.fn(() => Promise.resolve({})), + watchMachineTheme: jest.fn(), +})); + +// Mock all other setup functions +jest.mock('./deepLinks/main', () => ({ + setupDeepLinks: jest.fn(), + processDeepLinksInArgs: jest.fn(() => Promise.resolve()), +})); + +jest.mock('./documentViewer/ipc', () => ({ + startDocumentViewerHandler: jest.fn(), +})); + +jest.mock('./errors', () => ({ + setupMainErrorHandling: jest.fn(), +})); + +jest.mock('./jitsi/ipc', () => ({ + handleJitsiDesktopCapturerGetSources: jest.fn(), +})); + +jest.mock('./navigation/main', () => ({ + setupNavigation: jest.fn(() => Promise.resolve()), +})); + +jest.mock('./notifications/main', () => ({ + setupNotifications: jest.fn(), +})); + +jest.mock('./outlookCalendar/ipc', () => ({ + startOutlookCalendarUrlHandler: jest.fn(), +})); + +jest.mock('./screenSharing/main', () => ({ + setupScreenSharing: jest.fn(), +})); + +jest.mock('./servers/cache', () => ({ + handleClearCacheDialog: jest.fn(), +})); + +jest.mock('./servers/supportedVersions/main', () => ({ + checkSupportedVersionServers: jest.fn(), +})); + +jest.mock('./spellChecking/main', () => ({ + setupSpellChecking: jest.fn(() => Promise.resolve()), +})); + +jest.mock('./ui/components/CertificatesManager/main', () => ({ + handleCertificatesManager: jest.fn(), +})); + +jest.mock('./ui/main/dock', () => ({ + default: { + setUp: jest.fn(), + tearDown: jest.fn(), + }, +})); + +jest.mock('./ui/main/menuBar', () => ({ + default: { + setUp: jest.fn(), + tearDown: jest.fn(), + }, +})); + +jest.mock('./ui/main/serverView', () => ({ + attachGuestWebContentsEvents: jest.fn(), +})); + +jest.mock('./ui/main/touchBar', () => ({ + default: { + setUp: jest.fn(), + tearDown: jest.fn(), + }, +})); + +jest.mock('./ui/main/trayIcon', () => ({ + default: { + setUp: jest.fn(), + tearDown: jest.fn(), + }, +})); + +jest.mock('./updates/main', () => ({ + setupUpdates: jest.fn(() => Promise.resolve()), +})); + +jest.mock('./userPresence/main', () => ({ + setupPowerMonitor: jest.fn(), +})); + +jest.mock('./videoCallWindow/ipc', () => ({ + handleDesktopCapturerGetSources: jest.fn(), + startVideoCallWindowHandler: jest.fn(), + cleanupVideoCallResources: jest.fn(), +})); + +describe('main.ts electron-dl integration', () => { + let electronDlMock: jest.MockedFunction; + let webContentsMock: jest.Mocked; + let handleWillDownloadEventMock: jest.MockedFunction< + typeof handleWillDownloadEvent + >; + let createNotificationMock: jest.MockedFunction; + + beforeEach(() => { + jest.clearAllMocks(); + electronDlMock = electronDl as jest.MockedFunction; + webContentsMock = webContents as jest.Mocked; + handleWillDownloadEventMock = + handleWillDownloadEvent as jest.MockedFunction< + typeof handleWillDownloadEvent + >; + createNotificationMock = createNotification as jest.MockedFunction< + typeof createNotification + >; + }); + + const createMockDownloadItem = ( + overrides: Partial = {} + ): jest.Mocked => { + return { + getFilename: jest.fn(() => 'test-file.pdf'), + getState: jest.fn(() => 'progressing'), + isPaused: jest.fn(() => false), + getReceivedBytes: jest.fn(() => 1024), + getTotalBytes: jest.fn(() => 2048), + getStartTime: jest.fn(() => 1640995200), + getURL: jest.fn(() => 'https://example.com/file.pdf'), + getMimeType: jest.fn(() => 'application/pdf'), + getSavePath: jest.fn(() => '/downloads/test-file.pdf'), + on: jest.fn(), + ...overrides, + } as unknown as jest.Mocked; + }; + + const createMockWebContents = (id = 123) => ({ + id, + isDestroyed: jest.fn(() => false), + }); + + // Test the electron-dl setup function directly + const setupElectronDlWithTracking = () => { + electronDl({ + saveAs: true, + onStarted: (item) => { + const webContentsArray = webContents.getAllWebContents(); + + let sourceWebContents = null; + for (const wc of webContentsArray) { + if (wc && !wc.isDestroyed()) { + sourceWebContents = wc; + break; + } + } + + if (sourceWebContents) { + const fakeEvent = { + defaultPrevented: false, + preventDefault: () => {}, + }; + handleWillDownloadEvent( + fakeEvent as any, + item, + sourceWebContents + ).catch(() => { + // Silently handle tracking errors + }); + } + }, + onCompleted: (file) => { + createNotification({ + title: t('downloads.title', { defaultValue: 'Downloads' }), + body: file.filename || 'Unknown file', + subtitle: t('downloads.notifications.downloadFinished'), + category: 'DOWNLOADS', + }); + }, + }); + }; + + describe('setupElectronDlWithTracking', () => { + it('should configure electron-dl with saveAs: true', () => { + setupElectronDlWithTracking(); + + expect(electronDlMock).toHaveBeenCalledWith({ + saveAs: true, + onStarted: expect.any(Function), + onCompleted: expect.any(Function), + }); + }); + + describe('onStarted callback', () => { + let onStartedCallback: (item: DownloadItem) => void; + + beforeEach(() => { + setupElectronDlWithTracking(); + const electronDlCall = electronDlMock.mock.calls[0]; + if (electronDlCall?.[0]) { + onStartedCallback = electronDlCall[0].onStarted!; + } + }); + + it('should call handleWillDownloadEvent when webContents are available', () => { + const mockItem = createMockDownloadItem(); + const mockWC = createMockWebContents(); + + webContentsMock.getAllWebContents.mockReturnValue([mockWC] as any); + + onStartedCallback(mockItem); + + expect(handleWillDownloadEventMock).toHaveBeenCalledWith( + expect.objectContaining({ + defaultPrevented: false, + preventDefault: expect.any(Function), + }), + mockItem, + mockWC + ); + }); + + it('should not call handleWillDownloadEvent when no webContents are available', () => { + const mockItem = createMockDownloadItem(); + + webContentsMock.getAllWebContents.mockReturnValue([]); + + onStartedCallback(mockItem); + + expect(handleWillDownloadEventMock).not.toHaveBeenCalled(); + }); + + it('should skip destroyed webContents', () => { + const mockItem = createMockDownloadItem(); + const destroyedWC = createMockWebContents(); + const validWC = createMockWebContents(456); + + destroyedWC.isDestroyed = jest.fn(() => true); + validWC.isDestroyed = jest.fn(() => false); + + webContentsMock.getAllWebContents.mockReturnValue([ + destroyedWC, + validWC, + ] as any); + + onStartedCallback(mockItem); + + expect(handleWillDownloadEventMock).toHaveBeenCalledWith( + expect.any(Object), + mockItem, + validWC + ); + }); + + it('should handle tracking errors silently', () => { + const mockItem = createMockDownloadItem(); + const mockWC = createMockWebContents(); + + webContentsMock.getAllWebContents.mockReturnValue([mockWC] as any); + handleWillDownloadEventMock.mockRejectedValue( + new Error('Tracking failed') + ); + + // Should not throw + expect(() => onStartedCallback(mockItem)).not.toThrow(); + }); + }); + + describe('onCompleted callback', () => { + let onCompletedCallback: (file: { filename: string }) => void; + + beforeEach(() => { + setupElectronDlWithTracking(); + const electronDlCall = electronDlMock.mock.calls[0]; + if (electronDlCall?.[0]) { + onCompletedCallback = electronDlCall[0].onCompleted! as any; + } + }); + + it('should create a notification when download completes', () => { + const mockFile = { filename: 'completed-file.pdf' }; + + onCompletedCallback(mockFile); + + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'downloads.title', + body: 'completed-file.pdf', + subtitle: 'downloads.notifications.downloadFinished', + category: 'DOWNLOADS', + }); + expect(t).toHaveBeenCalledWith('downloads.title', { + defaultValue: 'Downloads', + }); + }); + + it('should use translated subtitle', () => { + const mockFile = { filename: 'test.pdf' }; + const tMock = t as jest.MockedFunction; + tMock.mockImplementation((...args: any[]) => { + const key = args[0]; + const options = args[1]; + if (key === 'downloads.title') { + return options?.defaultValue || 'Downloads'; + } + if (key === 'downloads.notifications.downloadFinished') { + return 'Download finished'; + } + return key; + }); + + onCompletedCallback(mockFile); + + expect(tMock).toHaveBeenCalledWith( + 'downloads.notifications.downloadFinished' + ); + expect(createNotificationMock).toHaveBeenCalledWith({ + title: 'Downloads', + body: 'test.pdf', + subtitle: 'Download finished', + category: 'DOWNLOADS', + }); + expect(tMock).toHaveBeenCalledWith('downloads.title', { + defaultValue: 'Downloads', + }); + }); + }); + }); + + describe('integration behavior', () => { + it('should prioritize electron-dl handling over custom will-download listeners', () => { + setupElectronDlWithTracking(); + + // This test verifies that the electron-dl integration is set up correctly + // and that it takes precedence over any custom will-download event handling + expect(electronDlMock).toHaveBeenCalledTimes(1); + expect(electronDlMock).toHaveBeenCalledWith( + expect.objectContaining({ + saveAs: true, + }) + ); + }); + + it('should provide both download tracking and completion notifications', () => { + setupElectronDlWithTracking(); + + const electronDlConfig = electronDlMock.mock.calls[0]?.[0]; + + expect(electronDlConfig?.onStarted).toBeDefined(); + expect(electronDlConfig?.onCompleted).toBeDefined(); + expect(typeof electronDlConfig?.onStarted).toBe('function'); + expect(typeof electronDlConfig?.onCompleted).toBe('function'); + }); + }); +}); diff --git a/src/main.ts b/src/main.ts index 191e3d43d2..871fcd7e3f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,7 +1,12 @@ -import { app } from 'electron'; -import electronDl from 'electron-dl'; +import { app, shell } from 'electron'; -import { performElectronStartup, setupApp } from './app/main/app'; +import { + performElectronStartup, + setupApp, + initializeScreenCaptureFallbackState, + setupGpuCrashHandler, + markMainWindowStable, +} from './app/main/app'; import { mergePersistableValues, watchAndPersistChanges, @@ -10,12 +15,26 @@ import { setUserDataDirectory } from './app/main/dev'; import { setupDeepLinks, processDeepLinksInArgs } from './deepLinks/main'; import { startDocumentViewerHandler } from './documentViewer/ipc'; import { setupDownloads } from './downloads/main'; +import { setupElectronDlWithTracking } from './downloads/main/setup'; import { setupMainErrorHandling } from './errors'; import i18n from './i18n/main'; +import { handle } from './ipc/main'; import { handleJitsiDesktopCapturerGetSources } from './jitsi/ipc'; +import { startLogViewerWindowHandler } from './logViewerWindow/ipc'; +import { + logger, + setupWebContentsLogging, + cleanupOldLogs, + setupDebugLoggingWatch, +} from './logging'; import { setupNavigation } from './navigation/main'; +import attentionDrawing from './notifications/attentionDrawing'; import { setupNotifications } from './notifications/main'; -import { startOutlookCalendarUrlHandler } from './outlookCalendar/ipc'; +import { + startOutlookCalendarUrlHandler, + stopOutlookCalendarSync, +} from './outlookCalendar/ipc'; +import { setupOutlookLogger } from './outlookCalendar/logger'; import { setupScreenSharing } from './screenSharing/main'; import { handleClearCacheDialog } from './servers/cache'; import { setupServers } from './servers/main'; @@ -36,22 +55,40 @@ import touchBar from './ui/main/touchBar'; import trayIcon from './ui/main/trayIcon'; import { setupUpdates } from './updates/main'; import { setupPowerMonitor } from './userPresence/main'; +import { openExternal } from './utils/browserLauncher'; import { handleDesktopCapturerGetSources, startVideoCallWindowHandler, + cleanupVideoCallResources, } from './videoCallWindow/ipc'; -electronDl({ saveAs: true }); - const start = async (): Promise => { setUserDataDirectory(); + logger.info('Starting Rocket.Chat Desktop application'); + + setupWebContentsLogging(); + performElectronStartup(); + // Set up GPU crash handler BEFORE whenReady to catch early GPU failures + setupGpuCrashHandler(); + await app.whenReady(); + cleanupOldLogs(); + createMainReduxStore(); + setupOutlookLogger(); + setupDebugLoggingWatch(); + + // Initialize screen capture fallback state after store is available + initializeScreenCaptureFallbackState(); + + // Set up electron-dl with our download tracking callbacks + setupElectronDlWithTracking(); + const localStorage = await exportLocalStorage(); await mergePersistableValues(localStorage); await setupServers(localStorage); @@ -68,14 +105,14 @@ const start = async (): Promise => { attachGuestWebContentsEvents(); await showRootWindow(); - // React DevTools is currently incompatible with Electron 10 - // if (process.env.NODE_ENV === 'development') { - // installDevTools(); - // } + // Mark main window as stable - GPU crashes after this won't trigger fallback + markMainWindowStable(); watchMachineTheme(); setupNotifications(); + attentionDrawing.setUp(); setupScreenSharing(); startVideoCallWindowHandler(); + startLogViewerWindowHandler(); await setupSpellChecking(); @@ -96,6 +133,9 @@ const start = async (): Promise => { menuBar.tearDown(); touchBar.tearDown(); trayIcon.tearDown(); + attentionDrawing.tearDown(); + stopOutlookCalendarSync(); + cleanupVideoCallResources(); }); watchAndPersistChanges(); @@ -105,7 +145,37 @@ const start = async (): Promise => { startDocumentViewerHandler(); checkSupportedVersionServers(); + handle('open-external', async (_webContents, rawUrl) => { + let url: URL; + + try { + url = new URL(rawUrl); + } catch { + console.warn('Blocked malformed external URL'); + return; + } + + const { isProtocolAllowed } = await import('./navigation/main'); + + if (!(await isProtocolAllowed(url.toString()))) { + console.warn('Blocked external URL with disallowed protocol'); + return; + } + + if (url.protocol === 'http:' || url.protocol === 'https:') { + await openExternal(url.toString()); + return; + } + + await shell.openExternal(url.toString()); + }); + await processDeepLinksInArgs(); + + console.info('Application initialization completed successfully'); }; -start(); +start().catch((error) => { + logger.error('Failed to start application', error); + app.exit(1); +}); diff --git a/src/navigation/main.ts b/src/navigation/main.ts index 94a7503d2b..e56fa2fb9c 100644 --- a/src/navigation/main.ts +++ b/src/navigation/main.ts @@ -1,3 +1,4 @@ +import { X509Certificate } from 'crypto'; import fs from 'fs'; import path from 'path'; @@ -41,6 +42,18 @@ const loadUserTrustedCertificates = async (): Promise< const serializeCertificate = (certificate: Certificate): string => `${certificate.issuerName}\n${certificate.data.toString()}`; +const isHostnameValid = ( + certificate: Certificate, + hostname: string +): boolean => { + try { + const x509 = new X509Certificate(certificate.data); + return x509.checkHost(hostname) !== undefined; + } catch { + return false; + } +}; + const queuedTrustRequests = new Map< Certificate['fingerprint'], Array<(isTrusted: boolean) => void> @@ -52,8 +65,9 @@ export const setupNavigation = async (): Promise => { async (event, _webContents, requestedUrl, error, certificate, callback) => { event.preventDefault(); + const { host, hostname } = new URL(requestedUrl); + const serialized = serializeCertificate(certificate); - const { host } = new URL(requestedUrl); let trustedCertificates = select( ({ trustedCertificates }) => trustedCertificates @@ -80,6 +94,15 @@ export const setupNavigation = async (): Promise => { return; } + // Only bypass hostname mismatch errors + if ( + error === 'net::ERR_CERT_COMMON_NAME_INVALID' && + isHostnameValid(certificate, hostname) + ) { + callback(true); + return; + } + if (queuedTrustRequests.has(certificate.fingerprint)) { queuedTrustRequests.get(certificate.fingerprint)?.push(callback); return; diff --git a/src/notifications/actions.ts b/src/notifications/actions.ts index 2b5a68a2a1..636b5d2216 100644 --- a/src/notifications/actions.ts +++ b/src/notifications/actions.ts @@ -19,7 +19,12 @@ export type NotificationsActionTypeToPayloadMap = { [NOTIFICATIONS_CREATE_REQUESTED]: ExtendedNotificationOptions; [NOTIFICATIONS_CREATE_RESPONDED]: unknown; [NOTIFICATIONS_NOTIFICATION_ACTIONED]: { id: unknown; index: number }; - [NOTIFICATIONS_NOTIFICATION_CLICKED]: { id: unknown; title: string }; + [NOTIFICATIONS_NOTIFICATION_CLICKED]: { + id: unknown; + title: string; + serverUrl?: string; + category?: 'DOWNLOADS' | 'SERVER'; + }; [NOTIFICATIONS_NOTIFICATION_CLOSED]: { id: unknown }; [NOTIFICATIONS_NOTIFICATION_DISMISSED]: { id: unknown }; [NOTIFICATIONS_NOTIFICATION_REPLIED]: { id: unknown; reply: string }; diff --git a/src/notifications/attentionDrawing.ts b/src/notifications/attentionDrawing.ts new file mode 100644 index 0000000000..8dbbd82fe1 --- /dev/null +++ b/src/notifications/attentionDrawing.ts @@ -0,0 +1,86 @@ +import { app } from 'electron'; + +import { select, Service } from '../store'; +import { getRootWindow } from '../ui/main/rootWindow'; + +class AttentionDrawingService extends Service { + private activeVoiceNotifications = new Set(); + + private bounceId: number | null = null; + + public async drawAttention(notificationId: string): Promise { + if (this.activeVoiceNotifications.has(notificationId)) { + return; + } + + const { isFlashFrameEnabled } = select(({ isFlashFrameEnabled }) => ({ + isFlashFrameEnabled, + })); + + if (!isFlashFrameEnabled) { + return; + } + + try { + const browserWindow = await getRootWindow(); + if (browserWindow.isDestroyed()) { + return; + } + + this.activeVoiceNotifications.add(notificationId); + + if (process.platform === 'darwin') { + if (this.bounceId === null) { + this.bounceId = app.dock?.bounce('critical') ?? null; + } + } else { + browserWindow.flashFrame(true); + } + } catch (error) { + if (process.env.NODE_ENV === 'development') { + console.warn('Failed to draw attention:', error); + } + } + } + + public async stopAttention(notificationId: string): Promise { + if (!this.activeVoiceNotifications.has(notificationId)) { + return; + } + + this.activeVoiceNotifications.delete(notificationId); + + if (this.activeVoiceNotifications.size > 0) { + return; + } + + if (process.platform === 'darwin' && this.bounceId !== null) { + app.dock?.cancelBounce(this.bounceId); + this.bounceId = null; + } + + try { + const browserWindow = await getRootWindow(); + if (browserWindow.isDestroyed()) { + return; + } + + if (process.platform !== 'darwin') { + browserWindow.flashFrame(false); + } + } catch (error) { + if (process.env.NODE_ENV === 'development') { + console.warn('Failed to stop attention:', error); + } + } + } + + protected destroy(): void { + if (process.platform === 'darwin' && this.bounceId !== null) { + app.dock?.cancelBounce(this.bounceId); + this.bounceId = null; + } + } +} + +export default new AttentionDrawingService(); diff --git a/src/notifications/common.ts b/src/notifications/common.ts index 24bb91ef2e..a7580c8207 100644 --- a/src/notifications/common.ts +++ b/src/notifications/common.ts @@ -1,3 +1,5 @@ +export type NotificationCategory = 'DOWNLOADS' | 'SERVER'; + export type ExtendedNotificationOptions = NotificationOptions & { canReply?: boolean; title: string; @@ -6,4 +8,19 @@ export type ExtendedNotificationOptions = NotificationOptions & { title: string; }[]; renotify?: boolean; + requireInteraction?: boolean; + notificationType?: 'voice' | 'text'; + category?: NotificationCategory; +}; + +export type CustomNotificationOptions = { + type: 'voice' | 'text'; + id?: string; + payload: { + title: string; + body: string; + avatar?: string; + silent?: boolean; + requireInteraction?: boolean; + }; }; diff --git a/src/notifications/main.ts b/src/notifications/main.ts index e9cc37e974..aa44d079cc 100644 --- a/src/notifications/main.ts +++ b/src/notifications/main.ts @@ -6,6 +6,7 @@ import { dispatch, dispatchSingle, listen } from '../store'; import type { ActionIPCMeta } from '../store/actions'; import { hasMeta } from '../store/fsa'; import { getRootWindow } from '../ui/main/rootWindow'; +import { getServerUrlByWebContentsId } from '../ui/main/serverView'; import { NOTIFICATIONS_CREATE_REQUESTED, NOTIFICATIONS_CREATE_RESPONDED, @@ -16,6 +17,7 @@ import { NOTIFICATIONS_NOTIFICATION_ACTIONED, NOTIFICATIONS_NOTIFICATION_DISMISSED, } from './actions'; +import attentionDrawing from './attentionDrawing'; import type { ExtendedNotificationOptions } from './common'; const resolveIcon = async ( @@ -44,6 +46,8 @@ const resolveIcon = async ( }; const notifications = new Map(); +const notificationTypes = new Map(); +const notificationCategories = new Map(); const createNotification = async ( id: string, @@ -55,6 +59,8 @@ const createNotification = async ( silent, canReply, actions, + requireInteraction, + category, }: ExtendedNotificationOptions, ipcMeta?: ActionIPCMeta ): Promise => { @@ -69,6 +75,7 @@ const createNotification = async ( type: 'button', text: action.title, })), + ...(requireInteraction !== undefined && { requireInteraction }), }); notification.addListener('show', () => { @@ -77,6 +84,11 @@ const createNotification = async ( payload: { id }, ipcMeta, }); + + const notificationType = notificationTypes.get(id); + if (notificationType === 'voice') { + attentionDrawing.drawAttention(id); + } }); notification.addListener('close', () => { @@ -86,12 +98,29 @@ const createNotification = async ( ipcMeta, }); notifications.delete(id); + + const notificationType = notificationTypes.get(id); + if (notificationType === 'voice') { + attentionDrawing.stopAttention(id); + } + notificationTypes.delete(id); + notificationCategories.delete(id); }); notification.addListener('click', () => { + const serverUrl = + ipcMeta?.webContentsId !== undefined + ? getServerUrlByWebContentsId(ipcMeta.webContentsId) + : undefined; + const notificationCategory = notificationCategories.get(id); dispatchSingle({ type: NOTIFICATIONS_NOTIFICATION_CLICKED, - payload: { id, title }, + payload: { + id, + title, + ...(serverUrl && { serverUrl }), + ...(notificationCategory && { category: notificationCategory }), + }, ipcMeta, }); }); @@ -113,6 +142,9 @@ const createNotification = async ( }); notifications.set(id, notification); + if (category) { + notificationCategories.set(id, category); + } notification.show(); @@ -121,24 +153,61 @@ const createNotification = async ( const updateNotification = async ( id: string, - { title, body, silent, renotify }: ExtendedNotificationOptions + { + title, + body, + silent, + renotify: _renotify, + icon, + requireInteraction, + notificationType, + }: ExtendedNotificationOptions ): Promise => { const notification = notifications.get(id); - if (title) { + if (!notification) { + return id; + } + + if (title !== undefined) { notification.title = title; } - if (body) { + if (body !== undefined) { notification.body = body; } - if (silent) { + if (silent !== undefined) { notification.silent = silent; } - if (renotify) { - notification.show(); + if (icon !== undefined) { + const resolvedIcon = await resolveIcon(icon); + if (resolvedIcon) { + notification.icon = resolvedIcon; + } + } + + if (requireInteraction !== undefined) { + notification.requireInteraction = requireInteraction; + } + + let changedToVoice = false; + if (notificationType !== undefined) { + const previousType = notificationTypes.get(id); + notificationTypes.set(id, notificationType); + + if (previousType === 'voice' && notificationType !== 'voice') { + attentionDrawing.stopAttention(id); + } else if (previousType !== 'voice' && notificationType === 'voice') { + changedToVoice = true; + } + } + + notification.show(); + + if (changedToVoice) { + attentionDrawing.drawAttention(id); } return id; @@ -153,6 +222,7 @@ const handleCreateEvent = async ( } const id = tag || Math.random().toString(36).slice(2); + notificationTypes.set(id, options.notificationType || 'text'); return createNotification(id, options, ipcMeta); }; @@ -172,6 +242,14 @@ export const setupNotifications = (): void => { }); listen(NOTIFICATIONS_NOTIFICATION_DISMISSED, (action) => { - notifications.get(action.payload.id)?.close(); + const notificationId = String(action.payload.id); + const notificationType = notificationTypes.get(notificationId); + + notifications.get(notificationId)?.close(); + + if (notificationType === 'voice') { + attentionDrawing.stopAttention(notificationId); + } + notificationTypes.delete(notificationId); }); }; diff --git a/src/notifications/preload.ts b/src/notifications/preload.ts index 48a10b1aa3..9c3026d6ba 100644 --- a/src/notifications/preload.ts +++ b/src/notifications/preload.ts @@ -1,6 +1,9 @@ import { getServerUrl, getAbsoluteUrl } from '../servers/preload/urls'; import { dispatch, listen, request } from '../store'; -import { WEBVIEW_FOCUS_REQUESTED } from '../ui/actions'; +import { + SIDE_BAR_DOWNLOADS_BUTTON_CLICKED, + WEBVIEW_FOCUS_REQUESTED, +} from '../ui/actions'; import { NOTIFICATIONS_CREATE_REQUESTED, NOTIFICATIONS_CREATE_RESPONDED, @@ -11,6 +14,7 @@ import { NOTIFICATIONS_NOTIFICATION_REPLIED, NOTIFICATIONS_NOTIFICATION_SHOWN, } from './actions'; +import type { CustomNotificationOptions } from './common'; const normalizeIconUrl = (iconUrl: string): string => { if (/^data:/.test(iconUrl)) { @@ -38,6 +42,8 @@ export const createNotification = async ({ canReply?: boolean; title: string; subtitle?: string; + notificationType?: 'voice' | 'text'; + category?: 'DOWNLOADS' | 'SERVER'; onEvent?: (eventDescriptor: { type: string; detail: unknown }) => void; }): Promise => { const id = await request( @@ -68,6 +74,25 @@ export const destroyNotification = (id: unknown): void => { eventHandlers.delete(id); }; +export const dispatchCustomNotification = async ( + options: CustomNotificationOptions +): Promise => { + const { id, payload, type } = options; + const notificationId = id || Math.random().toString(36).slice(2); + return createNotification({ + title: payload.title, + body: payload.body, + icon: payload.avatar, + tag: notificationId, + requireInteraction: payload.requireInteraction, + notificationType: type, + }); +}; + +export const closeCustomNotification = (id: unknown): void => { + destroyNotification(id); +}; + export const listenToNotificationsRequests = (): void => { listen(NOTIFICATIONS_NOTIFICATION_SHOWN, (action) => { const { @@ -88,16 +113,20 @@ export const listenToNotificationsRequests = (): void => { listen(NOTIFICATIONS_NOTIFICATION_CLICKED, (action) => { const { - payload: { id, title }, + payload: { id, serverUrl, category }, } = action; - dispatch({ - type: WEBVIEW_FOCUS_REQUESTED, - payload: { - url: getServerUrl(), - view: title === 'Downloads' ? 'downloads' : 'server', - }, - }); + if (category === 'DOWNLOADS') { + dispatch({ type: SIDE_BAR_DOWNLOADS_BUTTON_CLICKED }); + } else { + dispatch({ + type: WEBVIEW_FOCUS_REQUESTED, + payload: { + url: serverUrl || getServerUrl(), + view: 'server', + }, + }); + } const eventHandler = eventHandlers.get(id); eventHandler?.({ type: 'click' }); diff --git a/src/outlookCalendar/AGENTS.md b/src/outlookCalendar/AGENTS.md new file mode 100644 index 0000000000..daa20ed9a4 --- /dev/null +++ b/src/outlookCalendar/AGENTS.md @@ -0,0 +1,56 @@ +# Outlook Calendar Module + +## Logging + +**Always use the centralized logger from `logger.ts`** - never use `console.log('[OutlookCalendar]...')` directly. + +```typescript +import { + outlookLog, + outlookDebug, + outlookError, + outlookWarn, + outlookEventDetail, +} from './logger'; + +// These respect the verbose logging toggle in Settings > Developer +outlookLog('message', data); // Only logs when verbose enabled +outlookWarn('message', data); // Only logs when verbose enabled +outlookDebug('message', data); // Only logs when verbose enabled + +// This ALWAYS logs (errors should always be visible) +outlookError('message', data); + +// This respects the detailed events logging toggle in Settings > Developer +outlookEventDetail('full event data', eventObject); // Only logs when detailed events enabled +``` + +### Why This Matters + +- Users can toggle verbose logging in Settings > Developer > Verbose Outlook Logging +- Without verbose mode, only errors are logged (cleaner console) +- The toggle persists across app restarts + +### Architecture + +```text +Redux Store (isVerboseOutlookLoggingEnabled) + ↓ watch() +global.isVerboseOutlookLoggingEnabled + ↓ checked by +outlookLog() / outlookWarn() / outlookDebug() +``` + +## Preload Script Limitation + +**`preload.ts` cannot use the verbose logging toggle** - it runs in the renderer process and doesn't share the `global.isVerboseOutlookLoggingEnabled` variable with the main process. + +Logs in `preload.ts` always appear. Keep preload logging minimal. + +## Error Classification + +Use `createClassifiedError()` from `errorClassification.ts` for user-facing errors. It provides: + +- Error categorization (network, auth, exchange, unknown) +- User-friendly messages with troubleshooting steps +- Structured error context for debugging diff --git a/src/outlookCalendar/errorClassification.ts b/src/outlookCalendar/errorClassification.ts new file mode 100644 index 0000000000..d4299856ea --- /dev/null +++ b/src/outlookCalendar/errorClassification.ts @@ -0,0 +1,350 @@ +import type { ErrorClassification, OutlookCalendarError } from './type'; + +const ERROR_PATTERNS: Array<{ + pattern: RegExp; + classification: ErrorClassification; +}> = [ + { + pattern: /Cannot read properties of undefined \(reading 'headers'\)/i, + classification: { + source: 'exchange', + severity: 'high', + code: 'EWS_NTLM_AUTH_FAILED', + userMessage: + 'Unable to connect to Exchange server. The NTLM authentication failed.', + suggestedActions: [ + 'Verify your Exchange server URL is correct', + 'Check if your username and password are valid', + 'Ensure the Exchange server supports NTLM authentication', + 'Contact your IT administrator if the issue persists', + ], + }, + }, + { + pattern: /The remote server returned an error: \(401\) Unauthorized/i, + classification: { + source: 'exchange', + severity: 'high', + code: 'EWS_UNAUTHORIZED', + userMessage: 'Your Exchange credentials are invalid or expired.', + suggestedActions: [ + 'Verify your username and password are correct', + 'Check if your account is locked or disabled', + 'Contact your IT administrator to verify Exchange access', + ], + }, + }, + { + pattern: /The remote server returned an error: \(404\) Not Found/i, + classification: { + source: 'exchange', + severity: 'high', + code: 'EWS_SERVER_NOT_FOUND', + userMessage: + 'Exchange server not found. The server URL appears to be incorrect.', + suggestedActions: [ + 'Verify the Exchange server URL is correct', + 'Check if the server is accessible from your network', + 'Try using the full EWS endpoint URL (e.g., https://mail.company.com/ews/exchange.asmx)', + ], + }, + }, + { + pattern: /(getaddrinfo ENOTFOUND|ECONNREFUSED|\btimeout\b)/i, + classification: { + source: 'network', + severity: 'high', + code: 'NETWORK_CONNECTION_FAILED', + userMessage: + 'Cannot connect to Exchange server. Network connection failed.', + suggestedActions: [ + 'Check your internet connection', + 'Verify the Exchange server URL is accessible', + 'Check if there are firewall restrictions', + 'Try connecting from a different network', + ], + }, + }, + { + pattern: + /SSL_ERROR|UNABLE_TO_VERIFY|CERT_|ERR_TLS|self\.signed|certificate\.has\.expired/i, + classification: { + source: 'network', + severity: 'medium', + code: 'SSL_CERTIFICATE_ERROR', + userMessage: + 'SSL/TLS certificate error when connecting to Exchange server.', + suggestedActions: [ + 'Check if the Exchange server has a valid SSL certificate', + 'Contact your IT administrator about certificate issues', + 'Verify the server URL uses HTTPS', + ], + }, + }, + { + pattern: /Request failed with status code 401.*rocket.*chat/i, + classification: { + source: 'rocket_chat', + severity: 'high', + code: 'RC_UNAUTHORIZED', + userMessage: 'Authentication with Rocket.Chat server failed.', + suggestedActions: [ + 'Log out and log back into Rocket.Chat', + 'Check if your Rocket.Chat session is still valid', + 'Contact your administrator if calendar integration is not enabled', + ], + }, + }, + { + pattern: /Request failed with status code 500.*rocket.*chat/i, + classification: { + source: 'rocket_chat', + severity: 'high', + code: 'RC_SERVER_ERROR', + userMessage: 'Rocket.Chat server encountered an internal error.', + suggestedActions: [ + 'Try again in a few minutes', + 'Contact your Rocket.Chat administrator', + 'Check if the calendar feature is properly configured on the server', + ], + }, + }, + { + pattern: /calendar.*not.*enabled|calendar.*feature.*disabled/i, + classification: { + source: 'rocket_chat', + severity: 'medium', + code: 'RC_CALENDAR_DISABLED', + userMessage: + 'Calendar integration is not enabled on this Rocket.Chat server.', + suggestedActions: [ + 'Contact your Rocket.Chat administrator to enable calendar integration', + 'Check if you have the necessary permissions for calendar features', + ], + }, + }, + { + pattern: /Missing required Outlook credentials/i, + classification: { + source: 'desktop_app', + severity: 'medium', + code: 'MISSING_CREDENTIALS', + userMessage: 'Outlook credentials are not configured.', + suggestedActions: [ + 'Go to Settings and configure your Outlook credentials', + 'Make sure all required fields are filled out', + ], + }, + }, + { + pattern: /Failed to decrypt credentials/i, + classification: { + source: 'desktop_app', + severity: 'medium', + code: 'CREDENTIAL_DECRYPTION_FAILED', + userMessage: 'Cannot decrypt your saved Outlook credentials.', + suggestedActions: [ + 'Try re-entering your Outlook credentials in Settings', + 'If the issue persists, clear your credentials and set them up again', + ], + }, + }, + { + pattern: /Invalid Exchange server URL configuration/i, + classification: { + source: 'configuration', + severity: 'medium', + code: 'INVALID_EXCHANGE_URL', + userMessage: 'The Exchange server URL is not properly configured.', + suggestedActions: [ + 'Check the Exchange server URL format (e.g., https://mail.company.com)', + 'Contact your IT administrator for the correct Exchange server URL', + 'Make sure the URL includes the protocol (https://)', + ], + }, + }, + { + pattern: /NTLM.*authentication.*failed/i, + classification: { + source: 'authentication', + severity: 'high', + code: 'NTLM_AUTH_FAILED', + userMessage: 'NTLM authentication with Exchange server failed.', + suggestedActions: [ + 'Verify your domain username and password', + 'Check if your domain is included in the username (DOMAIN\\username)', + 'Contact your IT administrator about NTLM authentication requirements', + ], + }, + }, +]; + +export function classifyError( + error: Error | string, + _context: Record = {} +): ErrorClassification { + const errorMessage = error instanceof Error ? error.message : String(error); + const stack = error instanceof Error ? error.stack : ''; + const fullErrorText = `${errorMessage} ${stack}`.toLowerCase(); + + for (const { pattern, classification } of ERROR_PATTERNS) { + if (pattern.test(fullErrorText)) { + return classification; + } + } + + return { + source: 'desktop_app', + severity: 'medium', + code: 'UNKNOWN_ERROR', + userMessage: + 'An unexpected error occurred during calendar synchronization.', + suggestedActions: [ + 'Try the operation again', + 'Check your network connection', + 'Restart the application if the issue persists', + 'Contact support if the problem continues', + ], + }; +} + +export function createClassifiedError( + error: Error | string, + context: Record = {} +): OutlookCalendarError { + const classification = classifyError(error, context); + const technicalMessage = + error instanceof Error ? error.message : String(error); + const timestamp = new Date().toISOString(); + + return { + ...classification, + technicalMessage, + context: { + ...context, + timestamp, + stack: error instanceof Error ? error.stack : undefined, + }, + timestamp, + }; +} + +const SENSITIVE_CONTEXT_KEYS = new Set([ + 'token', + 'password', + 'secret', + 'authorization', +]); + +const isSensitiveKey = (key: string): boolean => { + const lowerKey = key.toLowerCase(); + return ( + SENSITIVE_CONTEXT_KEYS.has(lowerKey) || + lowerKey.includes('token') || + lowerKey.includes('password') || + lowerKey.includes('secret') || + lowerKey.includes('authorization') + ); +}; + +const sanitizeContext = ( + context: Record, + seen: WeakSet = new WeakSet() +): Record => { + const sanitized: Record = {}; + for (const [key, value] of Object.entries(context)) { + if (isSensitiveKey(key)) { + sanitized[key] = '[REDACTED]'; + } else if (Array.isArray(value)) { + if (seen.has(value)) { + sanitized[key] = '[Circular]'; + } else { + seen.add(value); + sanitized[key] = value.map((item) => { + if (typeof item !== 'object' || item === null) return item; + if (seen.has(item)) return '[Circular]'; + seen.add(item); + return sanitizeContext(item as Record, seen); + }); + } + } else if (typeof value === 'object' && value !== null) { + if (seen.has(value as object)) { + sanitized[key] = '[Circular]'; + } else { + seen.add(value as object); + sanitized[key] = sanitizeContext( + value as Record, + seen + ); + } + } else { + sanitized[key] = value; + } + } + return sanitized; +}; + +export function formatErrorForLogging( + classifiedError: OutlookCalendarError, + operationContext: string +): string { + const { source, severity, code, userMessage, technicalMessage, context } = + classifiedError; + + const safeContext = context ? sanitizeContext(context) : {}; + + return `[OutlookCalendar] ${operationContext} failed - ${source.toUpperCase()} ERROR +┌─ Error Classification ───────────────────────────────────── +│ Source: ${source} +│ Severity: ${severity} +│ Code: ${code} +│ +│ User Message: ${userMessage} +│ +│ Technical Details: ${technicalMessage} +│ +│ Context: ${JSON.stringify(safeContext, null, 2).replace(/\n/g, '\n│ ')} +│ +│ Suggested Actions: +${classifiedError.suggestedActions?.map((action) => `│ • ${action}`).join('\n') || '│ • Contact support'} +└────────────────────────────────────────────────────────────`; +} + +export function generateUserFriendlyMessage( + classifiedError: OutlookCalendarError +): string { + const { source, userMessage, suggestedActions } = classifiedError; + + let sourceDescription = ''; + switch (source) { + case 'exchange': + sourceDescription = 'Exchange Server'; + break; + case 'rocket_chat': + sourceDescription = 'Rocket.Chat Server'; + break; + case 'desktop_app': + sourceDescription = 'Desktop Application'; + break; + case 'network': + sourceDescription = 'Network Connection'; + break; + case 'authentication': + sourceDescription = 'Authentication'; + break; + case 'configuration': + sourceDescription = 'Configuration'; + break; + default: + sourceDescription = 'Unknown Source'; + } + + let message = `${sourceDescription} Error: ${userMessage}`; + + if (suggestedActions && suggestedActions.length > 0) { + message += '\n\nWhat you can try:\n'; + message += suggestedActions.map((action) => `• ${action}`).join('\n'); + } + + return message; +} diff --git a/src/outlookCalendar/getOutlookEvents.spec.ts b/src/outlookCalendar/getOutlookEvents.spec.ts new file mode 100644 index 0000000000..6a4b8b8f99 --- /dev/null +++ b/src/outlookCalendar/getOutlookEvents.spec.ts @@ -0,0 +1,342 @@ +import { sanitizeExchangeUrl } from './getOutlookEvents'; + +describe('Exchange URL Sanitization', () => { + describe('Base URL scenarios', () => { + it.each([ + [ + 'https://mail.example.com', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com///', + 'https://mail.example.com/ews/exchange.asmx', + ], + ['http://mail.example.com', 'http://mail.example.com/ews/exchange.asmx'], + [ + 'https://mail.example.com:8443', + 'https://mail.example.com:8443/ews/exchange.asmx', + ], + ])('transforms base URL %s into %s', (input, expected) => { + const result = sanitizeExchangeUrl(input); + expect(result).toBe(expected); + }); + }); + + describe('URLs with /ews path', () => { + it.each([ + [ + 'https://mail.example.com/ews', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/ews/', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/ews///', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/ews', + 'https://mail.example.com/ews/exchange.asmx', + ], + ])('transforms EWS path URL %s into %s', (input, expected) => { + const result = sanitizeExchangeUrl(input); + expect(result).toBe(expected); + }); + }); + + describe('Case-insensitive scenarios', () => { + it.each([ + [ + 'https://mail.example.com/EWS', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/Ews', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/EWS/', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/EWS/EXCHANGE.ASMX', + 'https://mail.example.com/ews/exchange.asmx', + ], + ])('handles case-insensitive URL %s into %s', (input, expected) => { + const result = sanitizeExchangeUrl(input); + expect(result).toBe(expected); + }); + }); + + describe('Already complete URLs', () => { + it.each([ + [ + 'https://mail.example.com/ews/exchange.asmx', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/ews/exchange.asmx/', + 'https://mail.example.com/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/EWS/EXCHANGE.ASMX', + 'https://mail.example.com/ews/exchange.asmx', + ], + ])('preserves complete URL %s as %s', (input, expected) => { + const result = sanitizeExchangeUrl(input); + expect(result).toBe(expected); + }); + }); + + describe('URLs with subpaths', () => { + it.each([ + [ + 'https://mail.example.com/outlook', + 'https://mail.example.com/outlook/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/mail/ews', + 'https://mail.example.com/mail/ews/exchange.asmx', + ], + [ + 'https://mail.example.com/subdir/path', + 'https://mail.example.com/subdir/path/ews/exchange.asmx', + ], + ])('handles subpaths %s into %s', (input, expected) => { + const result = sanitizeExchangeUrl(input); + expect(result).toBe(expected); + }); + }); + + describe('Fallback for malformed URLs', () => { + it.each([ + ['mail.example.com', 'https://mail.example.com/ews/exchange.asmx'], + ['mail.example.com/ews', 'https://mail.example.com/ews/exchange.asmx'], + [ + 'mail.example.com:443/ews', + 'https://mail.example.com:443/ews/exchange.asmx', + ], + ])('handles URLs without protocol %s into %s', (input, expected) => { + const result = sanitizeExchangeUrl(input); + expect(result).toBe(expected); + }); + }); + + describe('Error handling', () => { + it.each([ + ['', 'Invalid server URL: must be a non-empty string'], + [null as any, 'Invalid server URL: must be a non-empty string'], + [undefined as any, 'Invalid server URL: must be a non-empty string'], + [123 as any, 'Invalid server URL: must be a non-empty string'], + ])('throws error for invalid input %s', (input, expectedError) => { + expect(() => sanitizeExchangeUrl(input)).toThrow(expectedError); + }); + }); + + describe('Real-world examples', () => { + it.each([ + // The original problematic case from the issue + [ + 'https://mail.example.com/ews', + 'https://mail.example.com/ews/exchange.asmx', + ], + + // Common Office 365 patterns + [ + 'https://outlook.office365.com/ews', + 'https://outlook.office365.com/ews/exchange.asmx', + ], + [ + 'https://outlook.office365.com', + 'https://outlook.office365.com/ews/exchange.asmx', + ], + + // On-premises Exchange patterns + [ + 'https://mail.company.com/exchange', + 'https://mail.company.com/exchange/ews/exchange.asmx', + ], + [ + 'https://exchange.company.com', + 'https://exchange.company.com/ews/exchange.asmx', + ], + + // Legacy configurations + [ + 'https://mail.company.com/ews/exchange.asmx', + 'https://mail.company.com/ews/exchange.asmx', + ], + ])('handles real-world URL %s correctly as %s', (input, expected) => { + const result = sanitizeExchangeUrl(input); + expect(result).toBe(expected); + }); + }); + + describe('URL validation and error handling', () => { + describe('Invalid URL structures', () => { + it.each([ + [ + 'invalid://domain.com', + 'Invalid protocol "invalid:". Only HTTP and HTTPS are supported', + ], + ['https://', 'URL must have a valid hostname'], + ])( + 'throws error for invalid URL %s with message containing %s', + (input, expectedErrorPart) => { + expect(() => sanitizeExchangeUrl(input)).toThrow( + new RegExp(expectedErrorPart) + ); + } + ); + + it('handles URLs without dots via fallback', () => { + const result = sanitizeExchangeUrl('not-a-url'); + expect(result).toBe('https://not-a-url/ews/exchange.asmx'); + }); + + it('handles URLs with double dots via fallback', () => { + const result = sanitizeExchangeUrl('https://bad..domain.com'); + expect(result).toBe('https://bad..domain.com/ews/exchange.asmx'); + }); + + it('handles URLs with double slashes by normalizing', () => { + const result = sanitizeExchangeUrl('https://domain.com//bad//path'); + expect(result).toBe('https://domain.com/bad/path/ews/exchange.asmx'); + }); + }); + + describe('Valid URLs with warnings', () => { + // These should work but may generate warnings + it('handles localhost URLs', () => { + const result = sanitizeExchangeUrl('http://localhost/ews'); + expect(result).toBe('http://localhost/ews/exchange.asmx'); + }); + + it('handles HTTP URLs (should warn but work)', () => { + const result = sanitizeExchangeUrl('http://exchange.company.com'); + expect(result).toBe('http://exchange.company.com/ews/exchange.asmx'); + }); + + it('handles non-standard ports', () => { + const result = sanitizeExchangeUrl('https://mail.company.com:9443'); + expect(result).toBe('https://mail.company.com:9443/ews/exchange.asmx'); + }); + }); + + describe('Fallback URL handling', () => { + it('handles URLs without protocol', () => { + const result = sanitizeExchangeUrl('mail.company.com'); + expect(result).toBe('https://mail.company.com/ews/exchange.asmx'); + }); + + it('handles partial URLs with /ews', () => { + const result = sanitizeExchangeUrl('mail.company.com/ews'); + expect(result).toBe('https://mail.company.com/ews/exchange.asmx'); + }); + + it('handles complete fallback URLs', () => { + const result = sanitizeExchangeUrl( + 'mail.company.com/ews/exchange.asmx' + ); + expect(result).toBe('https://mail.company.com/ews/exchange.asmx'); + }); + }); + + describe('Exchange-specific validation', () => { + it('validates Exchange endpoint path', () => { + const result = sanitizeExchangeUrl('https://mail.company.com'); + expect(result).toMatch(/\/ews\/exchange\.asmx$/); + }); + + it('preserves existing correct Exchange paths', () => { + const input = 'https://mail.company.com/ews/exchange.asmx'; + const result = sanitizeExchangeUrl(input); + expect(result).toBe(input); + }); + + it('corrects case in Exchange paths', () => { + const result = sanitizeExchangeUrl( + 'https://mail.company.com/EWS/EXCHANGE.ASMX' + ); + expect(result).toBe('https://mail.company.com/ews/exchange.asmx'); + }); + }); + + describe('Detailed error scenarios', () => { + it('handles input without dots via fallback', () => { + const result = sanitizeExchangeUrl('not-a-url-at-all'); + expect(result).toBe('https://not-a-url-at-all/ews/exchange.asmx'); + }); + + it('handles URLs with double dots', () => { + const result = sanitizeExchangeUrl('https://bad..domain.com'); + expect(result).toBe('https://bad..domain.com/ews/exchange.asmx'); + }); + + it('throws error for empty input', () => { + expect(() => sanitizeExchangeUrl('')).toThrow(/Invalid server URL/); + }); + }); + + describe('Edge cases with validation', () => { + it('handles URLs with query parameters', () => { + const result = sanitizeExchangeUrl( + 'https://mail.company.com/ews?test=1' + ); + expect(result).toBe( + 'https://mail.company.com/ews/exchange.asmx?test=1' + ); + }); + + it('handles URLs with fragments', () => { + const result = sanitizeExchangeUrl( + 'https://mail.company.com/ews#fragment' + ); + expect(result).toBe( + 'https://mail.company.com/ews/exchange.asmx#fragment' + ); + }); + + it('handles internationalized domain names', () => { + const result = sanitizeExchangeUrl('https://mail.münchen.de'); + expect(result).toMatch(/ews\/exchange\.asmx$/); + }); + }); + + describe('Connectivity testing features', () => { + it('handles connectivity testing gracefully when it fails', () => { + // Test that even if connectivity testing fails, the function still returns a valid URL + const result = sanitizeExchangeUrl('https://unreachable.example.com'); + expect(result).toBe( + 'https://unreachable.example.com/ews/exchange.asmx' + ); + }); + + it('runs connectivity testing automatically on this debugging branch', () => { + // Connectivity testing runs automatically, validating the URL construction works + const result = sanitizeExchangeUrl('https://mail.company.com'); + expect(result).toBe('https://mail.company.com/ews/exchange.asmx'); + }); + }); + + describe('Production scenario tests', () => { + it('handles the exact problematic URL from the original issue', () => { + const result = sanitizeExchangeUrl('https://mail.example.com/ews'); + expect(result).toBe('https://mail.example.com/ews/exchange.asmx'); + expect(result).not.toContain('//ews'); + expect(result).not.toContain('/ews/ews'); + }); + + it('handles URLs without protocol via fallback', () => { + const result = sanitizeExchangeUrl('invalid-url'); + expect(result).toBe('https://invalid-url/ews/exchange.asmx'); + }); + }); + }); +}); diff --git a/src/outlookCalendar/getOutlookEvents.ts b/src/outlookCalendar/getOutlookEvents.ts index a12701dc01..d01d7cd0b5 100644 --- a/src/outlookCalendar/getOutlookEvents.ts +++ b/src/outlookCalendar/getOutlookEvents.ts @@ -16,16 +16,266 @@ import { LegacyFreeBusyStatus, } from 'ews-javascript-api'; +import { + createClassifiedError, + formatErrorForLogging, +} from './errorClassification'; +import { + outlookLog, + outlookError, + outlookWarn, + outlookEventDetail, +} from './logger'; import type { OutlookCredentials, AppointmentData } from './type'; +/** + * Optional function to test basic connectivity to the Exchange server + * This is not called by default to avoid network delays, but can be enabled for debugging + */ +const testExchangeConnectivity = async ( + exchangeUrl: string, + timeoutMs: number = 5000 +): Promise => { + try { + outlookLog('Testing connectivity to Exchange server:', exchangeUrl); + + // Extract base URL for connectivity test + const url = new URL(exchangeUrl); + const baseUrl = `${url.protocol}//${url.host}`; + + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), timeoutMs); + + const response = await fetch(baseUrl, { + method: 'HEAD', + signal: controller.signal, + // Don't follow redirects for this test + redirect: 'manual', + }); + + clearTimeout(timeoutId); + + // Any response (including errors) indicates the server is reachable + outlookLog('Connectivity test result:', { + url: baseUrl, + status: response.status, + reachable: true, + }); + + return true; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + outlookWarn('Connectivity test failed:', { + url: exchangeUrl, + error: errorMessage, + note: 'This may be normal if the server requires authentication or has CORS restrictions', + }); + + return false; + } +}; + +// Export the connectivity test function for manual testing/debugging +export const testExchangeServerConnectivity = testExchangeConnectivity; + +/** + * Builds the EWS endpoint pathname on a URL object + * Normalizes path segments and ensures proper /ews/exchange.asmx suffix + */ +const buildEwsPathname = (url: URL): void => { + const pathSegments = url.pathname + .split('/') + .filter((segment) => segment.length > 0); + + // Only strip trailing 'ews' and 'exchange.asmx' segments + while (pathSegments.length > 0) { + const last = pathSegments[pathSegments.length - 1].toLowerCase(); + if (last === 'ews' || last === 'exchange.asmx') { + pathSegments.pop(); + } else { + break; + } + } + + pathSegments.push('ews', 'exchange.asmx'); + url.pathname = `/${pathSegments.join('/')}`; +}; + +/** + * Sanitizes and constructs a proper Exchange Web Services (EWS) URL + * Handles various input formats and edge cases to prevent URL construction errors + * + * Features: + * - Native URL() constructor for robust parsing and validation + * - Intelligent path segment handling to prevent duplication + * - Support for various URL formats (base URLs, /ews paths, complete URLs) + * - Case-insensitive path normalization + * - Smart fallback for malformed URLs with protocol detection + * - Automatic connectivity testing for debugging + * - Extensive logging for troubleshooting + * + * @param serverUrl - The server URL from configuration (can be base URL or include EWS path) + * @returns A properly formatted EWS endpoint URL + * + * Examples: + * - 'https://mail.example.com' → 'https://mail.example.com/ews/exchange.asmx' + * - 'https://mail.example.com/' → 'https://mail.example.com/ews/exchange.asmx' + * - 'https://mail.example.com/ews' → 'https://mail.example.com/ews/exchange.asmx' + * - 'https://mail.example.com/ews/' → 'https://mail.example.com/ews/exchange.asmx' + * - 'https://mail.example.com/EWS' → 'https://mail.example.com/ews/exchange.asmx' + * - 'https://mail.example.com/ews/exchange.asmx' → 'https://mail.example.com/ews/exchange.asmx' + * + * Debugging: + * - Connectivity testing runs automatically on this debugging branch + * - Check console logs for detailed validation information + * - Use testExchangeServerConnectivity() function for manual connectivity testing + */ +export const sanitizeExchangeUrl = (serverUrl: string): string => { + if (!serverUrl || typeof serverUrl !== 'string') { + throw new Error('Invalid server URL: must be a non-empty string'); + } + + outlookLog('Starting URL sanitization for:', serverUrl); + + try { + // Use URL constructor for parsing and validation + const url = new URL(serverUrl); + + // Validate protocol + if (!['http:', 'https:'].includes(url.protocol)) { + throw new Error( + `Invalid protocol "${url.protocol}". Only HTTP and HTTPS are supported` + ); + } + + // Validate hostname + if (!url.hostname) { + throw new Error('URL must have a valid hostname'); + } + + // Normalize and construct EWS path using URL properties + buildEwsPathname(url); + + const sanitizedUrl = url.toString(); + + outlookLog('URL sanitization completed:', { + input: serverUrl, + output: sanitizedUrl, + protocol: url.protocol, + hostname: url.hostname, + pathname: url.pathname, + }); + + if (!url.pathname.endsWith('/ews/exchange.asmx')) { + throw new Error('Failed to construct proper EWS endpoint URL'); + } + + return sanitizedUrl; + } catch (error) { + // Enhanced fallback using URL constructor more intelligently + outlookWarn('URL parsing failed, attempting fallback method:', { + originalUrl: serverUrl, + error: error instanceof Error ? error.message : String(error), + }); + + // Try to fix common URL issues + let fallbackUrl = serverUrl.trim(); + + // Add protocol if missing + if (!fallbackUrl.match(/^https?:\/\//)) { + fallbackUrl = `https://${fallbackUrl}`; + outlookLog('Added default HTTPS protocol'); + } + + // Remove multiple slashes (except after protocol) + fallbackUrl = fallbackUrl.replace(/([^:]\/)\/+/g, '$1'); + + try { + const url = new URL(fallbackUrl); + + // Validate basic requirements + if (!url.hostname) { + throw new Error('URL must have a valid hostname'); + } + + // Use the same clean path construction as main logic + buildEwsPathname(url); + + const finalUrl = url.toString(); + + outlookLog('Fallback URL construction successful:', { + input: serverUrl, + fallback: fallbackUrl, + output: finalUrl, + protocol: url.protocol, + hostname: url.hostname, + pathname: url.pathname, + }); + + return finalUrl; + } catch (fallbackError) { + const errorMessage = `Failed to create valid Exchange URL from "${serverUrl}". + Original error: ${error instanceof Error ? error.message : String(error)}. + Fallback error: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}. + Please ensure the URL includes a valid protocol (http:// or https://) and hostname.`; + + outlookError('URL sanitization failed completely:', { + input: serverUrl, + originalError: error instanceof Error ? error.message : String(error), + fallbackError: + fallbackError instanceof Error + ? fallbackError.message + : String(fallbackError), + }); + + throw new Error(errorMessage); + } + } +}; + export const getOutlookEvents = async ( credentials: OutlookCredentials, - date: Date + date: Date, + allowInsecure: boolean = false ): Promise => { + outlookLog('Starting getOutlookEvents', { + userId: credentials.userId, + serverUrl: credentials.serverUrl, + date: date.toISOString(), + hasLogin: !!credentials.login, + hasPassword: !!credentials.password, + }); + try { const { login, password, serverUrl } = credentials; - const xhrApi = new XhrApi({ decompress: true }); + // Validate required credentials + if (!login || !password || !serverUrl) { + const error = new Error('Missing required Outlook credentials'); + const classifiedError = createClassifiedError(error, { + operation: 'credential_validation', + hasLogin: !!login, + hasPassword: !!password, + hasServerUrl: !!serverUrl, + userId: credentials.userId, + }); + + outlookError( + formatErrorForLogging(classifiedError, 'Credential validation') + ); + throw error; + } + + outlookLog('Initializing Exchange connection'); + // When allowInsecure is true, bypass SSL certificate validation + // for air-gapped environments with self-signed/internal CA certificates + const xhrApi = new XhrApi({ + decompress: true, + rejectUnauthorized: !allowInsecure, + }); + if (allowInsecure) { + outlookWarn('SSL certificate validation disabled (allowInsecure=true)'); + } xhrApi.useNtlmAuthentication(login, password); ConfigurationApi.ConfigureXHR(xhrApi); @@ -33,9 +283,29 @@ export const getOutlookEvents = async ( const exchange = new ExchangeService(ExchangeVersion.Exchange2013); // This credentials object isn't used when ntlm is active, but the lib still requires it. exchange.Credentials = new WebCredentials(login, password); - exchange.Url = new Uri(`${serverUrl}/ews/exchange.asmx`); + + try { + const exchangeUrl = sanitizeExchangeUrl(serverUrl); + exchange.Url = new Uri(exchangeUrl); + outlookLog('Exchange URL set:', exchangeUrl); + } catch (error) { + const classifiedError = createClassifiedError(error as Error, { + operation: 'exchange_url_configuration', + serverUrl, + userId: credentials.userId, + }); + + outlookError( + formatErrorForLogging(classifiedError, 'Exchange URL configuration') + ); + + throw new Error( + `Invalid Exchange server URL configuration: ${classifiedError.technicalMessage}` + ); + } const validatedDate = new Date(date); + outlookLog('Searching for appointments on:', validatedDate.toDateString()); const folderId = new FolderId(WellKnownFolderName.Calendar); const minTime = new DateTime( @@ -56,11 +326,32 @@ export const getOutlookEvents = async ( let appointments: Appointment[] = []; try { - appointments = (await exchange.FindAppointments(folderId, view)) - .Items as Appointment[]; + outlookLog('Fetching appointments from Exchange server...'); + const findResult = await exchange.FindAppointments(folderId, view); + appointments = findResult.Items as Appointment[]; + outlookLog('Found', appointments.length, 'appointments'); + outlookEventDetail( + 'Raw Exchange appointments count:', + appointments.length + ); } catch (error) { - console.error(error); - return Promise.reject(error); + const classifiedError = createClassifiedError(error as Error, { + operation: 'fetch_appointments', + serverUrl: credentials.serverUrl, + userId: credentials.userId, + exchangeUrl: exchange.Url?.ToString(), + }); + + outlookError( + formatErrorForLogging( + classifiedError, + 'Fetch appointments from Exchange' + ) + ); + + throw new Error( + `Failed to fetch appointments: ${classifiedError.technicalMessage}` + ); } // Filter out appointments that end exactly at midnight const filtered = appointments.filter( @@ -73,18 +364,38 @@ export const getOutlookEvents = async ( const propertySet = new PropertySet(BasePropertySet.FirstClassProperties); try { + outlookLog('Loading properties for', filtered.length, 'appointments'); await exchange.LoadPropertiesForItems(filtered, propertySet); + outlookLog('Successfully loaded appointment properties'); } catch (error) { - return Promise.reject(error); + const classifiedError = createClassifiedError(error as Error, { + operation: 'load_appointment_properties', + appointmentCount: filtered.length, + serverUrl: credentials.serverUrl, + userId: credentials.userId, + }); + + outlookError( + formatErrorForLogging(classifiedError, 'Load appointment properties') + ); + + throw new Error( + `Failed to load appointment properties: ${classifiedError.technicalMessage}` + ); } - return filtered.map((appointment) => { + outlookLog('Processing', filtered.length, 'appointments'); + return filtered.map((appointment, index) => { let description = ''; try { if (appointment.Body?.Text) { description = appointment.Body.Text; } - } catch { + } catch (error) { + outlookWarn( + `Failed to get description for appointment ${index}:`, + error + ); // Ignore errors when the appointment body is missing. } @@ -93,22 +404,53 @@ export const getOutlookEvents = async ( const isBusy = appointment.LegacyFreeBusyStatus === LegacyFreeBusyStatus.Busy; - return { - id: appointment.Id.UniqueId, - subject: appointment.Subject, - startTime: appointment.Start.ToISOString(), - endTime: appointment.End.ToISOString(), - description, - isAllDay: appointment.IsAllDayEvent ?? false, - isCanceled: appointment.IsCancelled ?? false, - meetingUrl: appointment.JoinOnlineMeetingUrl ?? undefined, - reminderMinutesBeforeStart: - appointment.ReminderMinutesBeforeStart ?? undefined, - busy: isBusy, - }; + try { + const appointmentData = { + id: appointment.Id.UniqueId, + subject: appointment.Subject, + startTime: appointment.Start.ToISOString(), + endTime: appointment.End.ToISOString(), + description, + isAllDay: appointment.IsAllDayEvent ?? false, + isCanceled: appointment.IsCancelled ?? false, + meetingUrl: appointment.JoinOnlineMeetingUrl ?? undefined, + reminderMinutesBeforeStart: + appointment.ReminderMinutesBeforeStart ?? undefined, + busy: isBusy, + }; + + outlookLog(`Processed appointment ${index + 1}/${filtered.length}:`, { + id: appointmentData.id, + subject: appointmentData.subject, + startTime: appointmentData.startTime, + busy: appointmentData.busy, + }); + outlookEventDetail(`Mapped appointment ${index + 1}:`, appointmentData); + + return appointmentData; + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + outlookError(`Failed to process appointment ${index}:`, { + error: errorMessage, + appointmentId: appointment.Id?.UniqueId, + subject: appointment.Subject, + }); + throw new Error(`Failed to process appointment: ${errorMessage}`); + } }); } catch (error) { - console.error(error); - return Promise.reject(error); + const classifiedError = createClassifiedError(error as Error, { + operation: 'get_outlook_events', + serverUrl: credentials.serverUrl, + userId: credentials.userId, + date: date.toISOString(), + }); + + outlookError(formatErrorForLogging(classifiedError, 'Get Outlook Events')); + + throw new Error( + `Outlook calendar sync failed: ${classifiedError.technicalMessage}` + ); } }; diff --git a/src/outlookCalendar/ipc.ts b/src/outlookCalendar/ipc.ts index 5600e463ee..42b6f19327 100644 --- a/src/outlookCalendar/ipc.ts +++ b/src/outlookCalendar/ipc.ts @@ -1,10 +1,12 @@ +import https from 'https'; + import axios from 'axios'; -import { safeStorage } from 'electron'; +import { safeStorage, webContents } from 'electron'; import { selectPersistableValues } from '../app/selectors'; import { handle } from '../ipc/main'; import type { Server } from '../servers/common'; -import { dispatch, request, select } from '../store'; +import { dispatch, request, select, watch } from '../store'; import * as urls from '../urls'; import { meetsMinimumVersion } from '../utils'; import { @@ -13,13 +15,34 @@ import { OUTLOOK_CALENDAR_DIALOG_DISMISSED, OUTLOOK_CALENDAR_SAVE_CREDENTIALS, } from './actions'; +import { + createClassifiedError, + formatErrorForLogging, + generateUserFriendlyMessage, +} from './errorClassification'; import { getOutlookEvents } from './getOutlookEvents'; +import { + outlookLog, + outlookError, + outlookWarn, + outlookEventDetail, +} from './logger'; import type { OutlookCredentials, AppointmentData, OutlookEventsResponse, + RocketChatCalendarEvent, + RocketChatEventsResponse, } from './type'; +type CrudOperationResult = { + success: boolean; + error?: Error; +}; + +const AXIOS_TIMEOUT_MS = 10_000; +const INITIAL_SYNC_DEBOUNCE_MS = 100; + const getServerInformationByWebContentsId = (webContentsId: number): Server => { const { servers } = select(selectPersistableValues); const server = servers.find( @@ -42,51 +65,154 @@ function checkIfCredentialsAreNotEmpty( function encryptedCredentials( credentials: OutlookCredentials ): OutlookCredentials { - return { - ...credentials, - login: safeStorage.encryptString(credentials.login).toString('base64'), - password: safeStorage + outlookLog('Encrypting credentials for user:', credentials.userId); + try { + if (!safeStorage.isEncryptionAvailable()) { + outlookWarn( + 'Encryption not available, storing credentials in plain text' + ); + return credentials; + } + + const encryptedLogin = safeStorage + .encryptString(credentials.login) + .toString('base64'); + const encryptedPassword = safeStorage .encryptString(credentials.password) - .toString('base64'), - }; + .toString('base64'); + + outlookLog('Successfully encrypted credentials'); + return { + ...credentials, + login: encryptedLogin, + password: encryptedPassword, + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + outlookError('Failed to encrypt credentials:', { + error: errorMessage, + userId: credentials.userId, + hasLogin: !!credentials.login, + hasPassword: !!credentials.password, + }); + throw new Error(`Failed to encrypt credentials: ${errorMessage}`); + } } +const isLikelyBase64 = (str: string): boolean => { + if (!str || str.length < 4) return false; + return /^[A-Za-z0-9+/]+=*$/.test(str) && str.length % 4 === 0; +}; + function decryptedCredentials( credentials: OutlookCredentials ): OutlookCredentials { - return { - ...credentials, - login: safeStorage + outlookLog('Decrypting credentials for user:', credentials.userId); + + if (!safeStorage.isEncryptionAvailable()) { + outlookWarn('Encryption not available, using credentials as plaintext'); + return credentials; + } + + const loginLooksEncrypted = isLikelyBase64(credentials.login); + const passwordLooksEncrypted = isLikelyBase64(credentials.password); + + if (!loginLooksEncrypted || !passwordLooksEncrypted) { + outlookWarn( + 'Credentials do not appear to be encrypted (legacy/plaintext), using as-is', + { userId: credentials.userId } + ); + return credentials; + } + + try { + const decryptedLogin = safeStorage .decryptString(Buffer.from(credentials.login, 'base64')) - .toString(), - password: safeStorage + .toString(); + const decryptedPassword = safeStorage .decryptString(Buffer.from(credentials.password, 'base64')) - .toString(), - }; + .toString(); + + outlookLog('Successfully decrypted credentials'); + return { + ...credentials, + login: decryptedLogin, + password: decryptedPassword, + }; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + outlookWarn( + 'Failed to decrypt credentials, falling back to plaintext (legacy credentials)', + { userId: credentials.userId, error: errorMessage } + ); + return credentials; + } +} + +/** + * Creates an HTTPS agent that bypasses certificate validation. + * Used for air-gapped environments with self-signed or internal CA certificates. + */ +function createInsecureHttpsAgent(): https.Agent { + return new https.Agent({ rejectUnauthorized: false }); } async function listEventsFromRocketChatServer( serverUrl: string, userId: string, - token: string + token: string, + httpsAgent?: https.Agent ) { + const url = urls.server(serverUrl).calendarEvents.list; + outlookLog('Fetching events from Rocket.Chat server:', { + url, + userId, + hasToken: !!token, + }); + try { - const response = await axios.get( - urls.server(serverUrl).calendarEvents.list, - { - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': token, - 'X-User-Id': userId, - }, - params: { - date: new Date().toISOString(), - }, - } - ); + const response = await axios.get(url, { + headers: { + 'Content-Type': 'application/json', + 'X-Auth-Token': token, + 'X-User-Id': userId, + }, + params: { + date: new Date().toISOString(), + }, + timeout: AXIOS_TIMEOUT_MS, + httpsAgent, + }); + + outlookLog('Successfully fetched events from server:', { + statusCode: response.status, + eventCount: response.data?.data?.length || 0, + }); + outlookEventDetail('RC server events response:', response.data); + return response.data; } catch (error) { - console.error('Error sending message:', error); + const classifiedError = createClassifiedError(error as Error, { + operation: 'fetch_events_from_rocket_chat', + url, + userId, + isAxiosError: axios.isAxiosError(error), + status: axios.isAxiosError(error) ? error.response?.status : undefined, + statusText: axios.isAxiosError(error) + ? error.response?.statusText + : undefined, + responseData: axios.isAxiosError(error) + ? error.response?.data + : undefined, + }); + + outlookError( + formatErrorForLogging( + classifiedError, + 'Fetch events from Rocket.Chat server' + ) + ); + return null; } } @@ -94,14 +220,22 @@ async function createEventOnRocketChatServer( serverUrl: string, userId: string, token: string, - event: AppointmentData -) { + event: AppointmentData, + httpsAgent?: https.Agent +): Promise { + const url = urls.server(serverUrl).calendarEvents.import; + outlookLog('Creating event on Rocket.Chat server:', { + url, + eventId: event.id, + subject: event.subject, + userId, + }); + try { // Get server object to check version const { servers } = select(selectPersistableValues); const server = servers.find((server) => server.url === serverUrl); - // Base payload const payload: Record = { externalId: event.id, subject: event.subject, @@ -114,17 +248,54 @@ async function createEventOnRocketChatServer( if (server?.version && meetsMinimumVersion(server.version, '7.5.0')) { payload.endTime = event.endTime; payload.busy = event.busy; + outlookLog('Including endTime and busy status (server version >= 7.5.0)'); } - await axios.post(urls.server(serverUrl).calendarEvents.import, payload, { + outlookEventDetail('Create event payload:', payload); + + const response = await axios.post(url, payload, { headers: { 'Content-Type': 'application/json', 'X-Auth-Token': token, 'X-User-Id': userId, }, + timeout: AXIOS_TIMEOUT_MS, + httpsAgent, + }); + + outlookLog('Successfully created event:', { + eventId: event.id, + statusCode: response.status, + responseData: response.data, }); + outlookEventDetail('Create event response:', response.data); + return { success: true }; } catch (error) { - console.error('Error saving event on server:', error); + if (axios.isAxiosError(error)) { + outlookError('Axios error creating event:', { + url, + eventId: event.id, + status: error.response?.status, + statusText: error.response?.statusText, + message: error.message, + code: error.code, + userId, + }); + outlookLog('Verbose error details (create):', { + subject: event.subject, + responseData: error.response?.data, + }); + } else { + const errorMessage = + error instanceof Error ? error.message : String(error); + outlookError('Non-axios error creating event:', { + url, + eventId: event.id, + error: errorMessage, + userId, + }); + } + return { success: false, error: error as Error }; } } @@ -133,14 +304,23 @@ async function updateEventOnRocketChatServer( userId: string, token: string, rocketChatEventId: string, - event: AppointmentData -) { + event: AppointmentData, + httpsAgent?: https.Agent +): Promise { + const url = urls.server(serverUrl).calendarEvents.update; + outlookLog('Updating event on Rocket.Chat server:', { + url, + rocketChatEventId, + eventId: event.id, + subject: event.subject, + userId, + }); + try { // Get server object to check version const { servers } = select(selectPersistableValues); const server = servers.find((server) => server.url === serverUrl); - // Base payload const payload: Record = { eventId: rocketChatEventId, subject: event.subject, @@ -153,17 +333,59 @@ async function updateEventOnRocketChatServer( if (server?.version && meetsMinimumVersion(server.version, '7.5.0')) { payload.endTime = event.endTime; payload.busy = event.busy; + outlookLog( + 'Including endTime and busy status for update (server version >= 7.5.0)' + ); } - await axios.post(urls.server(serverUrl).calendarEvents.update, payload, { + outlookEventDetail('Update event payload:', payload); + + const response = await axios.post(url, payload, { headers: { 'Content-Type': 'application/json', 'X-Auth-Token': token, 'X-User-Id': userId, }, + timeout: AXIOS_TIMEOUT_MS, + httpsAgent, + }); + + outlookLog('Successfully updated event:', { + rocketChatEventId, + eventId: event.id, + statusCode: response.status, + responseData: response.data, }); + outlookEventDetail('Update event response:', response.data); + return { success: true }; } catch (error) { - console.error('Error updating event on server:', error); + if (axios.isAxiosError(error)) { + outlookError('Axios error updating event:', { + url, + rocketChatEventId, + eventId: event.id, + status: error.response?.status, + statusText: error.response?.statusText, + message: error.message, + code: error.code, + userId, + }); + outlookLog('Verbose error details (update):', { + subject: event.subject, + responseData: error.response?.data, + }); + } else { + const errorMessage = + error instanceof Error ? error.message : String(error); + outlookError('Non-axios error updating event:', { + url, + rocketChatEventId, + eventId: event.id, + error: errorMessage, + userId, + }); + } + return { success: false, error: error as Error }; } } @@ -171,11 +393,19 @@ async function deleteEventOnRocketChatServer( serverUrl: string, userId: string, token: string, - rocketChatEventId: string -) { + rocketChatEventId: string, + httpsAgent?: https.Agent +): Promise { + const url = urls.server(serverUrl).calendarEvents.delete; + outlookLog('Deleting event from Rocket.Chat server:', { + url, + rocketChatEventId, + userId, + }); + try { - await axios.post( - urls.server(serverUrl).calendarEvents.delete, + const response = await axios.post( + url, { eventId: rocketChatEventId, }, @@ -185,66 +415,332 @@ async function deleteEventOnRocketChatServer( 'X-Auth-Token': token, 'X-User-Id': userId, }, + timeout: AXIOS_TIMEOUT_MS, + httpsAgent, } ); + + outlookLog('Successfully deleted event:', { + rocketChatEventId, + statusCode: response.status, + responseData: response.data, + }); + outlookEventDetail('Delete event response:', response.data); + return { success: true }; } catch (error) { - console.error('Error deleting event on server:', error); + if (axios.isAxiosError(error)) { + outlookError('Axios error deleting event:', { + url, + rocketChatEventId, + status: error.response?.status, + statusText: error.response?.statusText, + message: error.message, + code: error.code, + userId, + }); + outlookLog('Verbose error details (delete):', { + responseData: error.response?.data, + }); + } else { + const errorMessage = + error instanceof Error ? error.message : String(error); + outlookError('Non-axios error deleting event:', { + url, + rocketChatEventId, + error: errorMessage, + userId, + }); + } + return { success: false, error: error as Error }; } } +/** + * Synchronizes Outlook calendar events with the Rocket.Chat server. + * + * This function implements sync coalescing: when a sync is already in progress, + * subsequent sync requests are queued. Only the LAST queued sync actually runs, + * as it contains the most recent state. Earlier queued syncs resolve immediately + * since their changes would be superseded by the final sync. + * + * This prevents redundant API calls when multiple sync triggers fire in quick + * succession (e.g., during app startup or rapid credential updates). + * + * @param serverUrl - The Rocket.Chat server URL + * @param credentials - Outlook Exchange credentials + * @param token - Rocket.Chat authentication token + */ export async function syncEventsWithRocketChatServer( serverUrl: string, credentials: OutlookCredentials, - token: string + token: string, + allowInsecure: boolean = false ) { - if (!checkIfCredentialsAreNotEmpty(credentials)) return; - const eventsOnOutlookServer = await getOutlookEvents( - credentials, - new Date(Date.now()) + // Validate token before doing anything else + if (!token || typeof token !== 'string') { + throw new Error( + 'Authentication required - please log in to Rocket.Chat first' + ); + } + + const state = getServerState(serverUrl); + + // Check if sync is already in progress + if (state.isSyncInProgress) { + outlookLog('Sync already in progress, queueing this request'); + + // Queue this sync request to run after current sync completes + return new Promise((resolve, reject) => { + state.syncQueue.push({ + run: async () => { + if (!token || typeof token !== 'string') { + throw new Error( + 'Authentication required - please log in to Rocket.Chat first' + ); + } + await performSync(serverUrl, credentials, token, allowInsecure); + }, + resolve, + reject, + }); + }); + } + + // Set lock + state.isSyncInProgress = true; + + try { + await performSync(serverUrl, credentials, token, allowInsecure); + + // Process queued syncs - loop until queue is truly empty + // (new syncs can be queued while lastSync.run() executes) + while (state.syncQueue.length > 0) { + outlookLog(`Processing ${state.syncQueue.length} queued sync requests`); + // Only process the last sync request (most recent state) + const lastSync = state.syncQueue[state.syncQueue.length - 1]; + // Resolve all other queued syncs since the last one will cover them + const skippedSyncs = state.syncQueue.slice(0, -1); + state.syncQueue = []; + + try { + // eslint-disable-next-line no-await-in-loop -- Must drain queue sequentially + await lastSync.run(); + lastSync.resolve(); + for (const skipped of skippedSyncs) { + skipped.resolve(); + } + } catch (error) { + outlookError('Queued sync failed:', error); + lastSync.reject(error); + for (const skipped of skippedSyncs) { + skipped.reject(error); + } + } + } + } finally { + // Release lock after all syncs complete + state.isSyncInProgress = false; + } +} + +// eslint-disable-next-line complexity +async function performSync( + serverUrl: string, + credentials: OutlookCredentials, + token: string, + allowInsecure: boolean = false +) { + outlookLog( + 'Starting Outlook calendar synchronization for server:', + serverUrl ); - const eventsOnRocketChatServer = await listEventsFromRocketChatServer( + if (!checkIfCredentialsAreNotEmpty(credentials)) return; + + outlookLog('Starting sync with Rocket.Chat server:', { serverUrl, - credentials.userId, - token - ); + userId: credentials.userId, + hasToken: !!token, + }); + + // Validate input parameters + if (!serverUrl || typeof serverUrl !== 'string') { + throw new Error('Invalid server URL provided'); + } + + if (!credentials || typeof credentials !== 'object') { + throw new Error('Invalid credentials provided'); + } + + // Token is already validated in syncEventsWithRocketChatServer + + // Create a single HTTPS agent instance to reuse across all requests in this sync + const httpsAgent = allowInsecure ? createInsecureHttpsAgent() : undefined; + + let eventsOnOutlookServer: AppointmentData[]; + let eventsOnRocketChatServer: RocketChatEventsResponse | null; + + try { + outlookLog('Fetching events from Outlook server...'); + eventsOnOutlookServer = await getOutlookEvents( + credentials, + new Date(Date.now()), + allowInsecure + ); + outlookLog( + 'Found', + eventsOnOutlookServer.length, + 'events on Outlook server' + ); + + outlookLog('Fetching events from Rocket.Chat server...'); + eventsOnRocketChatServer = await listEventsFromRocketChatServer( + serverUrl, + credentials.userId, + token, + httpsAgent + ); + + // If we can't fetch events from the server, propagate the failure + if (!eventsOnRocketChatServer) { + throw new Error( + 'Failed to fetch events from Rocket.Chat server - sync cannot proceed' + ); + } + + outlookLog( + 'Found', + eventsOnRocketChatServer?.data?.length || 0, + 'events on Rocket.Chat server' + ); + } catch (error) { + const classifiedError = createClassifiedError(error as Error, { + operation: 'fetch_events_during_sync', + serverUrl, + userId: credentials.userId, + outlookServerUrl: credentials.serverUrl, + }); + + outlookError( + formatErrorForLogging(classifiedError, 'Fetch events during sync') + ); + + throw new Error( + `Sync failed during event fetching: ${classifiedError.technicalMessage}` + ); + } + + // Get server object to check version + const { servers } = select(selectPersistableValues); + const server = servers.find((server) => server.url === serverUrl); const appointmentsFound = eventsOnOutlookServer.map( (appointment) => appointment.id ); const externalEventsFromRocketChatServer = - eventsOnRocketChatServer?.data.filter( + eventsOnRocketChatServer?.data?.filter( ({ externalId }: { externalId?: string }) => externalId - ); + ) || []; - // Get server object to check version - const { servers } = select(selectPersistableValues); - const server = servers.find((server) => server.url === serverUrl); + // Check for and clean up duplicate events + const eventsByExternalId = new Map(); + for (const event of externalEventsFromRocketChatServer) { + if (!eventsByExternalId.has(event.externalId!)) { + eventsByExternalId.set(event.externalId!, []); + } + eventsByExternalId.get(event.externalId!)!.push(event); + } + + // Collect all duplicate deletion promises + const allDeletionPromises: Promise[] = []; + + // Remove duplicates - keep only the first occurrence + for (const [externalId, events] of eventsByExternalId) { + if (events.length > 1) { + outlookLog( + `Found ${events.length} duplicate events for externalId: ${externalId}` + ); + outlookLog('Keeping first event, deleting duplicates'); + + // Delete all duplicates except the first one + const deletionPromises = events.slice(1).map(async (duplicateEvent) => { + outlookLog('Deleting duplicate event:', { + rocketChatId: duplicateEvent._id, + externalId: duplicateEvent.externalId, + subject: duplicateEvent.subject, + }); + + try { + await deleteEventOnRocketChatServer( + serverUrl, + credentials.userId, + token, + duplicateEvent._id, + httpsAgent + ); + } catch (error) { + outlookError('Failed to delete duplicate event:', { + eventId: duplicateEvent._id, + error: error instanceof Error ? error.message : String(error), + }); + } + }); + + allDeletionPromises.push(...deletionPromises); + } + } + + // Wait for all duplicate deletions to complete + if (allDeletionPromises.length > 0) { + outlookLog('Waiting for duplicate deletion to complete...'); + await Promise.all(allDeletionPromises); + outlookLog('Duplicate deletion completed'); + } + + outlookLog( + 'Starting sync loop for', + eventsOnOutlookServer.length, + 'Outlook events' + ); + + let syncCreated = 0; + let syncUpdated = 0; + let syncSkipped = 0; for await (const appointment of eventsOnOutlookServer) { try { - const alreadyOnRocketChatServer = externalEventsFromRocketChatServer.find( - ({ externalId }: { externalId?: string }) => - externalId === appointment.id - ); + outlookLog('Processing appointment:', { + id: appointment.id, + subject: appointment.subject, + startTime: appointment.startTime, + }); + + // Use the deduplicated map to find the event (only first occurrence kept) + const eventsForThisAppointment = + eventsByExternalId.get(appointment.id) || []; + const alreadyOnRocketChatServer = eventsForThisAppointment[0]; const { subject, startTime, description, reminderMinutesBeforeStart } = appointment; // If the appointment is not in the rocket.chat calendar for today, add it. if (!alreadyOnRocketChatServer) { - createEventOnRocketChatServer( + outlookLog('Creating new event in Rocket.Chat:', appointment.id); + outlookEventDetail('New event from Exchange:', appointment); + const createResult = await createEventOnRocketChatServer( serverUrl, credentials.userId, token, - appointment + appointment, + httpsAgent ); + if (createResult.success) syncCreated++; continue; } // If nothing on the event has changed, do nothing. - if ( + const hasChanges = !( alreadyOnRocketChatServer.subject === subject && alreadyOnRocketChatServer.startTime === startTime && alreadyOnRocketChatServer.description === description && @@ -254,112 +750,352 @@ export async function syncEventsWithRocketChatServer( !meetsMinimumVersion(server.version, '7.5.0') || (alreadyOnRocketChatServer.endTime === appointment.endTime && alreadyOnRocketChatServer.busy === appointment.busy)) - ) { + ); + + if (!hasChanges) { + outlookLog('No changes detected for event:', appointment.id); + outlookEventDetail('Unchanged event comparison:', { + outlookEvent: appointment, + rcEvent: alreadyOnRocketChatServer, + }); + syncSkipped++; continue; } // If the appointment is in the rocket.chat calendar for today, but something has changed, update it. - await updateEventOnRocketChatServer( + outlookLog('Updating existing event in Rocket.Chat:', { + rocketChatId: alreadyOnRocketChatServer._id, + outlookId: appointment.id, + }); + outlookEventDetail( + 'Event changed — before (RC):', + alreadyOnRocketChatServer + ); + outlookEventDetail('Event changed — after (Outlook):', appointment); + + const updateResult = await updateEventOnRocketChatServer( serverUrl, credentials.userId, token, alreadyOnRocketChatServer._id, - appointment + appointment, + httpsAgent ); + if (updateResult.success) syncUpdated++; } catch (error) { - console.error('Error syncing event:', error); + const errorMessage = + error instanceof Error ? error.message : String(error); + outlookError('Error syncing individual event:', { + appointmentId: appointment.id, + error: errorMessage, + }); + outlookLog('Verbose error details (sync event):', { + subject: appointment.subject, + }); + // Continue with other events even if one fails } } - if (!eventsOnRocketChatServer.data.length) { + if (!eventsOnRocketChatServer?.data?.length) { + outlookLog('No events on Rocket.Chat server to check for deletion'); return; } - for await (const event of eventsOnRocketChatServer.data) { - if (!event.externalId || appointmentsFound.includes(event.externalId)) { - continue; - } + outlookLog('Checking for events to delete from Rocket.Chat server'); + const eventsToDelete = eventsOnRocketChatServer.data.filter( + (event: RocketChatCalendarEvent) => + event.externalId && !appointmentsFound.includes(event.externalId) + ); + if (eventsToDelete.length === 0) { + outlookLog('No events need to be deleted'); + } else { + outlookLog('Found', eventsToDelete.length, 'events to delete'); + } + + for await (const event of eventsToDelete) { try { + outlookLog('Deleting event from Rocket.Chat:', { + rocketChatId: event._id, + externalId: event.externalId, + subject: event.subject, + }); + await deleteEventOnRocketChatServer( serverUrl, credentials.userId, token, - event._id + event._id, + httpsAgent ); - } catch (e) { - console.error(e); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + outlookError('Error deleting individual event:', { + eventId: event._id, + externalId: event.externalId, + error: errorMessage, + }); + outlookLog('Verbose error details (delete sync):', { + subject: event.subject, + }); + // Continue with other deletions even if one fails } } + + const syncDeleted = eventsToDelete.length; + outlookLog('Sync completed successfully'); + outlookEventDetail('Sync summary:', { + created: syncCreated, + updated: syncUpdated, + deleted: syncDeleted, + unchanged: syncSkipped, + totalOutlookEvents: eventsOnOutlookServer.length, + totalRcEvents: eventsOnRocketChatServer?.data?.length || 0, + }); } -let recurringSyncTaskId: NodeJS.Timeout; -let userAPIToken: string; +type QueuedSync = { + run: () => Promise; + resolve: () => void; + reject: (error: unknown) => void; +}; + +type ServerSyncState = { + recurringSyncTaskId?: NodeJS.Timeout; + userAPIToken?: string; + initialSyncTimeoutId?: NodeJS.Timeout; + restartDebounceTimer?: NodeJS.Timeout; + serverRef?: Server; + isSyncInProgress: boolean; + syncQueue: QueuedSync[]; +}; + +const serverSyncStates = new Map(); + +const getServerState = (serverUrl: string): ServerSyncState => { + if (!serverSyncStates.has(serverUrl)) { + serverSyncStates.set(serverUrl, { + isSyncInProgress: false, + syncQueue: [], + }); + } + return serverSyncStates.get(serverUrl)!; +}; + +const clearServerState = (serverUrl: string): void => { + const state = serverSyncStates.get(serverUrl); + if (state) { + if (state.recurringSyncTaskId) { + clearInterval(state.recurringSyncTaskId); + } + if (state.initialSyncTimeoutId) { + clearTimeout(state.initialSyncTimeoutId); + } + if (state.restartDebounceTimer) { + clearTimeout(state.restartDebounceTimer); + } + serverSyncStates.delete(serverUrl); + } +}; async function maybeSyncEvents(serverToSync: Server) { - if (!userAPIToken) throw new Error('No user token'); - if (!serverToSync.webContentsId) throw new Error('No webContentsId'); - const server = getServerInformationByWebContentsId( - serverToSync.webContentsId - ); - if (!server.outlookCredentials) throw new Error('No credentials'); - const credentials = safeStorage.isEncryptionAvailable() - ? decryptedCredentials(server.outlookCredentials) - : server.outlookCredentials; + outlookLog('Starting maybeSyncEvents for server:', serverToSync.url); + + const state = getServerState(serverToSync.url); - if (!checkIfCredentialsAreNotEmpty(credentials)) - throw new Error('Credentials are empty'); try { - await syncEventsWithRocketChatServer(server.url, credentials, userAPIToken); - console.log('Recurring task executed successfully'); - } catch (e) { - console.error('Error sending events to server', e); + if (!state.userAPIToken) { + throw new Error('No user API token available'); + } + + if (!serverToSync.webContentsId) { + throw new Error('No webContentsId available for server'); + } + + const server = getServerInformationByWebContentsId( + serverToSync.webContentsId + ); + if (!server?.url) { + throw new Error('Server information not found'); + } + + if (!server.outlookCredentials) { + throw new Error('No Outlook credentials configured for server'); + } + + outlookLog('Decrypting credentials...'); + const credentials = safeStorage.isEncryptionAvailable() + ? decryptedCredentials(server.outlookCredentials) + : server.outlookCredentials; + + if (!checkIfCredentialsAreNotEmpty(credentials)) { + throw new Error('Outlook credentials are empty or invalid'); + } + + const allowInsecureOutlookConnections = select( + (state) => state.allowInsecureOutlookConnections + ); + + outlookLog('Starting sync with server:', server.url); + await syncEventsWithRocketChatServer( + server.url, + credentials, + state.userAPIToken, + allowInsecureOutlookConnections + ); + outlookLog('Sync task completed successfully'); + } catch (error) { + const classifiedError = createClassifiedError(error as Error, { + operation: 'maybe_sync_events', + serverUrl: serverToSync.url, + hasToken: !!state.userAPIToken, + webContentsId: serverToSync.webContentsId, + }); + + outlookError(formatErrorForLogging(classifiedError, 'Maybe sync events')); + throw error; } } async function recurringSyncTask(serverToSync: Server) { + const state = getServerState(serverToSync.url); + try { - console.log('Executing recurring task'); + outlookLog('Executing recurring sync task for server:', serverToSync.url); await maybeSyncEvents(serverToSync); + outlookLog('Recurring sync task completed successfully'); } catch (error) { - console.error('Error occurred:', error); - clearInterval(recurringSyncTaskId); + const classifiedError = createClassifiedError(error as Error, { + operation: 'recurring_sync_task', + serverUrl: serverToSync.url, + }); + + outlookError(formatErrorForLogging(classifiedError, 'Recurring sync task')); + + outlookLog('User-friendly error message for recurring sync:'); + outlookLog(generateUserFriendlyMessage(classifiedError)); + + outlookLog('Stopping recurring sync due to persistent errors'); + if (state.recurringSyncTaskId) { + clearInterval(state.recurringSyncTaskId); + state.recurringSyncTaskId = undefined; + } } } function startRecurringSyncTask(server: Server) { - if (!userAPIToken) return; - recurringSyncTaskId = setInterval( + const state = getServerState(server.url); + + if (!state.userAPIToken) return; + + // Clear any existing recurring sync task to prevent duplicates + if (state.recurringSyncTaskId) { + outlookLog('Clearing existing recurring sync task'); + clearInterval(state.recurringSyncTaskId); + } + + const intervalMinutes = select( + (state) => + state.outlookCalendarSyncIntervalOverride ?? + state.outlookCalendarSyncInterval + ); + state.serverRef = server; + state.recurringSyncTaskId = setInterval( () => recurringSyncTask(server), - 60 * 60 * 1000 - ); // minutes * seconds * milliseconds + intervalMinutes * 60 * 1000 + ); } export const startOutlookCalendarUrlHandler = (): void => { handle('outlook-calendar/set-user-token', async (event, token, userId) => { - userAPIToken = token; - const server = getServerInformationByWebContentsId(event.id); - if (!server) return; - const { outlookCredentials } = server; - if (!outlookCredentials) return; - if (outlookCredentials.userId !== userId || !userAPIToken) return; - if (!checkIfCredentialsAreNotEmpty(outlookCredentials)) return; - startRecurringSyncTask(server); + outlookLog('Setting user token for webContents:', event.id); - setImmediate(() => { - try { - maybeSyncEvents(server); - } catch (e) { - console.error('Failed to sync outlook events on startup.', e); + try { + if (!token || typeof token !== 'string') { + outlookError('Invalid token provided'); + return; } - }); + + if (!userId || typeof userId !== 'string') { + outlookError('Invalid userId provided'); + return; + } + + const server = getServerInformationByWebContentsId(event.id); + if (!server?.url) { + outlookError('Server not found for webContents:', event.id); + return; + } + + const state = getServerState(server.url); + + const { outlookCredentials } = server; + if (!outlookCredentials) { + outlookLog('No Outlook credentials configured for server:', server.url); + return; + } + + if (outlookCredentials.userId !== userId) { + outlookLog('User ID mismatch - credentials are for different user'); + return; + } + + state.userAPIToken = token; + outlookLog('User API token set successfully'); + + if (!state.userAPIToken) { + outlookError('User API token is empty'); + return; + } + + if (!checkIfCredentialsAreNotEmpty(outlookCredentials)) { + outlookLog('Outlook credentials are empty'); + return; + } + + outlookLog('Starting recurring sync task for server:', server.url); + startRecurringSyncTask(server); + + // Cancel any pending initial sync to prevent duplicates + if (state.initialSyncTimeoutId) { + outlookLog('Cancelling pending initial sync'); + clearTimeout(state.initialSyncTimeoutId); + } + + // Perform initial sync with debounce to prevent duplicate calls + state.initialSyncTimeoutId = setTimeout(() => { + outlookLog('Executing initial sync'); + maybeSyncEvents(server).catch((error) => { + const errorMessage = + error instanceof Error ? error.message : String(error); + outlookError('Failed to sync outlook events on startup:', { + serverUrl: server.url, + userId, + error: errorMessage, + }); + }); + }, INITIAL_SYNC_DEBOUNCE_MS); + } catch (error) { + const errorMessage = + error instanceof Error ? error.message : String(error); + outlookError('Error in set-user-token handler:', { + webContentsId: event.id, + userId, + error: errorMessage, + }); + } }); handle('outlook-calendar/clear-credentials', async (event) => { const server = getServerInformationByWebContentsId(event.id); - if (!server) return; + if (!server?.url) return; const { outlookCredentials } = server; if (!outlookCredentials) return; + + // Clear server sync state (stops recurring sync, clears timers) + clearServerState(server.url); + dispatch({ type: OUTLOOK_CALENDAR_SAVE_CREDENTIALS, payload: { @@ -400,16 +1136,13 @@ export const startOutlookCalendarUrlHandler = (): void => { } ); - handle( - 'outlook-calendar/has-credentials', - async (event): Promise> => { - const server = getServerInformationByWebContentsId(event.id); - if (!server) return false; - const { outlookCredentials } = server; - if (!outlookCredentials) return false; - return checkIfCredentialsAreNotEmpty(outlookCredentials); - } - ); + handle('outlook-calendar/has-credentials', async (event) => { + const server = getServerInformationByWebContentsId(event.id); + if (!server) return false; + const { outlookCredentials } = server; + if (!outlookCredentials) return false; + return checkIfCredentialsAreNotEmpty(outlookCredentials); + }); handle( 'outlook-calendar/get-events', @@ -425,6 +1158,8 @@ export const startOutlookCalendarUrlHandler = (): void => { return Promise.reject(new Error('No credentials')); } + const state = getServerState(server.url); + let credentials: OutlookCredentials; let saveCredentials = false; if (!checkIfCredentialsAreNotEmpty(outlookCredentials)) { @@ -459,15 +1194,76 @@ export const startOutlookCalendarUrlHandler = (): void => { : outlookCredentials; } + const allowInsecureOutlookConnections = select( + (state) => state.allowInsecureOutlookConnections + ); + try { + // Check if user is logged in before attempting sync + if (!state.userAPIToken) { + outlookWarn('Manual sync attempted but token not available', { + serverUrl: server.url, + userId: outlookCredentials.userId, + hint: 'Token is set by injected script when: 1) User is logged in, 2) Outlook is enabled in settings, 3) Exchange URL is configured', + }); + + // Try to get the token from the webContents + outlookLog('Attempting to fetch token from webContents...'); + const contents = webContents.fromId(event.id); + if (contents) { + try { + const token = await contents.executeJavaScript( + `Meteor._localStorage?.getItem('Meteor.loginToken')` + ); + if (token) { + outlookLog('Successfully retrieved token from webContents'); + state.userAPIToken = token; // Store it for future use + // Continue with sync using the retrieved token + await syncEventsWithRocketChatServer( + server.url, + credentials, + token, + allowInsecureOutlookConnections + ); + return { status: 'success' }; + } + } catch (error) { + outlookError('Failed to get token from webContents:', error); + } + } + + return Promise.reject( + new Error( + 'Authentication token not yet available. Please ensure: 1) You are logged into Rocket.Chat, 2) Outlook Calendar is enabled in settings, 3) Exchange URL is configured.' + ) + ); + } + await syncEventsWithRocketChatServer( server.url, credentials, - userAPIToken + state.userAPIToken, + allowInsecureOutlookConnections ); } catch (e) { - console.error('Error syncing events with Rocket.Chat server', e); - return Promise.reject(e); + const classifiedError = createClassifiedError(e as Error, { + operation: 'sync_events_with_rocket_chat', + serverUrl: server.url, + userId: credentials.userId, + hasToken: !!state.userAPIToken, + }); + + outlookError( + formatErrorForLogging( + classifiedError, + 'Sync events with Rocket.Chat server' + ) + ); + + outlookLog('User-friendly error message:'); + outlookLog(generateUserFriendlyMessage(classifiedError)); + + throw e; } if (saveCredentials) { @@ -487,4 +1283,35 @@ export const startOutlookCalendarUrlHandler = (): void => { }; } ); + + watch( + (state) => + state.outlookCalendarSyncIntervalOverride ?? + state.outlookCalendarSyncInterval, + (curr, prev) => { + if (prev === undefined || curr === prev) return; + outlookLog( + `Outlook sync interval changed to ${curr} minutes, rescheduling sync jobs` + ); + for (const [, state] of serverSyncStates) { + if (!state.serverRef || !state.userAPIToken) continue; + clearTimeout(state.restartDebounceTimer); + const server = state.serverRef; + state.restartDebounceTimer = setTimeout(async () => { + try { + await maybeSyncEvents(server); + } catch (e) { + outlookError('Error syncing after interval change:', e); + } + startRecurringSyncTask(server); + }, 10000); + } + } + ); +}; + +export const stopOutlookCalendarSync = (): void => { + for (const [serverUrl] of serverSyncStates) { + clearServerState(serverUrl); + } }; diff --git a/src/outlookCalendar/logger.ts b/src/outlookCalendar/logger.ts new file mode 100644 index 0000000000..62cfcf0795 --- /dev/null +++ b/src/outlookCalendar/logger.ts @@ -0,0 +1,76 @@ +import { select, watch } from '../store'; + +declare global { + // eslint-disable-next-line no-var + var isVerboseOutlookLoggingEnabled: boolean; + // eslint-disable-next-line no-var + var isDetailedEventsLoggingEnabled: boolean; +} + +global.isVerboseOutlookLoggingEnabled = false; +global.isDetailedEventsLoggingEnabled = false; + +const prefix = '[OutlookCalendar]'; +const eventDetailPrefix = '[OutlookCalendar:Events]'; + +export const setupOutlookLogger = (): void => { + global.isVerboseOutlookLoggingEnabled = select( + ({ isVerboseOutlookLoggingEnabled }) => isVerboseOutlookLoggingEnabled + ); + + global.isDetailedEventsLoggingEnabled = select( + ({ isDetailedEventsLoggingEnabled }) => isDetailedEventsLoggingEnabled + ); + + watch( + ({ isVerboseOutlookLoggingEnabled }) => isVerboseOutlookLoggingEnabled, + (enabled) => { + global.isVerboseOutlookLoggingEnabled = enabled; + } + ); + + watch( + ({ isDetailedEventsLoggingEnabled }) => isDetailedEventsLoggingEnabled, + (enabled) => { + global.isDetailedEventsLoggingEnabled = enabled; + } + ); +}; + +export const outlookLog = (...args: unknown[]): void => { + if (global.isVerboseOutlookLoggingEnabled) { + console.info(prefix, ...args); + } +}; + +export const outlookDebug = (...args: unknown[]): void => { + if (global.isVerboseOutlookLoggingEnabled) { + console.info(prefix, ...args); + } +}; + +export const outlookInfo = (...args: unknown[]): void => { + if (global.isVerboseOutlookLoggingEnabled) { + console.info(prefix, ...args); + } +}; + +export const outlookWarn = (...args: unknown[]): void => { + if (global.isVerboseOutlookLoggingEnabled) { + console.warn(prefix, ...args); + } +}; + +export const outlookError = (message: unknown, ...details: unknown[]): void => { + if (global.isVerboseOutlookLoggingEnabled) { + console.error(prefix, message, ...details); + } else { + console.error(prefix, message); + } +}; + +export const outlookEventDetail = (...args: unknown[]): void => { + if (global.isDetailedEventsLoggingEnabled) { + console.info(eventDetailPrefix, ...args); + } +}; diff --git a/src/outlookCalendar/preload.ts b/src/outlookCalendar/preload.ts index a001d03b55..de514b0293 100644 --- a/src/outlookCalendar/preload.ts +++ b/src/outlookCalendar/preload.ts @@ -5,24 +5,112 @@ import type { OutlookEventsResponse } from './type'; export const getOutlookEvents = async ( date: Date ): Promise => { - const response = await ipcRenderer.invoke( - 'outlook-calendar/get-events', - date + console.info( + '[OutlookCalendar] Preload: Getting Outlook events for date:', + date.toISOString() ); - return response; + + try { + const response = await ipcRenderer.invoke( + 'outlook-calendar/get-events', + date + ); + console.info( + '[OutlookCalendar] Preload: Successfully got Outlook events response:', + response + ); + return response; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.error('[OutlookCalendar] Preload: Failed to get Outlook events:', { + date: date.toISOString(), + error: errorMessage, + }); + throw new Error(`Failed to get Outlook events: ${errorMessage}`); + } }; export const setOutlookExchangeUrl = (url: string, userId: string): void => { - ipcRenderer.invoke('outlook-calendar/set-exchange-url', url, userId); + console.info('[OutlookCalendar] Preload: Setting Exchange URL:', { + url, + userId, + }); + + ipcRenderer + .invoke('outlook-calendar/set-exchange-url', url, userId) + .then(() => { + console.info('[OutlookCalendar] Preload: Successfully set Exchange URL'); + }) + .catch((error: unknown) => { + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error('[OutlookCalendar] Preload: Failed to set Exchange URL:', { + url, + userId, + error: errorMessage, + }); + }); }; -export const hasOutlookCredentials = async (): Promise => - ipcRenderer.invoke('outlook-calendar/has-credentials'); +export const hasOutlookCredentials = async (): Promise => { + console.info( + '[OutlookCalendar] Preload: Checking if Outlook credentials exist' + ); + + try { + const result = await ipcRenderer.invoke('outlook-calendar/has-credentials'); + console.info( + '[OutlookCalendar] Preload: Credentials check result:', + result + ); + return result; + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + console.error( + '[OutlookCalendar] Preload: Failed to check credentials:', + errorMessage + ); + return false; + } +}; export const clearOutlookCredentials = (): void => { - ipcRenderer.invoke('outlook-calendar/clear-credentials'); + console.info('[OutlookCalendar] Preload: Clearing Outlook credentials'); + + ipcRenderer + .invoke('outlook-calendar/clear-credentials') + .then(() => { + console.info( + '[OutlookCalendar] Preload: Successfully cleared credentials' + ); + }) + .catch((error: unknown) => { + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error( + '[OutlookCalendar] Preload: Failed to clear credentials:', + errorMessage + ); + }); }; export const setUserToken = (token: string, userId: string): void => { - ipcRenderer.invoke('outlook-calendar/set-user-token', token, userId); + console.info( + '[OutlookCalendar] Preload: Setting user token for userId:', + userId + ); + + ipcRenderer + .invoke('outlook-calendar/set-user-token', token, userId) + .then(() => { + console.info('[OutlookCalendar] Preload: Successfully set user token'); + }) + .catch((error: unknown) => { + const errorMessage = + error instanceof Error ? error.message : String(error); + console.error('[OutlookCalendar] Preload: Failed to set user token:', { + userId, + error: errorMessage, + }); + }); }; diff --git a/src/outlookCalendar/reducers/allowInsecureOutlookConnections.ts b/src/outlookCalendar/reducers/allowInsecureOutlookConnections.ts new file mode 100644 index 0000000000..cfa47c4cba --- /dev/null +++ b/src/outlookCalendar/reducers/allowInsecureOutlookConnections.ts @@ -0,0 +1,33 @@ +import type { Reducer } from 'redux'; + +import { APP_SETTINGS_LOADED } from '../../app/actions'; +import type { ActionOf } from '../../store/actions'; + +type AllowInsecureOutlookConnectionsAction = ActionOf< + typeof APP_SETTINGS_LOADED +>; + +/** + * Controls whether to bypass SSL certificate validation for Outlook calendar connections. + * This setting is intended for air-gapped environments where Exchange servers use + * self-signed certificates or internal CA certificates that are not in the system trust store. + * + * Configurable via overridden-settings.json: + * { "allowInsecureOutlookConnections": true } + * + * Defaults to false (secure connections with certificate validation). + */ +export const allowInsecureOutlookConnections: Reducer< + boolean, + AllowInsecureOutlookConnectionsAction +> = (state = false, action) => { + switch (action.type) { + case APP_SETTINGS_LOADED: { + const { allowInsecureOutlookConnections = state } = action.payload; + return allowInsecureOutlookConnections; + } + + default: + return state; + } +}; diff --git a/src/outlookCalendar/reducers/outlookCalendarSyncInterval.ts b/src/outlookCalendar/reducers/outlookCalendarSyncInterval.ts new file mode 100644 index 0000000000..cd1aebaeb4 --- /dev/null +++ b/src/outlookCalendar/reducers/outlookCalendarSyncInterval.ts @@ -0,0 +1,27 @@ +import type { Reducer } from 'redux'; + +import { APP_SETTINGS_LOADED } from '../../app/actions'; +import type { ActionOf } from '../../store/actions'; +import { SETTINGS_SET_OUTLOOK_CALENDAR_SYNC_INTERVAL_CHANGED } from '../../ui/actions'; + +type OutlookCalendarSyncIntervalAction = + | ActionOf + | ActionOf; + +export const outlookCalendarSyncInterval: Reducer< + number, + OutlookCalendarSyncIntervalAction +> = (state = 60, action) => { + switch (action.type) { + case SETTINGS_SET_OUTLOOK_CALENDAR_SYNC_INTERVAL_CHANGED: + return action.payload; + + case APP_SETTINGS_LOADED: { + const { outlookCalendarSyncInterval = state } = action.payload; + return outlookCalendarSyncInterval; + } + + default: + return state; + } +}; diff --git a/src/outlookCalendar/reducers/outlookCalendarSyncIntervalOverride.ts b/src/outlookCalendar/reducers/outlookCalendarSyncIntervalOverride.ts new file mode 100644 index 0000000000..95285f85c0 --- /dev/null +++ b/src/outlookCalendar/reducers/outlookCalendarSyncIntervalOverride.ts @@ -0,0 +1,23 @@ +import type { Reducer } from 'redux'; + +import { APP_SETTINGS_LOADED } from '../../app/actions'; +import type { ActionOf } from '../../store/actions'; + +type OutlookCalendarSyncIntervalOverrideAction = ActionOf< + typeof APP_SETTINGS_LOADED +>; + +export const outlookCalendarSyncIntervalOverride: Reducer< + number | null, + OutlookCalendarSyncIntervalOverrideAction +> = (state = null, action) => { + switch (action.type) { + case APP_SETTINGS_LOADED: { + const { outlookCalendarSyncIntervalOverride = state } = action.payload; + return outlookCalendarSyncIntervalOverride ?? null; + } + + default: + return state; + } +}; diff --git a/src/outlookCalendar/type.ts b/src/outlookCalendar/type.ts index 23122ef614..95eee827e9 100644 --- a/src/outlookCalendar/type.ts +++ b/src/outlookCalendar/type.ts @@ -22,3 +22,63 @@ export type AppointmentData = { }; export type OutlookEventsResponse = { status: 'success' | 'canceled' }; + +export type ErrorSource = + | 'exchange' + | 'rocket_chat' + | 'desktop_app' + | 'network' + | 'authentication' + | 'configuration'; + +export type ErrorSeverity = 'low' | 'medium' | 'high' | 'critical'; + +export type OutlookCalendarError = { + source: ErrorSource; + severity: ErrorSeverity; + code: string; + technicalMessage: string; + userMessage: string; + context: Record; + timestamp: string; + suggestedActions?: string[]; +}; + +export type ErrorClassification = { + source: ErrorSource; + severity: ErrorSeverity; + code: string; + userMessage: string; + suggestedActions?: string[]; +}; + +/** + * Calendar event as stored in Rocket.Chat server + */ +export type RocketChatCalendarEvent = { + _id: string; + externalId?: string; + subject: string; + startTime: string; + endTime?: string; + description?: string; + reminderMinutesBeforeStart?: number; + meetingUrl?: string; + busy?: boolean; +}; + +/** + * Response from Rocket.Chat calendar events list endpoint + */ +export type RocketChatEventsResponse = { + success: boolean; + data: RocketChatCalendarEvent[]; +}; + +/** + * Response from Rocket.Chat calendar event create/update endpoint + */ +export type RocketChatEventResponse = { + success: boolean; + event?: RocketChatCalendarEvent; +}; diff --git a/src/preload.ts b/src/preload.ts index d9940bc461..f61560ad4e 100644 --- a/src/preload.ts +++ b/src/preload.ts @@ -5,7 +5,6 @@ import type { JitsiMeetElectronAPI } from './jitsi/preload'; import { JitsiMeetElectron } from './jitsi/preload'; import { listenToNotificationsRequests } from './notifications/preload'; import { listenToScreenSharingRequests } from './screenSharing/preload'; -import type { RocketChatDesktopAPI } from './servers/preload/api'; import { RocketChatDesktop } from './servers/preload/api'; import { setServerUrl } from './servers/preload/urls'; import { createRendererReduxStore, listen } from './store'; @@ -15,11 +14,13 @@ import { listenToMessageBoxEvents } from './ui/preload/messageBox'; import { handleTrafficLightsSpacing } from './ui/preload/sidebar'; import { whenReady } from './whenReady'; +// Import and apply console override for the main renderer process +import './logging/preload'; + declare global { // eslint-disable-next-line @typescript-eslint/naming-convention interface Window { JitsiMeetElectron: JitsiMeetElectronAPI; - RocketChatDesktop: RocketChatDesktopAPI; } } @@ -29,16 +30,25 @@ contextBridge.exposeInMainWorld('JitsiMeetElectron', JitsiMeetElectron); contextBridge.exposeInMainWorld('RocketChatDesktop', RocketChatDesktop); let retryCount = 0; +let startInProgress = false; const start = async (): Promise => { + if (startInProgress) { + return; + } + startInProgress = true; console.log('[Rocket.Chat Desktop] Preload.ts start fired'); const serverUrl = await invoke('server-view/get-url'); - if (retryCount > 5) return; + if (retryCount > 5) { + startInProgress = false; + return; + } if (!serverUrl) { console.log('[Rocket.Chat Desktop] serverUrl is not defined'); console.log('[Rocket.Chat Desktop] Preload start - retrying in 1 seconds'); + startInProgress = false; setTimeout(start, 1000); retryCount += 1; return; @@ -77,3 +87,4 @@ const start = async (): Promise => { console.log('[Rocket.Chat Desktop] waiting for window load'); window.addEventListener('load', start); +window.addEventListener('DOMContentLoaded', start); diff --git a/src/public/error.css b/src/public/error.css new file mode 100644 index 0000000000..2b0f5467ab --- /dev/null +++ b/src/public/error.css @@ -0,0 +1,102 @@ +#error-overlay-root { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #2f343d; + color: white; + z-index: 1000; + align-items: center; + justify-content: center; + flex-direction: column; + font-family: system-ui; +} + +#error-overlay-root.show { + display: flex; +} + +.error-background { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + opacity: 0.1; + background-image: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMTAwIDEwMEw1MCA1MEwxMDAgNTBMNTAgMTAweiIgZmlsbD0id2hpdGUiLz48L3N2Zz4='); + background-repeat: repeat; +} + +.error-content { + position: relative; + z-index: 1; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +.error-title { + font-size: 1.5rem; + font-weight: 700; + letter-spacing: 0; + line-height: 2rem; + margin: 0; + margin-bottom: 0.5rem; +} + +.error-announcement { + font-size: 0.875rem; + font-weight: 500; + letter-spacing: 0; + line-height: 1.25rem; + margin: 0; + margin-bottom: 0.5rem; +} + +.error-message { + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0; + line-height: 1rem; + color: #ccc; + margin: 0; + margin-top: 0.5rem; + display: none; +} + +.error-message.show { + display: block; +} + +.error-reload-button { + padding: 0.75rem 1.5rem; + background-color: #1d74f5; + color: white; + border: none; + border-radius: 4px; + font-size: 0.875rem; + font-weight: 500; + letter-spacing: 0; + line-height: 1.25rem; + cursor: pointer; + margin-top: 1rem; + transition: background-color 0.2s; +} + +.error-reload-button:hover { + background-color: #1565c0; +} + +.error-reload-button:active { + background-color: #0d47a1; +} + +.error-reload-button:focus { + outline: 2px solid #1d74f5; + outline-offset: 2px; +} diff --git a/src/public/index.html b/src/public/index.html index 0aa784f98e..a9ec27b875 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -1,13 +1,13 @@ - - - - - Rocket.Chat - - - - - + + + + + Rocket.Chat + + + + + diff --git a/src/public/loading.css b/src/public/loading.css new file mode 100644 index 0000000000..2e46eadb65 --- /dev/null +++ b/src/public/loading.css @@ -0,0 +1,94 @@ +.loading__animation { + display: flex; + align-items: center; + justify-content: center; +} + +.loading__animation__bounce { + display: inline-block; + width: 1rem; + height: 1rem; + margin: 2px; + animation: loading-bouncedelay 1.4s infinite ease-in-out both; + animation-delay: -0.32s; + border-radius: 100%; + background-color: rgba(255, 255, 255, 0.4); +} + +.loading__animation__bounce--medium { + display: inline-block; + width: 1.25rem; + height: 1.25rem; +} + +.loading__animation__bounce--large { + display: inline-block; + width: 1.5rem; + height: 1.5rem; +} + +.loading__animation__bounce + .loading__animation__bounce { + animation-delay: -0.16s; +} + +.loading__animation__bounce + + .loading__animation__bounce + + .loading__animation__bounce { + animation-delay: 0s; +} + +@keyframes loading-bouncedelay { + 0%, + 80%, + 100% { + transform: scale(0); + } + + 40% { + transform: scale(1); + } +} + +#loading-overlay-root { + display: none; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #2f343d; + color: white; + z-index: 999; + align-items: center; + justify-content: center; + flex-direction: column; + font-family: system-ui; +} + +#loading-overlay-root.show { + display: flex; +} + +.loading-content { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.loading-text { + font-size: 1.5rem; + font-weight: 700; + letter-spacing: 0; + line-height: 2rem; + margin-top: 1.5rem; + margin-bottom: 0.5rem; +} + +.loading-description { + font-size: 0.875rem; + font-weight: 400; + letter-spacing: 0; + line-height: 1.25rem; + color: #ccc; +} diff --git a/src/public/log-viewer-window.html b/src/public/log-viewer-window.html new file mode 100644 index 0000000000..762ed00f7e --- /dev/null +++ b/src/public/log-viewer-window.html @@ -0,0 +1,33 @@ + + + + + + Log Viewer - Rocket.Chat + + + + + + + + + + diff --git a/src/public/main.css b/src/public/main.css index 240e2cd957..ea825ed8d8 100644 --- a/src/public/main.css +++ b/src/public/main.css @@ -1,5 +1,5 @@ @import '../node_modules/@rocket.chat/fuselage/dist/fuselage.css'; :root { - --rcx-color-surface-overlay: rgba(47, 52, 61, 0.5) -} \ No newline at end of file + --rcx-color-surface-overlay: rgba(47, 52, 61, 0.5); +} diff --git a/src/public/video-call-window.html b/src/public/video-call-window.html index f55eb09a1a..c3119ab8c7 100644 --- a/src/public/video-call-window.html +++ b/src/public/video-call-window.html @@ -5,6 +5,8 @@ Rocket.Chat + +
+ +