diff --git a/.dockerignore b/.dockerignore index 92c206a7..98c2a244 100644 --- a/.dockerignore +++ b/.dockerignore @@ -19,7 +19,7 @@ tools/ LICENSE # IPAM UI -node_modules +**/node_modules build dist diff --git a/.github/workflows/azure-ipam-assets.yml b/.github/workflows/azure-ipam-assets.yml index 83c6d055..19d2272e 100644 --- a/.github/workflows/azure-ipam-assets.yml +++ b/.github/workflows/azure-ipam-assets.yml @@ -17,19 +17,20 @@ jobs: - run: echo "Job triggered by a ${{ github.event_name }} event." - run: echo "Release Tag - ${{ github.event.release.tag_name }}." - - name: "Setup NodeJS v18" + - name: "Setup NodeJS v22" id: setupNode uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 - - name: "Setup Python v3.9" + - name: "Setup Python v3.11" id: setupPython uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.11' - name: Checkout Azure IPAM Code + id: checkoutRepo uses: actions/checkout@v4 - name: Install NPM Packages diff --git a/.github/workflows/azure-ipam-build.yml b/.github/workflows/azure-ipam-build.yml index fad38e7e..e006ec68 100644 --- a/.github/workflows/azure-ipam-build.yml +++ b/.github/workflows/azure-ipam-build.yml @@ -39,6 +39,7 @@ jobs: needs: [ init ] steps: - name: Azure Login + id: azureLogin uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} @@ -47,6 +48,7 @@ jobs: enable-AzPSSession: true - name: Checkout Azure IPAM Code + id: checkoutRepo uses: actions/checkout@v4 with: sparse-checkout: | @@ -54,6 +56,7 @@ jobs: ui - name: Build Azure IPAM Containers + id: buildContainers env: IPAM_VERSION: ${{ needs.init.outputs.ipamVersion }} run: | @@ -67,6 +70,7 @@ jobs: needs: [ init ] steps: - name: Azure Login + id: azureLogin uses: azure/login@v2 with: client-id: ${{ secrets.AZURE_CLIENT_ID }} @@ -75,6 +79,7 @@ jobs: enable-AzPSSession: true - name: Checkout Azure IPAM Code + id: checkoutRepo uses: actions/checkout@v4 with: sparse-checkout: | @@ -83,6 +88,7 @@ jobs: lb - name: Build Legacy Azure IPAM Containers + id: buildLegacyContainers env: IPAM_VERSION: ${{ needs.init.outputs.ipamVersion }} run: | diff --git a/.github/workflows/azure-ipam-testing.yml b/.github/workflows/azure-ipam-testing.yml index 02ea4b79..396283ad 100644 --- a/.github/workflows/azure-ipam-testing.yml +++ b/.github/workflows/azure-ipam-testing.yml @@ -24,18 +24,21 @@ jobs: ipamResourceGroup: ${{ steps.deployScript.outputs.ipamResourceGroup }} steps: - name: Install Deployment Prerequisites + id: installPrerequisites shell: pwsh run: | Set-PSRepository PSGallery -InstallationPolicy Trusted Install-Module Az, Microsoft.Graph -AllowClobber -Force - name: Azure Login + id: azureLogin uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} enable-AzPSSession: true - name: Checkout Azure IPAM Code + id: checkoutRepo uses: actions/checkout@v4 with: sparse-checkout: | @@ -44,10 +47,12 @@ jobs: ui - name: Build Azure IPAM Container + id: buildContainer run: | az acr build -r $ACR_NAME -t ipam:${{ github.run_id }}-${{ github.run_attempt }} -f ./Dockerfile.deb . - name: Update Bicep File + id: updateBicep working-directory: deploy shell: pwsh run: | @@ -62,14 +67,14 @@ jobs: $bicepFile | Out-File -FilePath ./modules/appService.bicep -Force - name: Deploy Azure IPAM - working-directory: deploy id: deployScript + working-directory: deploy shell: pwsh run: ./deploy.ps1 -Location "westus3" -UIAppName $env:IPAM_UI_NAME -EngineAppName $env:IPAM_ENGINE_NAME - name: "Upload Logs" - working-directory: logs id: uploadLogs + working-directory: logs env: AZURE_IPAM_SUFFIX: ${{ steps.deployScript.outputs.ipamSuffix }} STORAGE_ACCT_RG: ${{ vars.LOGGING_STORAGE_RG }} @@ -97,6 +102,7 @@ jobs: Write-Output "logFile=$archiveName" >> $Env:GITHUB_OUTPUT - name: "Output Azure IPAM Deployment Details" + id: deploymentDetails env: DEPLOYMENT_DETAILS: ${{ steps.uploadLogs.outputs.deployDetails }} shell: pwsh @@ -117,6 +123,7 @@ jobs: Write-Host "-------------------" - name: "Sleep for 5 Minutes" + id: sleep shell: pwsh run: Start-Sleep -s 300 @@ -126,24 +133,28 @@ jobs: needs: [ deploy ] steps: - name: Install Testing Prerequisites + id: installPrerequisites shell: pwsh run: | Set-PSRepository PSGallery -InstallationPolicy Trusted Install-Module Az, Pester -AllowClobber -Force - name: Azure Login + id: azureLogin uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} enable-AzPSSession: true - name: Checkout Azure IPAM Code + id: checkoutRepo uses: actions/checkout@v4 with: sparse-checkout: | tests - name: Test Azure IPAM w/ Pester + id: ipamTesting working-directory: tests env: IPAM_RESOURCE_GROUP: ${{ needs.deploy.outputs.ipamResourceGroup }} @@ -174,23 +185,27 @@ jobs: needs: [ deploy, test ] steps: - name: Install Cleanup Prerequisites + id: installPrerequisites shell: pwsh run: | Set-PSRepository PSGallery -InstallationPolicy Trusted Install-Module Az -AllowClobber -Force - name: Azure Login + id: azureLogin uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} enable-AzPSSession: true - name : Remove Azure IPAM Resources + id: cleanupResources shell: pwsh run: | Remove-AzResourceGroup -Name ${{ needs.deploy.outputs.ipamResourceGroup }} -Force - name : Remove Azure IPAM Identities + id: cleanupIdentities shell: pwsh run: | $tenantId = (Get-AzContext).Tenant.Id @@ -207,5 +222,6 @@ jobs: $engineApp | Remove-AzADApplication - name: "Remove Azure IPAM Container" + id: cleanupContainer run: | az acr repository delete --name $ACR_NAME --repository ipam --yes diff --git a/.github/workflows/azure-ipam-version.yml b/.github/workflows/azure-ipam-version.yml index 346d1a5a..ca36d953 100644 --- a/.github/workflows/azure-ipam-version.yml +++ b/.github/workflows/azure-ipam-version.yml @@ -23,17 +23,17 @@ jobs: steps: - run: echo "Job triggered by a ${{ github.event_name }} event to main." - - name: "Setup NodeJS v18" + - name: "Setup NodeJS v22" id: setupNode uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 - - name: "Setup Python v3.9" + - name: "Setup Python v3.11" id: setupPython uses: actions/setup-python@v5 with: - python-version: '3.9' + python-version: '3.11' - name: "Extract Pull Request Details" id: getPullRequestData @@ -48,10 +48,19 @@ jobs: }) ).data[0]; + - name: "Create GitHub App Token" + id: appToken + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ vars.IPAM_GITHUB_APP_ID }} + private-key: ${{ secrets.IPAM_GITHUB_APP_KEY }} + - name: Checkout Azure IPAM Code + id: checkoutRepo uses: actions/checkout@v4 with: - token: ${{ secrets.PAT_TOKEN }} + token: ${{ steps.appToken.outputs.token }} + persist-credentials: false - name: Configure Git id: configureGit @@ -116,10 +125,17 @@ jobs: runs-on: ubuntu-latest needs: [ version ] steps: + - name: "Create GitHub App Token" + id: appToken + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ vars.IPAM_GITHUB_APP_ID }} + private-key: ${{ secrets.IPAM_GITHUB_APP_KEY }} + - name: Publish Azure IPAM Release id: publishRelease env: - GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} + GITHUB_TOKEN: ${{ steps.appToken.outputs.token }} tagName: v${{ needs.version.outputs.ipamVersion }} run: | gh release create "$tagName" \ diff --git a/.vscode/settings.json b/.vscode/settings.json index c686e252..392c3a89 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,8 @@ "markdownlint.config": { "default": true, "MD024": { "siblings_only": true }, + "MD028": false, "MD033": false - } -} \ No newline at end of file + }, + "files.trimTrailingWhitespace": true +} diff --git a/Dockerfile.deb b/Dockerfile.deb index 6a6976f8..83f4990e 100644 --- a/Dockerfile.deb +++ b/Dockerfile.deb @@ -1,10 +1,20 @@ -ARG BUILD_IMAGE=node:18-slim -ARG SERVE_IMAGE=python:3.9-slim +ARG BUILD_IMAGE=node:22-slim +ARG SERVE_IMAGE=python:3.11-slim +# Set Production Build Flag +ARG PROD_BUILD=true + +# Set Default Port ARG PORT=8080 FROM $BUILD_IMAGE AS builder +# Production Build Flag +ARG PROD_BUILD + +# Set Debian Frontend Flag +ARG DEBIAN_FRONTEND=noninteractive + # Disable NPM Update Notifications ENV NPM_CONFIG_UPDATE_NOTIFIER=false @@ -15,7 +25,11 @@ WORKDIR /tmp COPY ./ui/. ./ # Install UI Dependencies -RUN npm ci +RUN if [ "${PROD_BUILD}" = true ]; then \ + npm ci; \ +else \ + npm install; \ +fi RUN chmod 777 -R node_modules # Build IPAM UI @@ -23,8 +37,15 @@ RUN npm run build FROM $SERVE_IMAGE +# Set Production Build Flag +ARG PROD_BUILD + +# Port to Listen On ARG PORT +# Set Debian Frontend to Non-Interactive +ARG DEBIAN_FRONTEND=noninteractive + # Set Environment Variable ENV PORT=${PORT} @@ -37,7 +58,7 @@ WORKDIR /tmp # Install OpenSSH and set the password for root to "Docker!" RUN apt-get update RUN apt-get install -qq openssh-server -y \ - && echo "root:Docker!" | chpasswd + && echo "root:Docker!" | chpasswd # Enable SSH root login with Password Authentication # RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/g' /etc/ssh/sshd_config @@ -45,20 +66,26 @@ RUN apt-get install -qq openssh-server -y \ # Copy 'sshd_config File' to /etc/ssh/ COPY sshd_config /etc/ssh/ +# Set SSH Key Permissions RUN ssh-keygen -A -RUN mkdir /var/run/sshd +RUN mkdir -p /var/run/sshd # Set Working Directory WORKDIR /ipam # Install Engine Dependencies +COPY ./engine/requirements.txt /code/requirements.txt COPY ./engine/requirements.lock.txt /code/requirements.lock.txt # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir -r /code/requirements.lock.txt --progress-bar off +RUN if [ "${PROD_BUILD}" = true ]; then \ + pip install --no-cache-dir -r /code/requirements.lock.txt --progress-bar off; \ +else \ + pip install --no-cache-dir -r /code/requirements.txt --progress-bar off; \ +fi # Copy Engine Code COPY ./engine/app ./app diff --git a/Dockerfile.func b/Dockerfile.func index 360f0a5d..e1f86404 100644 --- a/Dockerfile.func +++ b/Dockerfile.func @@ -1,8 +1,17 @@ -ARG BUILD_IMAGE=mcr.microsoft.com/azure-functions/node:4-node18-appservice -ARG SERVE_IMAGE=mcr.microsoft.com/azure-functions/python:4-python3.9-appservice +ARG BUILD_IMAGE=mcr.microsoft.com/azure-functions/node:4-node22-appservice +ARG SERVE_IMAGE=mcr.microsoft.com/azure-functions/python:4-python3.11-appservice + +# Set Production Build Flag +ARG PROD_BUILD=true FROM $BUILD_IMAGE AS builder +# Production Build Flag +ARG PROD_BUILD + +# Set Debian Frontend Flag +ARG DEBIAN_FRONTEND=noninteractive + # Disable NPM Update Notifications ENV NPM_CONFIG_UPDATE_NOTIFIER=false @@ -13,7 +22,11 @@ WORKDIR /tmp COPY ./ui/. ./ # Install UI Dependencies -RUN npm ci +RUN if [ "${PROD_BUILD}" = true ]; then \ + npm ci; \ +else \ + npm install; \ +fi RUN chmod 777 -R node_modules # Build IPAM UI @@ -21,6 +34,13 @@ RUN npm run build FROM $SERVE_IMAGE +# Set Production Build Flag +ARG PROD_BUILD + +# Set Debian Frontend to Non-Interactive +ARG DEBIAN_FRONTEND=noninteractive + +# Set Azure Function Root Directory & Enable Console Logging ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ AzureFunctionsJobHost__Logging__Console__IsEnabled=true @@ -31,13 +51,18 @@ ENV PIP_ROOT_USER_ACTION=ignore WORKDIR /tmp # Copy Requirements File +COPY ./engine/requirements.txt . COPY ./engine/requirements.lock.txt . # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir -r ./requirements.lock.txt --progress-bar off +RUN if [ "${PROD_BUILD}" = true ]; then \ + pip install --no-cache-dir -r ./requirements.lock.txt --progress-bar off; \ +else \ + pip install --no-cache-dir -r ./requirements.txt --progress-bar off; \ +fi # Copy Application Code to Function App Root Directory COPY ./engine/. /home/site/wwwroot diff --git a/Dockerfile.rhel b/Dockerfile.rhel index 4a14fa9e..50b85608 100644 --- a/Dockerfile.rhel +++ b/Dockerfile.rhel @@ -1,10 +1,17 @@ -ARG BUILD_IMAGE=registry.access.redhat.com/ubi8/nodejs-18 -ARG SERVE_IMAGE=registry.access.redhat.com/ubi8/python-39 +ARG BUILD_IMAGE=registry.access.redhat.com/ubi8/nodejs-22 +ARG SERVE_IMAGE=registry.access.redhat.com/ubi8/python-311 +# Set Production Build Flag +ARG PROD_BUILD=true + +# Set Default Port ARG PORT=8080 FROM $BUILD_IMAGE AS builder +# Production Build Flag +ARG PROD_BUILD + # Disable NPM Update Notifications ENV NPM_CONFIG_UPDATE_NOTIFIER=false @@ -18,7 +25,11 @@ USER root COPY ./ui/. ./ # Install UI Dependencies -RUN npm ci +RUN if [ "${PROD_BUILD}" = true ]; then \ + npm ci; \ +else \ + npm install; \ +fi RUN chmod 777 -R node_modules # Build IPAM UI @@ -26,6 +37,10 @@ RUN npm run build FROM $SERVE_IMAGE +# Set Production Build Flag +ARG PROD_BUILD + +# Port to Listen On ARG PORT # Set Environment Variable @@ -62,13 +77,18 @@ RUN mkdir /var/run/sshd WORKDIR /ipam # Install Engine Dependencies +COPY ./engine/requirements.txt /code/requirements.txt COPY ./engine/requirements.lock.txt /code/requirements.lock.txt # Upgrade PIP RUN pip install --upgrade pip --progress-bar off # Install Dependencies -RUN pip install --no-cache-dir -r /code/requirements.lock.txt --progress-bar off +RUN if [ "${PROD_BUILD}" = true ]; then \ + pip install --no-cache-dir -r /code/requirements.lock.txt --progress-bar off; \ +else \ + pip install --no-cache-dir -r /code/requirements.txt --progress-bar off; \ +fi # Copy Engine Code COPY ./engine/app ./app diff --git a/README.md b/README.md index fb49beed..1c70182b 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,7 @@ description: "IPAM - Azure IP Address Management made easy!" --- --> -# Azure IPAM - - -Azure IPAM is a lightweight solution developed on top of the Azure platform designed to help Azure customers manage their IP Address space easily and effectively. + + +# Azure IPAM + +Azure IPAM is a lightweight solution developed on top of the Azure platform designed to help Azure customers manage their IP Address space easily and effectively. ## Repo Contents @@ -31,11 +33,12 @@ Azure IPAM is a lightweight solution developed on top of the Azure platform desi |----------------------|---------------------------------------------------------------| | `.github/` | Bug Report, Issue Templates and GitHub Actions | | `.vscode/` | VSCode Configuration | -| `deploy/` | Deployment Bicep Templates & PowerShell Deployment Scripts | +| `deploy/` | Deployment Bicep Templates & PowerShell Deployment Script | | `assets/` | Compiled ZIP Archive | | `docs/` | Documentation Folder | | `engine/` | Engine Application Code | | `examples/` | Example Templates, Scripts and Code Snippets for Azure IPAM | +| `migrate/` | Migration Bicep Templates & Powershell Migration Script | | `lb/` | Load Balancer (NGINX) Configs | | `tests/` | Testing Scripts | | `tools/` | Lifecycle Scripts (Build/Version/Update) | @@ -58,10 +61,11 @@ Azure IPAM is a lightweight solution developed on top of the Azure platform desi ## Documentation -IPAM uses both [Docsify](https://docsify.js.org/) and [GitHub Pages](https://docs.github.com/en/github/working-with-github-pages) to present the project documentation, which can be found [here](https://azure.github.io/ipam/) +IPAM uses both [Docsify](https://docsify.js.org/) and [GitHub Pages](https://docs.github.com/en/github/working-with-github-pages) for all [project documentation](https://azure.github.io/ipam/). ## Questions or Comments for the team? -The IPAM team welcomes questions and contributions from the community. We have set up a GitHub Discussions page [here](https://github.com/Azure/ipam/discussions) to make it easy to engage with the IPAM team without opening an issue. + +The IPAM team welcomes questions and contributions from the community. We have set up a [GitHub Discussions](https://github.com/Azure/ipam/discussions) page to make it easy to engage with the IPAM team without opening an issue. ## FAQ @@ -69,11 +73,13 @@ The IPAM team welcomes questions and contributions from the community. We have s You realize that you do not have a clear picture as to what is deployed into your Azure environment and connected to your private IP address space. Or, you would like a way to easily manage, assign, and track your private IP addess space usage! **What does the roadmap for IPAM look like?** + - We are assessing leveraging Azure Container Apps for hosting the two containers that make up the IPAM application - We are assessing support for multiple Tenants, as today the tool is designed with a single Tenant in mind - We are working on capturing IP address infromation for resources that support hybrid connectivity (ie Gateways) **Who are the awesome people that built this solution??** + Matt and Harvey are Architects at Microsoft! We are always on the look out for interesting ways to help our customers overcome their challenges! ## Contributing diff --git a/deploy/deploy.ps1 b/deploy/deploy.ps1 index 98202582..0bc741dd 100644 --- a/deploy/deploy.ps1 +++ b/deploy/deploy.ps1 @@ -1,7 +1,7 @@ ############################################################################################################### ## ## Azure IPAM Solution Deployment Script -## +## ############################################################################################################### # Set minimum version requirements @@ -207,7 +207,7 @@ param( if ($_ -notmatch "(\.json)") { throw [System.ArgumentException]::New("The file specified in the 'ParameterFile' argument must be of type json.") } - return $true + return $true })] [System.IO.FileInfo] $ParameterFile, @@ -228,7 +228,7 @@ param( if ($_ -notmatch "(\.zip)") { throw [System.ArgumentException]::New("The file specified in the 'ZipFilePath' argument must be of type zip.") } - return $true + return $true })] [System.IO.FileInfo] $ZipFilePath @@ -420,20 +420,24 @@ process { AZURE_CHINA = "management.chinacloudapi.cn" } - $accessToken = (Get-AzAccessToken).Token | ConvertTo-SecureString -AsPlainText + $accessToken = (Get-AzAccessToken).Token + + if ($accessToken -isnot [System.Security.SecureString]) { + $accessToken = ConvertTo-SecureString $accessToken -AsPlainText -Force + } $response = Invoke-RestMethod ` -Method POST ` -Uri "https://$($msArmMap[$AzureCloud])/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.ContainerRegistry/registries/$RegistryName/runs/$BuildId/listLogSasUrl?api-version=2019-04-01" ` -Authentication Bearer ` -Token $accessToken - + $logLink = $response.logLink $logs = Invoke-RestMethod ` -Method GET ` -Uri $logLink - + return $logs } @@ -538,7 +542,7 @@ process { $engineApiSettings = @{ Oauth2PermissionScope = @( - @{ + @{ AdminConsentDescription = "Allows the IPAM UI to access IPAM Engine API as the signed-in user." AdminConsentDisplayName = "Access IPAM Engine API" Id = $engineApiGuid @@ -601,7 +605,7 @@ process { $uiObject = Get-AzADApplication -ApplicationId $uiApp.AppId } - + $engineObject = Get-AzADApplication -ApplicationId $engineApp.AppId # Create IPAM UI Service Principal (If DisableUI not specified) @@ -705,7 +709,9 @@ process { ?? [System.Version]([array](Get-InstalledModule | Where-Object { $_.Name -like "Microsoft.Graph.*" } | Select-Object -ExpandProperty Version | Sort-Object | Get-Unique))[0] if ($graphVersion.Major -gt 1) { - $accesstoken = ConvertTo-SecureString $accesstoken -AsPlainText -Force + if ($accesstoken -isnot [System.Security.SecureString]) { + $accesstoken = ConvertTo-SecureString $accesstoken -AsPlainText -Force + } } Write-Host "INFO: Logging in to Microsoft Graph" -ForegroundColor Green @@ -730,7 +736,7 @@ process { foreach ($scope in $uiGraphScopes) { $msGraphId = Get-AzADServicePrincipal ` -ApplicationId $scope.scopeId - + New-MgOauth2PermissionGrant ` -ResourceId $msGraphId.Id ` -Scope $scope.scopes ` @@ -943,25 +949,25 @@ process { [Parameter(Mandatory = $true)] [System.IO.DirectoryInfo]$AssetFolder ) - + $ZipFilePath = Join-Path -Path $AssetFolder.FullName -ChildPath $ZipFileName - + try { $GitHubURL = "https://api.github.com/repos/$GitHubUserName/$GitHubRepoName/releases/latest" - + Write-Host "INFO: Target GitHub Repo is " -ForegroundColor Green -NoNewline Write-Host "$GitHubUserName/$GitHubRepoName" -ForegroundColor Cyan Write-Host "INFO: Fetching download URL..." -ForegroundColor Green - + $GHResponse = Invoke-WebRequest -Method GET -Uri $GitHubURL $JSONResponse = $GHResponse.Content | ConvertFrom-Json $AssetList = $JSONResponse.assets $Asset = $AssetList | Where-Object { $_.name -eq $ZipFileName } $DownloadURL = $Asset.browser_download_url - + Write-Host "INFO: Downloading ZIP Archive to " -ForegroundColor Green -NoNewline Write-Host $ZipFilePath -ForegroundColor Cyan - + Invoke-WebRequest -Uri $DownloadURL -OutFile $ZipFilePath } catch { @@ -991,6 +997,11 @@ process { if ($UseAPI) { $accessToken = (Get-AzAccessToken).Token + + if ($accessToken -is [System.Security.SecureString]) { + $accessToken = ConvertFrom-SecureString $accessToken -AsPlainText -Force + } + $zipContents = Get-Item -Path $ZipFilePath $publishProfile = Get-AzWebAppPublishingProfile -Name $AppName -ResourceGroupName $ResourceGroupName @@ -1052,7 +1063,7 @@ process { $appServiceEndpoint = "https://$Endpoint" # Update UI Application with single-page application configuration - Update-AzADApplication -ApplicationId $UIAppId -SPARedirectUri $appServiceEndpoint + Update-AzADApplication -ApplicationId $UIAppId -SPARedirectUri $appServiceEndpoint Write-Host "INFO: UI Application SPA configuration update complete" -ForegroundColor Green } @@ -1232,16 +1243,16 @@ process { Extension = 'deb' Port = 8080 Images = @{ - Build = 'node:18-slim' - Serve = 'python:3.9-slim' + Build = 'node:22-slim' + Serve = 'python:3.11-slim' } } RHEL = @{ Extension = 'rhel' Port = 8080 Images = @{ - Build = 'registry.access.redhat.com/ubi8/nodejs-18' - Serve = 'registry.access.redhat.com/ubi8/python-39' + Build = 'registry.access.redhat.com/ubi8/nodejs-22' + Serve = 'registry.access.redhat.com/ubi8/python-311' } } } @@ -1369,7 +1380,7 @@ process { if ($env:CI) { Write-Host $_.ToString() - } + } exit 1 } diff --git a/deploy/modules/containerRegistry.bicep b/deploy/modules/containerRegistry.bicep index 5b786372..f9960585 100644 --- a/deploy/modules/containerRegistry.bicep +++ b/deploy/modules/containerRegistry.bicep @@ -11,7 +11,7 @@ var acrPull = '7f951dda-4ed3-4680-a7ca-43fe172d538d' var acrPullId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', acrPull) var acrPullRoleAssignmentId = guid(subscription().id, acrPull, principalId) -resource containerRegistry 'Microsoft.ContainerRegistry/registries@2021-12-01-preview' = { +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-12-01' = { name: containerRegistryName location: location sku: { @@ -19,7 +19,7 @@ resource containerRegistry 'Microsoft.ContainerRegistry/registries@2021-12-01-pr } } -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: acrPullRoleAssignmentId scope: containerRegistry properties: { diff --git a/deploy/modules/keyVault.bicep b/deploy/modules/keyVault.bicep index caba1330..078ba583 100644 --- a/deploy/modules/keyVault.bicep +++ b/deploy/modules/keyVault.bicep @@ -30,7 +30,7 @@ var keyVaultUser = '4633458b-17de-408a-b874-0445c86b69e6' var keyVaultUserId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', keyVaultUser) var keyVaultUserRoleAssignmentId = guid(keyVaultUser, identityPrincipalId, keyVault.id) -resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' = { +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: keyVaultName location: location properties: { @@ -48,7 +48,7 @@ resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' = { } } -resource identityId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { +resource identityId 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault name: 'IDENTITY-ID' properties: { @@ -56,7 +56,7 @@ resource identityId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { } } -resource uiId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { +resource uiId 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault name: 'UI-ID' properties: { @@ -64,7 +64,7 @@ resource uiId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { } } -resource engineId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { +resource engineId 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault name: 'ENGINE-ID' properties: { @@ -72,7 +72,7 @@ resource engineId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { } } -resource engineSecret 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { +resource engineSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault name: 'ENGINE-SECRET' properties: { @@ -80,7 +80,7 @@ resource engineSecret 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { } } -resource appTenant 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { +resource appTenant 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault name: 'TENANT-ID' properties: { @@ -116,7 +116,7 @@ resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-pr } } -resource keyVaultUserAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { +resource keyVaultUserAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: keyVaultUserRoleAssignmentId scope: keyVault properties: { diff --git a/deploy/modules/managedIdentity.bicep b/deploy/modules/managedIdentity.bicep index 2e669269..bedf2ea5 100644 --- a/deploy/modules/managedIdentity.bicep +++ b/deploy/modules/managedIdentity.bicep @@ -16,7 +16,7 @@ resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018- location: location } -resource contributorAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { +resource contributorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: contributorRoleAssignmentId properties: { principalType: 'ServicePrincipal' @@ -25,7 +25,7 @@ resource contributorAssignment 'Microsoft.Authorization/roleAssignments@2020-04- } } -resource managedIdentityOperatorAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { +resource managedIdentityOperatorAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: managedIdentityOperatorRoleAssignmentId properties: { principalType: 'ServicePrincipal' diff --git a/deploy/update.ps1 b/deploy/update.ps1 index 6e1fbf7c..b2ebd46f 100644 --- a/deploy/update.ps1 +++ b/deploy/update.ps1 @@ -1,7 +1,7 @@ ############################################################################################################### ## ## Azure IPAM ZIP Deploy Updater Script -## +## ############################################################################################################### # Set minimum version requirements @@ -53,7 +53,7 @@ param( if(-Not ($_ | Get-Item) ) { throw [System.ArgumentException]::New("AssetFolder does not exist, please provide a pre-existing folder.") } - return $true + return $true })] [System.IO.DirectoryInfo] $AssetFolder, @@ -70,7 +70,7 @@ param( if($_ -notmatch "(\.zip)") { throw [System.ArgumentException]::New("The file specified in the 'ZipFilePath' argument must be of type zip.") } - return $true + return $true })] [System.IO.FileInfo] $ZipFilePath @@ -88,6 +88,13 @@ $IPAM_PUBLIC_ACR = "azureipam.azurecr.io" # Set preference variables $ErrorActionPreference = "Stop" +# Hide Azure PowerShell SDK Warnings +$Env:SuppressAzurePowerShellBreakingChangeWarnings = $true + +# Hide Azure PowerShell SDK & Azure CLI Survey Prompts +$Env:AzSurveyMessage = $false +$Env:AZURE_CORE_SURVEY_MESSAGE = $false + # Set Log File Location $logPath = Join-Path -Path $ROOT_DIR -ChildPath "logs" New-Item -ItemType Directory -Path $logpath -Force | Out-Null @@ -126,16 +133,46 @@ Function Get-BuildLogs { -Uri "https://$($msArmMap[$AzureCloud])/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.ContainerRegistry/registries/$RegistryName/runs/$BuildId/listLogSasUrl?api-version=2019-04-01" ` -Authentication Bearer ` -Token $accessToken - + $logLink = $response.logLink $logs = Invoke-RestMethod ` -Method GET ` -Uri $logLink - + return $logs } +Function Set-HealthCheck { + Param( + [Parameter(Mandatory=$true)] + [string]$AppName, + [Parameter(Mandatory=$true)] + [string]$ResourceGroupName + ) + + Set-AzResource ` + -ResourceGroupName $ResourceGroupName ` + -ResourceType "Microsoft.Web/sites/config" ` + -ResourceName "${AppName}/web" ` + -ApiVersion "2023-12-01" ` + -Properties @{ healthCheckPath = "/api/status" } ` + -Force ` + | Out-Null + + $existing = @{} + + $site = Get-AzWebApp -ResourceGroupName $ResourceGroupName -Name $AppName + $site.SiteConfig.AppSettings | ForEach-Object { $existing[$_.Name] = $_.Value } + $existing["WEBSITE_HEALTHCHECK_MAXPINGFAILURES"] = "2" + + Set-AzWebApp ` + -ResourceGroupName $ResourceGroupName ` + -Name $AppName ` + -AppSettings $existing ` + | Out-Null +} + Function Restart-IpamApp { Param( [Parameter(Mandatory=$true)] @@ -293,7 +330,7 @@ Start-Transcript -Path $updateLog | Out-Null try { Write-Host - Write-Host "INFO: Verifying application exists" -ForegroundColor Green + Write-Host "INFO: Verifying application exists..." -ForegroundColor Green $appType = "" $isFunction = $false @@ -308,19 +345,35 @@ try { } else { $appKind = $existingApp.Kind $appType = $($appKind.Split(",") -contains 'functionapp') ? 'Function' : 'App' - $isFunction = $appType -eq 'Function' ? $true : $false + $isFunction = $appType -eq 'Function' ? $true : $false } - $appContainer = $existingApp.Kind.Split(",") -contains 'container' - - if ($appContainer) { + $isContainer = $appKind.Split(",") -contains 'container' + + if ($isContainer) { $appType += "Container" } Write-Host "INFO: Application exists, detected type is " -ForegroundColor Green -NoNewline Write-Host $appType -ForegroundColor Cyan - if ($appContainer) { + if($isContainer -and $existingApp.SiteConfig.LinuxFxVersion.StartsWith("COMPOSE|")) { + Write-Host "WARNING: Legacy Docker Compose detected!" -ForegroundColor Yellow + Write-Host + Write-Host "Please follow the migration guide at " -ForegroundColor Blue -NoNewline + Write-Host "https://azure.github.io/ipam/#/migration/README" -ForegroundColor Cyan -NoNewline + Write-Host " for complete instructions." -ForegroundColor Blue + exit + } + + if ($null -eq $existingApp.SiteConfig.HealthCheckPath) { + Write-Host "WARNING: Health Check is missing!" -ForegroundColor Yellow + Write-Host "INFO: Adding application health check..." -ForegroundColor Green + + Set-HealthCheck -ResourceGroupName $ResourceGroupName -AppName $AppName + } + + if ($isContainer) { $appAcr = $existingApp.SiteConfig.LinuxFxVersion.Split('|')[1].Split('/')[0] $privateAcr = $appAcr -eq $IPAM_PUBLIC_ACR ? $false : $true @@ -381,7 +434,7 @@ try { } } - if (-not $appContainer) { + if (-not $isContainer) { Write-Host "INFO: Verifying application Python version" -ForegroundColor Green $engineFolder = Join-Path -Path $ROOT_DIR -ChildPath 'engine' @@ -403,17 +456,18 @@ try { } } - if ($appContainer) { + if ($isContainer) { if (-not $isFunction) { Write-Host "INFO: Detecting container distro..." -ForegroundColor Green $appUri = $existingApp.HostNames[0] $statusUri = "https://${appUri}/api/status" - $status = Invoke-RestMethod -Method Get -Uri $statusUri -ErrorVariable statusErr -ErrorAction SilentlyContinue - if ($statusErr) { + try { + $status = Invoke-RestMethod -Method Get -Uri $statusUri -ErrorVariable statusErr -ErrorAction SilentlyContinue + } catch { Write-Host "ERROR: Unable to detect container distro!" -ForegroundColor Red - throw $statusErr + throw $_ } $containerType = $status.container.image_id @@ -426,16 +480,16 @@ try { Extension = 'deb' Port = 80 Images = @{ - Build = 'node:18-slim' - Serve = 'python:3.9-slim' + Build = 'node:22-slim' + Serve = 'python:3.11-slim' } } rhel = @{ Extension = 'rhel' Port = 8080 Images = @{ - Build = 'registry.access.redhat.com/ubi8/nodejs-18' - Serve = 'registry.access.redhat.com/ubi8/python-39' + Build = 'registry.access.redhat.com/ubi8/nodejs-22' + Serve = 'registry.access.redhat.com/ubi8/python-311' } } } diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 9df4464e..d16765f7 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,8 +1,10 @@ + ![logo](./images/ipam-logo.png ':size=45%') # IPAM 3.5.0 + > Azure IP Address Management Made Easy [GitHub](https://github.com/Azure/ipam) diff --git a/docs/_sidebar.md b/docs/_sidebar.md index e8fd818b..be938c22 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -1,11 +1,12 @@ + -* Getting Started - -* [Welcome](/README.md) -* [Deployment](/deployment/README.md) -* [Troubleshooting](/troubleshooting/README.md) -* [How-To](/how-to/README.md) -* [API](/api/README.md) -* [Questions/Comments](/questions-comments/README.md) -* [Contributing](/contributing/README.md) +- [Welcome](/README.md) +- [Deployment](/deployment/README.md) +- [Update](/update/README.md) +- [Migration](/migration/README.md) +- [Troubleshooting](/troubleshooting/README.md) +- [How-To](/how-to/README.md) +- [API](/api/README.md) +- [Questions/Comments](/questions-comments/README.md) +- [Contributing](/contributing/README.md) diff --git a/docs/deployment/README.md b/docs/deployment/README.md index 579464ae..72591710 100644 --- a/docs/deployment/README.md +++ b/docs/deployment/README.md @@ -24,7 +24,7 @@ To successfully deploy the solution, the following prerequisites must be met: - [Docker (Linux)](https://docs.docker.com/engine/install/) / [Docker Desktop (Windows)](https://docs.docker.com/desktop/install/windows-install/) installed (optional) - Required only if you are building your own container image and running it locally for development/testing purposes -> **NOTE:** An alternate [Management Group](https://learn.microsoft.com/en-us/azure/governance/management-groups/overview) can be specific, but is **highly discouraged** as it will limit the visibility of the Azure IPAM platform. This option should only be used for testing or proof-of-concept deployments. +> **NOTE:** An alternate [Management Group](https://learn.microsoft.com/azure/governance/management-groups/overview) can be specific, but is **highly discouraged** as it will limit the visibility of the Azure IPAM platform. This option should only be used for testing or proof-of-concept deployments. ## Deployment Overview @@ -61,7 +61,7 @@ Connect-AzAccount Connect-AzAccount -UseDeviceAuthentication ``` -> **NOTE:** If you're connecting to an Azure Cloud besides Azure Public (such as Gov, China, etc.), you may need to specify the `-Environment` flag as described [here](https://learn.microsoft.com/powershell/azure/authenticate-azureps#sign-in-to-another-cloud) when using `Connect-AzAccount` +> **NOTE:** If you're connecting to an Azure Cloud besides Azure Public (such as Gov, China, etc.), you may need to specify the `-Environment` flag as described in the [Authentication Methods](https://learn.microsoft.com/powershell/azure/authenticate-azureps#sign-in-to-another-cloud) documentation when using `Connect-AzAccount` ### Set the Active Subscription for Azure PowerShell @@ -76,7 +76,7 @@ Set-AzContext -Subscription "28b502e2-323f-4e57-98db-743459176557" Set-AzContext -Subscription "Contoso IPAM Subscription" ``` -For additional information on authenticating with Azure PowerShell, refer to the documentation [here](https://learn.microsoft.com/powershell/azure/authenticate-azureps) +For additional information on authenticating with Azure PowerShell, refer to the [documentation](https://learn.microsoft.com/powershell/azure/authenticate-azureps) ## Authenticate to Azure CLI (Optional) @@ -105,7 +105,7 @@ az account set --subscription "28b502e2-323f-4e57-98db-743459176557" az account set --subscription "Contoso IPAM Subscription" ``` -For additional information on authenticating with Azure CLI, refer to the documentation [here](https://learn.microsoft.com/cli/azure/authenticate-azure-cli) +For additional information on authenticating with Azure CLI, refer to the [documentation](https://learn.microsoft.com/cli/azure/authenticate-azure-cli) ## Clone the Github Repo @@ -126,7 +126,7 @@ PS /ipam/deploy> .\deploy.ps1 To deploy the full solution, run the following from within the `deploy` directory: ```powershell -./deploy.ps1 -Location "westus3" +./deploy.ps1 -Location "westus3" ``` You have the ability to pass optional flags to the deployment script: @@ -149,7 +149,7 @@ You have the ability to pass optional flags to the deployment script: > **NOTE 3:** Maximum of seven (7) characters. This is because the prefix is used to generate names for several different Azure resource types with varying maximum lengths. -> **NOTE 4:** It is **highly discouraged** to use a [Management Group](https://learn.microsoft.com/en-us/azure/governance/management-groups/overview) other than the [Root Management Group](https://learn.microsoft.com/azure/governance/management-groups/overview#root-management-group-for-each-directory) as it will limit the visibility of the Azure IPAM platform. This option should only be used for testing or proof-of-concept deployments. +> **NOTE 4:** It is **highly discouraged** to use a [Management Group](https://learn.microsoft.com/azure/governance/management-groups/overview) other than the [Root Management Group](https://learn.microsoft.com/azure/governance/management-groups/overview#root-management-group-for-each-directory) as it will limit the visibility of the Azure IPAM platform. This option should only be used for testing or proof-of-concept deployments. **Customize the name of the App Registrations:** diff --git a/docs/migration/README.md b/docs/migration/README.md new file mode 100644 index 00000000..d0503081 --- /dev/null +++ b/docs/migration/README.md @@ -0,0 +1,456 @@ +# Azure IPAM Legacy Migration Guide + +## Overview + +This guide provides comprehensive instructions for migrating existing Azure IPAM deployments from Docker Compose-based App Services to modern Azure infrastructure. The migration process preserves your existing data and configuration while updating your deployment to use current Azure best practices. + +**Migration is required immediately** due to Microsoft's announcement of the deprecation and eventual removal of Docker Compose support for Azure App Service. Docker Compose support is deprecated and will be removed in future App Service versions, making migration essential for continued operation and security updates. The retirement date for Docker Compose capability is **March 31, 2027**. For additional details, refer to the [Docker Compose Migration announcement](https://azure.github.io/AppService/2025/04/01/Docker-compose-migration.html). + +## Who Should Migrate? + +All Azure IPAM users currently running Docker Compose-based deployments should migrate ***as soon as possible***. + +### Check If You Need to Migrate + +To determine if your deployment requires migration, check your App Service configuration: + +1. Navigate to your Azure IPAM App Service in the Azure Portal +2. Go to **Deployment** → **Deployment Center** +3. Look for the **Container Type** field +4. If it shows **Docker Compose (Preview)**, you need to migrate + +![Docker Compose (Preview)](./images/verify_docker_compose.png) + +Alternatively, you can check via PowerShell: + +```powershell +$webApp = Get-AzWebApp -ResourceGroupName "your-resource-group" -Name "your-app-name" +$webApp.SiteConfig.LinuxFxVersion +``` + +If this returns a value containing `COMPOSE|` followed by Base64 content, you need to migrate. + +## Prerequisites + +To successfully migrate your Azure IPAM deployment, the following prerequisites must be met: + +- An Azure Subscription containing your existing Azure IPAM deployment +- The following Azure RBAC Roles: + - [Contributor](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#contributor) or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner) at the Resource Group scope containing your Azure IPAM resources + - [Reader](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#reader) at the Subscription scope for resource discovery +- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) installed + - Required to clone the Azure IPAM GitHub repository +- [PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) version 7.2.0 or later installed +- [Azure PowerShell](https://learn.microsoft.com/powershell/azure/install-az-ps) version 11.4.0 or later installed +- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) version 2.35.0 or later installed (optional) + - Required only if your deployment uses a private Azure Container Registry (Private ACR) + +> **NOTE:** The migration script requires access to your existing Azure IPAM resources. Ensure you have the necessary permissions to both read the current configuration and modify the App Service deployment. + +## Pre-Migration Backup + +**Before proceeding with the migration, it is strongly recommended to back up your Azure IPAM App Service.** While the migration process is designed to preserve your existing data and configuration, having a backup ensures you can restore your App Service if any issues occur during migration. + +> **Important**: App Service backups are only available in **Basic, Standard, Premium, and Isolated** pricing tiers. If your App Service is running on a Free or Shared tier, you cannot create backups and should consider upgrading your pricing tier before migration. + +### Check App Service Backup Status + +First, verify the backup configuration for your App Service: + +1. **Navigate to your App Service** in the Azure Portal +2. Go to **Settings** → **Backups** +3. Check the backup status: + - **Automatic backups** are always enabled for supported pricing tiers and cannot be disabled + - If **custom backups** are configured, you'll see backup configuration details including frequency and retention + - If **custom backups** are not configured, you'll see "Backups are not configured for this app" + - If your App Service is on an unsupported tier, you'll see "Backups are not available for this pricing tier" + +![Backup Status](./images/check_backup_status.png) + +### Check Recent Backups + +Verify recent backup history (both automatic and custom backups will appear here): + +1. In the **Backups** section, scroll down to view **Backup History** +2. Review recent backups and verify: + - **Status**: Ensure recent backups show "Succeeded" + - **Date**: Confirm backups are recent + - **Size**: Note the backup file sizes + - **Type**: Distinguish between automatic backups and custom backups (if configured) + +![Recent Backups](./images/check_recent_backups.png) + +> **Note**: App Services automatically perform backups that cannot be disabled. With automatic backups configured, you cannot perform on-demand backups. You should verify that a recent backup was successfully completed before proceeding with migration. + +### Configure Custom Backups (Optional) + +If you want to configure custom backups with your own storage account and schedule, you can set them up before migration: + +1. **Navigate to your App Service** in the Azure Portal +2. Go to **Settings** → **Backups** +3. Click **Configure Custom Backups** to set up custom backup settings +4. **Configure custom backup settings**: + - **Storage Account**: Select or create a storage account (required) + - **Container**: Select or create a container (e.g., "app-backups") +5. Optionally you can setup a custom back schedule by selecting the **Set Schedule** checkbox + - **Repeats Every**: Set frequency (at least Daily recommended) + - **Start Time**: Start time of the backup schedule + - **Time Zone**: UTC or local (poral) time zone + - **Retention**: Set retention period (at least 30 days recommended) +6. Click **Configure** to enable custom backups + +![Configure Custom Backups](./images/configure_custom_backups.png) + +### Create an On-Demand Backup + +If you want to run an on-demand backup, you must first configure **Custom Backups** (see above) then proceed as follows: + +1. **Navigate to your App Service** in the Azure Portal +2. Go to **Settings** → **Backups** +3. Click **Backup Now** to create an immediate backupstart the backup process +4. Monitor the backup status until it shows **Succeeded** + +![Create On-Demand Backup](./images/create_on_demand_backup.png) + +> **Important**: +> +> - If automatic backups are enabled, the "Backup Now" option is not available +> - Ensure you have at least one successful backup before proceeding with migration +> - If backup creation fails, resolve the issue before continuing with migration + +For detailed information about App Service backups, including limitations and requirements, see the [Azure App Service backup documentation](https://learn.microsoft.com/azure/app-service/manage-backup). + +## Authenticate to Azure PowerShell + +Before executing the Azure IPAM migration script, you'll need to authenticate to [Azure PowerShell](https://learn.microsoft.com/powershell/azure/install-az-ps) and set the context to the subscription containing your Azure IPAM deployment. + +### Connect to Azure PowerShell + +```powershell +# Sign in Interactively +Connect-AzAccount + +# Sign in with Device Code +Connect-AzAccount -UseDeviceAuthentication +``` + +> **NOTE:** If you're connecting to an Azure Cloud besides Azure Public (such as Gov, China, etc.), you may need to specify the `-Environment` flag as described in the [Azure PowerShell](https://learn.microsoft.com/powershell/module/az.accounts/connect-azaccount) documentation when using `Connect-AzAccount` + +### Set the Active Subscription for Azure PowerShell + +```powershell +# Set Azure PowerShell Context +Set-AzContext -Subscription "" + +# Example with Subscription ID +Set-AzContext -Subscription "28b502e2-323f-4e57-98db-743459176557" + +# Example with Subscription Name +Set-AzContext -Subscription "Contoso IPAM Subscription" +``` + +For additional information on authenticating with Azure PowerShell, refer to the [Azure PowerShell](https://learn.microsoft.com/powershell/azure/authenticate-azureps) documentation. + +## Authenticate to Azure CLI (Optional) + +If your Azure IPAM deployment uses a private Azure Container Registry, you will need to be authenticated to the [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) *in addition to* Azure PowerShell. This is because the migration script will use the `az acr build` feature to build updated application containers and push them into your private Azure Container Registry. + +### Connect to Azure CLI + +```bash +# Sign in Interactively +az login + +# Sign in with Device Code +az login --use-device-code +``` + +### Set the Active Subscription for Azure CLI + +```bash +# Set Azure CLI Active Subscription +az account set --subscription "" +``` + +> **Important**: Ensure both Azure PowerShell and Azure CLI are authenticated to the **same subscription**. Mismatched subscription contexts can cause deployment failures during the migration process. + +## Clone the Github Repo + +```powershell +# Example using PowerShell for Windows +PS C:\> git clone https://github.com/Azure/ipam.git +PS C:\> cd .\ipam\migrate +PS C:\ipam\migrate> .\migrate.ps1 + +# Example using PowerShell for Linux +PS /> git clone https://github.com/Azure/ipam.git +PS /> cd /ipam/migrate +PS /ipam/migrate> .\migrate.ps1 +``` + +## Migration Methods + +The migration script supports two methods for resource discovery, choose the method that best fits your scenario: + +1. Auto-Discovery (Recommended) + + The script automatically discovers your Azure IPAM resources by analyzing the existing App Service configuration. This is the simplest approach for most deployments. + +2. Manual Resource Specification + + Use a JSON configuration file to specify exact resource details. This method is useful for complex deployments, cross-subscription resources, or when auto-discovery fails. + +## Migration Process (Method 1: Auto-Discovery) + +### Step 1: Run Auto-Discovery Migration + +Execute the migration script with auto-discovery: + +```powershell +.\migrate.ps1 -AppName "your-ipam-app-name" -ResourceGroupName "your-resource-group" +``` + +#### Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `-AppName` | String | Yes | Name of your existing Azure IPAM App Service | +| `-ResourceGroupName` | String | Yes | Resource group containing your App Service | +| `-NoVerify` | Switch | No | Skip resource existence verification **1** | +| `-Force` | Switch | No | Skip user confirmation prompts **2** | + +> **NOTE 1:** Use with caution as this bypasses safety checks. + +> **NOTE 2:** Recommended for automated deployments only. + +### Step 2: Monitor Migration Progress + +The migration script will automatically: + +1. **Discover Resources**: Analyze your App Service to infer all Azure IPAM components +2. **Verify Resources**: Confirm all resources exist and are accessible +3. **Probe Status API**: Determine current container configuration and application health +4. **Deploy Updates**: Update infrastructure using Bicep templates +5. **Build Container** (Private ACR only): Build and push updated container images +6. **Restart App Service**: Apply new configuration + +> **NOTE:** It may take up to 5 minutes for the Azure IPAM services to become available as the new container is downloaded and started by Azure App Services + +## Migration Process (Method 2: Manual Resource Specification) + +### Step 1: Create Override Configuration File + +Create a JSON(C) file specifying your exact resource details: + +```jsonc +[ + { + // Web App Resource Details + "ResourceType": "Microsoft.Web/sites", + "ResourceName": "your-app-service-name", + "ResourceGroup": "your-resource-group", + "Subscription": "your-subscription-id" + }, + { + // App Service Plan Resource Details + "ResourceType": "Microsoft.Web/serverfarms", + "ResourceName": "your-app-service-plan-name", + "ResourceGroup": "your-resource-group", + "Subscription": "your-subscription-id" + }, + { + // Managed Identity Resource Details + "ResourceType": "Microsoft.ManagedIdentity/userAssignedIdentities", + "ResourceName": "your-managed-identity-name", + "ResourceGroup": "your-resource-group", + "Subscription": "your-subscription-id" + }, + { + // Log Analytics Workspace Resource Details + "ResourceType": "Microsoft.OperationalInsights/workspaces", + "ResourceName": "your-log-analytics-workspace-name", + "ResourceGroup": "your-resource-group", + "Subscription": "your-subscription-id" + }, + { + // Cosmos DB Account Resource Details + "ResourceType": "Microsoft.DocumentDB/databaseAccounts", + "ResourceName": "your-cosmos-db-account-name", + "ResourceGroup": "your-resource-group", + "Subscription": "your-subscription-id" + }, + { + // Key Vault Resource Details + "ResourceType": "Microsoft.KeyVault/vaults", + "ResourceName": "your-key-vault-name", + "ResourceGroup": "your-resource-group", + "Subscription": "your-subscription-id" + }, + { + // Container Registry Resource Details (Optional - only if using private ACR) + "ResourceType": "Microsoft.ContainerRegistry/registries", + "ResourceName": "your-container-registry-name", + "ResourceGroup": "your-resource-group", + "Subscription": "your-subscription-id" + } +] +``` + +Save this file as `resources.jsonc` in the same directory as the migration script. + +### Step 2: Run Manual Override Migration + +Execute the migration script with your resource JSON file specified: + +```powershell +.\migrate.ps1 -AppName "your-ipam-app-name" -ResourceGroupName "your-resource-group" -JsonFile "resources.jsonc" +``` + +#### Manual Override Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `-AppName` | String | Yes | Name of your existing Azure IPAM App Service | +| `-ResourceGroupName` | String | Yes | Resource group containing your App Service | +| `-JsonFile` | String | Yes | Path to your JSON override file | +| `-NoVerify` | Switch | No | Skip resource existence verification **1** | +| `-Force` | Switch | No | Skip user confirmation prompts **2** | + +> **NOTE 1:** Use with caution as this bypasses safety checks. + +> **NOTE 2:** Recommended for automated deployments only. + +### Step 3: Monitor Migration Progress + +The migration script will: + +1. **Load Override Configuration**: Read your specified resource details +2. **Verify Resources**: Confirm all specified resources exist and are accessible +3. **Probe Status API**: Determine current container configuration and application health +4. **Deploy Updates**: Update infrastructure using Bicep templates +5. **Build Container** (Private ACR only): Build and push updated container images +6. **Restart App Service**: Apply new configuration + +> **NOTE:** It may take up to 5 minutes for the Azure IPAM services to become available as the new container is downloaded and started by Azure App Services + +## Migration Validation + +### Post-Migration Verification + +After migration completes, verify your Azure IPAM deployment: + +1. **Check Application Health**: + - Verify the App Service is healthy + - ![Check App Service Helath](./images/app_service_health.png) + +2. **Check Container Configuration**: + - Verify the App Service is no longer using Docker Compose + - Confirm single container model + - ![App Service Container Model](./images/app_service_container_model.png) + +3. **Verify Application Accessibility**: + - Navigate to your Azure IPAM URL + - Confirm the application loads successfully + - Test basic functionality (viewing IP spaces, blocks, etc.) + +## Troubleshooting + +### Common Issues and Solutions + +#### Issue: Azure CLI authentication mismatches + +**Solution**: Ensure Azure PowerShell and Azure CLI contexts match + +```powershell +# Check PowerShell context +Get-AzContext +``` + +```bash +# Check Azure CLI context +az account show +``` + +#### Issue: Container build failures (Private ACR) + +**Solution**: + +1. Verify Azure CLI user has ACR push permissions +2. Check ACR resource availability +3. Review build logs in the script output +4. Manually build & push new containers to ACR + +```shell +# App Services Container +az acr build -r -t ipam:latest -f ./Dockerfile.deb . + +# Function Container +az acr build -r -t ipamfunc:latest -f ./Dockerfile.func . +``` + +#### Issue: App Service fails to start after migration + +**Solution**: + +1. Verify that there were no errors during the Migration process +2. Check migration logs and the Deployment logs in the Azure Portal +3. Check App Service logs in Azure Portal + +### Getting Help + +If you encounter issues during migration: + +1. **Review Migration Logs**: Check PowerShell output and log folder for detailed error messages +2. **Validate Prerequisites**: Ensure all [required permissions](#prerequisites) are in place +3. **Check Azure Status**: Verify Azure services are operational ([Azure Status](https://azure.status.microsoft/status)) +4. **Contact Support**: Create an [issue](https://github.com/Azure/ipam/issues/new?template=bug_report.md) in the Azure IPAM GitHub repository + +## Rollback Procedures + +In the evant a migration issue should occur, you can rollback to your previous configuration: + +1. **Navigate to your App Service** in the Azure Portal +2. Go to **Settings** → **Backups** +3. Locate a backup from **brefore** the Migration script was run +4. Click **Restore** icon associated with the target backup timestamp +5. Make sure to select the **existing** deployment slot, and select **Restore Site Configuration** +6. The restoration process can take *up to 30 minutes*, and will stop/start the App Service during that time + +![Restore From Backup](./images/restore_from_backup.png) + +## FAQ + +### Q: How long does migration take? + +**A**: Typical migrations complete in 10-15 minutes, depending on: + +- Number of resources to migrate +- Container build time (for private ACR) +- Network connectivity + +### Q: Will there be downtime during migration? + +**A**: Yes, brief downtime occurs during: + +- App Service configuration updates +- Container image replacement +- App Service restart + +Expected downtime: 10-15 minutes + +### Q: What happens to my existing data? + +**A**: All existing data is preserved: + +- Cosmos DB data remains unchanged +- Key Vault secrets are maintained +- App settings are migrated +- User configurations are preserved + +### Q: What if my deployment uses custom configurations? + +**A**: The script handles most custom configurations automatically, however, for complex setups: + +- Use the JSON override method +- Review script output carefully +- Contact support for assistance diff --git a/docs/migration/images/app_service_container_model.png b/docs/migration/images/app_service_container_model.png new file mode 100644 index 00000000..cb5770d4 Binary files /dev/null and b/docs/migration/images/app_service_container_model.png differ diff --git a/docs/migration/images/app_service_health.png b/docs/migration/images/app_service_health.png new file mode 100644 index 00000000..a9c9eba4 Binary files /dev/null and b/docs/migration/images/app_service_health.png differ diff --git a/docs/migration/images/check_backup_status.png b/docs/migration/images/check_backup_status.png new file mode 100644 index 00000000..c12de67d Binary files /dev/null and b/docs/migration/images/check_backup_status.png differ diff --git a/docs/migration/images/check_recent_backups.png b/docs/migration/images/check_recent_backups.png new file mode 100644 index 00000000..9a133132 Binary files /dev/null and b/docs/migration/images/check_recent_backups.png differ diff --git a/docs/migration/images/configure_custom_backups.png b/docs/migration/images/configure_custom_backups.png new file mode 100644 index 00000000..7aca6ab0 Binary files /dev/null and b/docs/migration/images/configure_custom_backups.png differ diff --git a/docs/migration/images/create_on_demand_backup.png b/docs/migration/images/create_on_demand_backup.png new file mode 100644 index 00000000..6d9e1140 Binary files /dev/null and b/docs/migration/images/create_on_demand_backup.png differ diff --git a/docs/migration/images/restore_from_backup.png b/docs/migration/images/restore_from_backup.png new file mode 100644 index 00000000..2af19197 Binary files /dev/null and b/docs/migration/images/restore_from_backup.png differ diff --git a/docs/migration/images/verify_docker_compose.png b/docs/migration/images/verify_docker_compose.png new file mode 100644 index 00000000..d515ea70 Binary files /dev/null and b/docs/migration/images/verify_docker_compose.png differ diff --git a/docs/troubleshooting/README.md b/docs/troubleshooting/README.md index 5193f49f..8e0d3393 100644 --- a/docs/troubleshooting/README.md +++ b/docs/troubleshooting/README.md @@ -132,6 +132,6 @@ az cosmosdb update --resource-group --name Notes -This flag may have been set by [Azure Policy](https://learn.microsoft.com/azure/governance/policy/overview). You can find more details about this policy [here](https://learn.microsoft.com/azure/cosmos-db/policy-reference#azure-cosmos-db) under *Azure Cosmos DB key based metadata write access should be disabled*. You may need to contact your policy administrator to request an exception for Azure IPAM. +This flag may have been set by [Azure Policy](https://learn.microsoft.com/azure/governance/policy/overview). You can find more details about this policy in the [Azure Policy Built-Ins](https://learn.microsoft.com/azure/cosmos-db/policy-reference#azure-cosmos-db) documentation under *Azure Cosmos DB key based metadata write access should be disabled*. You may need to contact your policy administrator to request an exception for Azure IPAM. Additionally this issue only applies to legacy deployments of Azure IPAM (prior to v3.0.0) as the latest versions use SQL [role-based access control](https://learn.microsoft.com/azure/cosmos-db/how-to-setup-rbac) to read/write data from Cosmos DB. diff --git a/docs/update/README.md b/docs/update/README.md new file mode 100644 index 00000000..38a2ec67 --- /dev/null +++ b/docs/update/README.md @@ -0,0 +1,479 @@ +# Azure IPAM Update Guide + +## Overview + +This guide provides comprehensive instructions for updating existing Azure IPAM deployments to the latest version. The update process preserves your existing data and configuration while updating your deployment with the latest features, security patches, and bug fixes from the Azure IPAM project. + +The Azure IPAM update script (`update.ps1`) supports multiple deployment architectures and automatically detects your current configuration to perform the appropriate update method. + +> **Important**: If your deployment uses **Docker Compose** (legacy deployment method), you cannot use this update script. Docker Compose deployments must use the [Migration Guide](/migration/README.md) to upgrade to modern deployment architecture. The update script will automatically detect Docker Compose deployments and redirect you to the migration guide. + +## Prerequisites + +To successfully update your Azure IPAM deployment, ensure the following prerequisites are met: + +- An Azure Subscription containing your existing Azure IPAM deployment +- The following Azure RBAC Roles: + - [Contributor](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#contributor) or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner) at the Resource Group scope containing your Azure IPAM resources +- [PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) version 7.2.0 or later installed +- [Azure PowerShell](https://learn.microsoft.com/powershell/azure/install-az-ps) version 2.13.0 or later installed +- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) version 2.35.0 or later installed (required only for Private ACR deployments) + +> **NOTE:** The update script requires access to your existing Azure IPAM resources. Ensure you have the necessary permissions to both read the current configuration and restart/redeploy the App Service. + +## Update Methods by Deployment Type + +The update process varies depending on your current Azure IPAM deployment architecture: + +### 1. Public Azure Container Registry (ACR) Deployments + +#### Most Common Deployment Type + +For deployments using the publicly hosted Azure Container Registry (`azureipam.azurecr.io`), updates are handled by simply restarting your Azure App Service or Function App to pull the latest container images. + +- **Update Method**: Container restart +- **Downtime**: Minimal (during restart only) +- **Additional Requirements**: None (fully automated) + +### 2. Private Azure Container Registry (ACR) Deployments + +For deployments using a self-hosted Azure Container Registry within your subscription, the update process includes building new container images with the latest code and pushing them to your private registry. + +- **Update Method**: Container build and deployment +- **Downtime**: During container build and restart +- **Additional Requirements**: Azure CLI authentication (as noted in Prerequisites above) + +### 3. Native ZIP Deploy Deployments + +For "native" deployments that use ZIP Deploy functionality to deploy Python code directly to Azure App Services or Function Apps without containers. + +- **Update Method**: ZIP Deploy (downloads from GitHub releases by default) +- **Downtime**: During ZIP deployment process (~5 minutes) +- **Additional Requirements**: None (downloads automatically from GitHub releases, or you can provide a local ZIP file path) + +## Pre-Update Considerations + +### Backup Recommendation + +Before performing any update, it is strongly recommended to verify that your Azure App Service has recent backups available. While the update process is designed to preserve your existing data and configuration, having a backup ensures you can restore your service if any issues occur. + +To check your backup status: + +1. Navigate to your Azure IPAM App Service in the Azure Portal +2. Go to **Settings** → **Backups** +3. Verify that recent automatic or custom backups are available and show "Succeeded" status + +> **Note**: If your deployment uses Azure Functions on Consumption or Elastic Premium plans, automatic backups are not supported. See [Azure Functions backup documentation](https://learn.microsoft.com/azure/app-service/manage-backup?tabs=portal#does-azure-functions-support-automatic-backups) for details on supported tiers. + +For detailed backup instructions, refer to the [Migration Guide backup section](/migration/README.md#pre-migration-backup). + +### Version Updates & Compatibility + +The update script automatically handles version compatibility, including: + +- **Python Version Updates**: If the target Azure IPAM version uses a different Python version, the script will automatically update your App Service configuration +- **Health Check Configuration**: Missing health check configurations will be automatically added during the update +- **Legacy Detection**: Docker Compose deployments (deprecated) will be detected and the script will redirect you to the migration guide + +## Authentication Setup + +### Authenticate to Azure PowerShell + +Before executing the update script, authenticate to [Azure PowerShell](https://learn.microsoft.com/powershell/azure/install-az-ps) and set the context to the subscription containing your Azure IPAM deployment. + +#### Connect to Azure PowerShell + +```powershell +# Sign in Interactively +Connect-AzAccount + +# Sign in with Device Code +Connect-AzAccount -UseDeviceAuthentication +``` + +> **NOTE:** If you're connecting to an Azure Cloud besides Azure Public (such as Gov, China, etc.), you may need to specify the `-Environment` flag as described in the [Azure PowerShell documentation](https://learn.microsoft.com/powershell/module/az.accounts/connect-azaccount) when using `Connect-AzAccount` + +#### Set the Active Subscription for Azure PowerShell + +```powershell +# Set Azure PowerShell Context +Set-AzContext -Subscription "" + +# Example with Subscription ID +Set-AzContext -Subscription "28b502e2-323f-4e57-98db-743459176557" + +# Example with Subscription Name +Set-AzContext -Subscription "Contoso IPAM Subscription" +``` + +### Authenticate to Azure CLI (Private ACR Only) + +If your Azure IPAM deployment uses a private Azure Container Registry, you must also authenticate to the [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) in addition to Azure PowerShell. This is required because the update script uses `az acr build` to build and push updated container images. + +#### Connect to Azure CLI + +```bash +# Sign in Interactively +az login + +# Sign in with Device Code +az login --use-device-code +``` + +#### Set the Active Subscription for Azure CLI + +```bash +# Set Azure CLI Active Subscription +az account set --subscription "" +``` + +> **Important**: Ensure both Azure PowerShell and Azure CLI are authenticated to the **same subscription**. Mismatched subscription contexts can cause deployment failures during the container build process. + +## Clone the GitHub Repository + +Clone the Azure IPAM repository to access the update script: + +```powershell +# Example using PowerShell for Windows +PS C:\> git clone https://github.com/Azure/ipam.git +PS C:\> cd .\ipam\deploy +PS C:\ipam\deploy> .\update.ps1 + +# Example using PowerShell for Linux +PS /> git clone https://github.com/Azure/ipam.git +PS /> cd /ipam/deploy +PS /ipam/deploy> .\update.ps1 +``` + +## Update Scenarios and Usage + +### Scenario 1: Standard Update (Public ACR or Native ZIP Deploy) + +For most deployments using the public Azure Container Registry or native ZIP deploy, a simple restart or deployment is sufficient: + +```powershell +# Basic update with required parameters +.\update.ps1 -AppName "your-ipam-app" -ResourceGroupName "your-ipam-rg" +``` + +### Scenario 2: Private ACR Update + +For deployments using a private Azure Container Registry, the script will automatically detect this and build new containers: + +```powershell +# Private ACR deployments (automatically detected) +.\update.ps1 -AppName "your-ipam-app" -ResourceGroupName "your-ipam-rg" +``` + +### Scenario 3: ZIP Deploy from Custom Source + +To update from a specific GitHub repository or user (potentially with custom ZIP filename): + +```powershell +# Update from custom GitHub source +.\update.ps1 ` + -AppName "your-ipam-app" ` + -ResourceGroupName "your-ipam-rg" ` + -GitHubUserName "your-corp" ` + -GitHubRepoName "ipam-fork" + +# Update from custom GitHub source with different ZIP file name +.\update.ps1 ` + -AppName "your-ipam-app" ` + -ResourceGroupName "your-ipam-rg" ` + -GitHubUserName "your-corp" ` + -GitHubRepoName "ipam-fork" ` + -ZipFileName "ipam-custom.zip" +``` + +### Scenario 4: ZIP Deploy from Local File + +To update using a local ZIP file (for testing or custom builds): + +```powershell +# Update from local ZIP file +.\update.ps1 ` + -AppName "your-ipam-app" ` + -ResourceGroupName "your-ipam-rg" ` + -ZipFilePath "C:\temp\ipam-custom.zip" +``` + +### Scenario 5: ZIP Deploy to Custom Directory (Optional) + +By default, the script automatically creates a temporary directory for downloading ZIP files. You only need to specify a custom directory if you want to control the download location or preserve the ZIP file after the update: + +```powershell +# Update with custom asset folder (folder must already exist) +# Useful for debugging or when you want to keep the downloaded ZIP file +.\update.ps1 ` + -AppName "your-ipam-app" ` + -ResourceGroupName "your-ipam-rg" ` + -AssetFolder "C:\ipam\assets" +``` + +## Update Script Parameters + +| Parameter | Type | Required | Description | +|----------------------|--------|----------|----------------------------------------------------------------------| +| `-AppName` | String | Yes | Name of your existing Azure IPAM App Service or Function App | +| `-ResourceGroupName` | String | Yes | Resource group containing your App Service | +| `-GitHubUserName` | String | No | GitHub user/organization name for custom repository **1** | +| `-GitHubRepoName` | String | No | GitHub repository name for custom repository **1** | +| `-ZipFileName` | String | No | ZIP file name to download from GitHub **1** | +| `-ZipFilePath` | String | No | Path to local ZIP file for deployment **1** | +| `-AssetFolder` | String | No | Directory to store downloaded ZIP file **2** | + +> **NOTE 1:** Only applicable for native (non-container) deployments. These parameters are ignored for container deployments, which are automatically built from the latest repository code. + +> **NOTE 2:** Script creates a temporary directory automatically if not specified. + +## Update Process Flow + +The update script follows this automated process and will automatically determine the appropriate update method based on your deployment configuration: + +### 1. Application Discovery and Validation + +- Verifies the specified App Service or Function App exists in the subscription +- Detects the application type (App Service vs Function App, Container vs Native) +- Checks for legacy Docker Compose deployments (redirects to migration guide if found) +- Determines deployment architecture (Public ACR, Private ACR, or Native ZIP Deploy) + +### 2. Health Check Configuration + +- Verifies health check endpoint is configured (`/api/status`) +- Automatically adds health check configuration if missing +- Sets health check failure threshold to 2 attempts + +### 3. Deployment Type Detection and Processing + +#### For Public ACR Container Deployments + +- Detects use of public Azure Container Registry (`azureipam.azurecr.io`) by examining `LinuxFxVersion` +- Simply restarts the application to pull latest container images from public registry +- **Process exits here** - no building or ZIP deployment needed + +#### For Private ACR Container Deployments + +- Validates private ACR exists in the same Resource Group +- Verifies Azure CLI version (minimum `2.35.0`) and authentication status +- Ensures Azure PowerShell and Azure CLI contexts match +- Detects container distribution type (Debian/RHEL) by querying application `/api/status` endpoint +- Builds new container images using `az acr build` with appropriate Dockerfile +- Tags and pushes updated images to private registry (`ipam:latest` or `ipamfunc:latest`) +- Restarts the application +- Captures and logs build errors if container build fails + +#### For Native ZIP Deploy Deployments + +- Checks and updates Python version if it has changed between versions +- Downloads latest release ZIP from GitHub (using GitHubUserName/GitHubRepoName parameters) +- Alternatively uses provided local ZIP file if ZipFilePath is specified +- Creates temporary directory for ZIP file if AssetFolder not provided +- Performs ZIP Deploy to App Service using PowerShell cmdlets +- Falls back to Kudu API if standard ZIP Deploy using Azure PowerShell fails +- Handles retry logic for deployment failures (3 attempts) +- Cleans up temporary files + +### 4. Restart and Validation + +- Restarts the App Service or Function App +- Implements retry logic for restart failures +- Provides status updates throughout the process + +## Monitoring the Update Process + +### Update Logs + +The update script generates detailed logs in the `logs` directory: + +- **Update Log**: `logs/update_[timestamp].log` - Complete update process log +- **Error Log**: `logs/error_[timestamp].log` - Detailed error information if issues occur + +### Container Build Monitoring (Private ACR) + +For private ACR deployments, monitor the container build process: + +1. **Build Initiation**: Script uses `az acr build` with `--no-logs` flag and reports build queue status +2. **Build Progress**: Monitor in Azure Portal → Container Registry → Tasks +3. **Error Handling**: If build fails, script extracts Build ID from output and fetches detailed error logs via REST API + +### ZIP Deploy Monitoring (Native Deployments) + +For native deployments: + +1. **GitHub Download**: Script calls GitHub API to get latest release asset download URL +2. **ZIP Upload**: First attempts standard `Publish-AzWebApp`, falls back to Kudu API if needed +3. **Completion**: Allow ~5 minutes for ZIP Deploy process to complete (shown as note in script output) + +## Post-Update Verification + +After the update completes, verify your Azure IPAM deployment: + +### 1. Application Health Check + +Verify the application is running and healthy: + +![Check App Service Helath](./images/app_service_health.png) + +### 2. Version Verification + +Check that the update was successful by querying the status API: + +```powershell +# Check version information via status API +$appUrl = "https://your-ipam-app.azurewebsites.net" +$status = Invoke-RestMethod -Uri "$appUrl/api/status" -Method Get +Write-Host "Current Version: $($status.version)" +``` + +**Example Output:** + +```text +Current Version: 3.5.0 +``` + +**Full API Response Body (Example):** + +```json +{ + "status": "OK", + "version": "3.5.0", + "stack": "AppContainer", + "environment": "AZURE_PUBLIC", + "container": { + "image_id": "debian", + "image_version": "13", + "image_codename": "trixie", + "image_pretty_name": "Debian GNU/Linux 13 (trixie)" + } +} +``` + +Verify the version matches the expected updated version from the [Azure IPAM releases page](https://github.com/Azure/ipam/releases). + +### 3. Functionality Testing + +Perform basic functionality tests: + +- Log into the application +- Verify pre-existing IP address Spaces & Blocks are visible +- Test basic IPAM operations (view networks, reservations, etc.) +- Confirm API endpoints respond as expected + +## Troubleshooting + +### Common Issues and Solutions + +#### Authentication Errors + +**Issue**: PowerShell authentication failures + +```text +ERROR: Azure PowerShell not logged in or no subscription has been selected! +``` + +**Solution**: + +```powershell +Connect-AzAccount +Set-AzContext -Subscription "your-subscription-id" +``` + +#### Private ACR Context Mismatch + +**Issue**: Azure CLI and PowerShell context mismatch + +```text +ERROR: Azure PowerShell and Azure CLI must be set to the same context! +``` + +**Solution**: + +```powershell +# PowerShell +Set-AzContext -Subscription "your-subscription-id" + +# CLI +az account set --subscription "your-subscription-id" +``` + +#### Container Build Failures + +**Issue**: Private ACR container build fails + +**Solution**: + +1. Check build logs in `logs/error_[timestamp].log` (script automatically captures detailed logs) +2. Verify ACR permissions and storage capacity +3. Review Azure Container Registry task logs in Azure Portal +4. Ensure the application's `/api/status` endpoint is accessible for container type detection +5. For manual container build instructions, see the [Contributing Guide](/contributing/README.md#manual-container-builds) + +#### ZIP Deploy Failures + +**Issue**: ZIP Deploy upload failures for native deployments + +**Solution**: + +1. Script automatically retries with Kudu API if standard `Publish-AzWebApp` fails +2. Check App Service deployment logs in Azure Portal +3. Verify sufficient storage space in App Service plan +4. Ensure GitHub release contains the specified ZIP file name (default: `ipam.zip`) + +#### Legacy Docker Compose Detection + +**Issue**: Script detects Docker Compose deployment + +```text +WARNING: Legacy Docker Compose detected! +Please follow the migration guide... +``` + +**Solution**: Use the [Migration Guide](/migration/README.md) instead of the update script + +### Health Check Issues + +If health check configuration fails: + +1. Verify App Service permissions +2. Check that `/api/status` endpoint is responding +3. Manually configure health check in Azure Portal if needed + +### Getting Help + +If you encounter issues not covered in this guide: + +1. Review the update logs for detailed error information +2. Check the [Troubleshooting Guide](/troubleshooting/README.md) +3. Open an issue on the [Azure IPAM GitHub repository](https://github.com/Azure/ipam/issues) +4. Include relevant log files and error messages in your issue report + +## Update Best Practices + +### Maintenance Windows + +- Schedule updates during maintenance windows to minimize impact +- For container deployments, expect 2-5 minutes of downtime during restart +- For ZIP deployments, allow up to 10 minutes for the complete process + +### Testing Updates + +- Test updates in a development or staging environment first +- Verify application functionality before updating production deployments +- Create a snapshot backup before updating critical production environments + +### Frequency Recommendations + +- Review and apply updates monthly or as code/security patches are released +- Watch the [Azure IPAM repository](https://github.com/Azure/ipam) for release notifications + - See [GitHub's guide on configuring repository watch settings](https://docs.github.com/en/account-and-profile/managing-subscriptions-and-notifications-on-github/setting-up-notifications/configuring-notifications#configuring-your-watch-settings-for-an-individual-repository) to set up custom notifications for releases only + +### Rollback Planning + +- Ensure recent backups are available before updating +- Understand your App Service backup and restore procedures +- Consider creating a manual backup before major version updates + +--- + +For additional information about Azure IPAM deployment and management, refer to the [Deployment Guide](/deployment/README.md) diff --git a/docs/update/images/app_service_health.png b/docs/update/images/app_service_health.png new file mode 100644 index 00000000..a9c9eba4 Binary files /dev/null and b/docs/update/images/app_service_health.png differ diff --git a/engine/.devcontainer/devcontainer.json b/engine/.devcontainer/devcontainer.json index 24997ed8..4331c2e2 100644 --- a/engine/.devcontainer/devcontainer.json +++ b/engine/.devcontainer/devcontainer.json @@ -5,9 +5,9 @@ "build": { "dockerfile": "Dockerfile", "context": "..", - "args": { - // Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9 - "VARIANT": "3.9", + "args": { + // Update 'VARIANT' to pick a Python version: 3, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11 + "VARIANT": "3.11", // Options "INSTALL_NODE": "true", "NODE_VERSION": "lts/*" @@ -15,7 +15,7 @@ }, // Set *default* container specific settings.json values on container create. - "settings": { + "settings": { "terminal.integrated.shell.linux": "/bin/bash", "python.pythonPath": "/usr/local/bin/python", "python.languageServer": "Pylance", diff --git a/engine/Dockerfile.deb b/engine/Dockerfile.deb index 53267a0c..c6a11f7c 100644 --- a/engine/Dockerfile.deb +++ b/engine/Dockerfile.deb @@ -1,8 +1,12 @@ -ARG BASE_IMAGE=python:3.9-slim +ARG BASE_IMAGE=python:3.11-slim FROM $BASE_IMAGE +# Set Default Port ARG PORT=80 +# Set Debian Frontend to Non-Interactive +ARG DEBIAN_FRONTEND=noninteractive + # Set Environment Variable ENV PORT=${PORT} diff --git a/engine/Dockerfile.dev b/engine/Dockerfile.dev index 91bd9a44..978a4977 100644 --- a/engine/Dockerfile.dev +++ b/engine/Dockerfile.dev @@ -1,20 +1,29 @@ -ARG VARIANT=3.9-bullseye +ARG VARIANT=3.11-bullseye FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT} +# Set Default Port ARG PORT=8000 +# Set the Working Directory WORKDIR /code +# Set Environment Variable ENV PORT=$PORT +# Copy Requirements File COPY ./requirements.txt /code/requirements.txt +# Upgrade PIP RUN pip install --upgrade pip --progress-bar off +# Install Dependencies RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt +# Copy Application Sources COPY ./app /code/app +# Expose Port EXPOSE $PORT +# Execute Startup Script CMD uvicorn "app.main:app" --reload --host "0.0.0.0" --port $PORT diff --git a/engine/Dockerfile.func b/engine/Dockerfile.func index 64fcda87..1cabef95 100644 --- a/engine/Dockerfile.func +++ b/engine/Dockerfile.func @@ -1,7 +1,8 @@ # To enable ssh & remote debugging on app service change the base image to the one below # FROM mcr.microsoft.com/azure-functions/python:3.0-python3.9-appservice -FROM mcr.microsoft.com/azure-functions/python:4-python3.9-appservice +FROM mcr.microsoft.com/azure-functions/python:4-python3.11-appservice +# Set Azure Function Root Directory & Enable Console Logging ENV AzureWebJobsScriptRoot=/home/site/wwwroot \ AzureFunctionsJobHost__Logging__Console__IsEnabled=true diff --git a/engine/Dockerfile.rhel b/engine/Dockerfile.rhel index 47de00ef..4bcdcbd1 100644 --- a/engine/Dockerfile.rhel +++ b/engine/Dockerfile.rhel @@ -1,6 +1,7 @@ -ARG BASE_IMAGE=registry.access.redhat.com/ubi8/python-39 +ARG BASE_IMAGE=registry.access.redhat.com/ubi8/python-311 FROM $BASE_IMAGE +# Set Default Port ARG PORT=8080 # Set Environment Variable @@ -25,7 +26,7 @@ RUN pip install --upgrade pip --progress-bar off RUN pip install --no-cache-dir -r ./requirements.lock.txt --progress-bar off # Copy Application Scripts & Sources -ADD ./app ./appDockerfile +ADD ./app ./app ADD ./init.sh . # Set Script Execute Permissions diff --git a/engine/app/routers/tool.py b/engine/app/routers/tool.py index d7fe4421..7027023c 100644 --- a/engine/app/routers/tool.py +++ b/engine/app/routers/tool.py @@ -83,19 +83,24 @@ async def next_available_subnet( for subnet in target['subnets']: vnet_all_cidrs.append(subnet['prefix']) - vnet_set = IPSet(target['prefixes']) + # vnet_set = IPSet(target['prefixes']) + vnet_set = [IPNetwork(x) for x in target['prefixes']] + # reserved_set = IPSet(vnet_all_cidrs) reserved_set = IPSet(vnet_all_cidrs) - available_set = vnet_set ^ reserved_set + # available_set = vnet_set ^ reserved_set + available_set = [item for sublist in [x.iter_cidrs() for x in list(filter(None, [(IPSet(x) - reserved_set) for x in vnet_set]))] for item in sublist] available_slicer = slice(None, None, -1) if req.reverse_search else slice(None) next_selector = -1 if req.reverse_search else 0 if req.smallest_cidr: - cidr_list = list(filter(lambda x: x.prefixlen <= req.size, available_set.iter_cidrs()[available_slicer])) + # cidr_list = list(filter(lambda x: x.prefixlen <= req.size, available_set.iter_cidrs()[available_slicer])) + cidr_list = list(filter(lambda x: x.prefixlen <= req.size, available_set[available_slicer])) min_mask = max(map(lambda x: x.prefixlen, cidr_list)) available_block = next((net for net in list(filter(lambda network: network.prefixlen == min_mask, cidr_list))), None) else: - available_block = next((net for net in list(available_set.iter_cidrs())[available_slicer] if net.prefixlen <= req.size), None) + # available_block = next((net for net in list(available_set.iter_cidrs())[available_slicer] if net.prefixlen <= req.size), None) + available_block = next((net for net in available_set[available_slicer] if net.prefixlen <= req.size), None) if not available_block: raise HTTPException(status_code=500, detail="Subnet of requested size unavailable in target virtual network.") diff --git a/engine/app/version.json b/engine/app/version.json index fe4560ae..13097fed 100644 --- a/engine/app/version.json +++ b/engine/app/version.json @@ -1,4 +1,4 @@ { "app": "3.5.0", - "python": "3.9" + "python": "3.11" } diff --git a/lb/Dockerfile b/lb/Dockerfile index 7358e7ce..2ef4c54b 100644 --- a/lb/Dockerfile +++ b/lb/Dockerfile @@ -1,10 +1,11 @@ -# registry.access.redhat.com/ubi8/nginx-122 ARG BASE_IMAGE=nginx:alpine FROM $BASE_IMAGE +# Create File Links RUN ln -sf /dev/stdout /var/log/nginx/access.log RUN ln -sf /dev/stderr /var/log/nginx/error.log +# Copy NGINX Config COPY nginx.conf /etc/nginx/nginx.conf # Run script uses standard ways to run the application diff --git a/lb/Dockerfile.deb b/lb/Dockerfile.deb new file mode 100644 index 00000000..8cb034c5 --- /dev/null +++ b/lb/Dockerfile.deb @@ -0,0 +1,12 @@ +ARG BASE_IMAGE=nginx:bookworm +FROM $BASE_IMAGE + +# Create File Links +RUN ln -sf /dev/stdout /var/log/nginx/access.log +RUN ln -sf /dev/stderr /var/log/nginx/error.log + +# Copy NGINX Config +COPY nginx.conf /etc/nginx/nginx.conf + +# Run script uses standard ways to run the application +CMD nginx -g "daemon off;" diff --git a/lb/Dockerfile.dev b/lb/Dockerfile.dev index e405144f..b633cc9c 100644 --- a/lb/Dockerfile.dev +++ b/lb/Dockerfile.dev @@ -1,10 +1,11 @@ -# registry.access.redhat.com/ubi8/nginx-120 ARG BASE_IMAGE=nginx:alpine FROM $BASE_IMAGE +# Create File Links RUN ln -sf /dev/stdout /var/log/nginx/access.log RUN ln -sf /dev/stderr /var/log/nginx/error.log +# Copy NGINX Config COPY nginx.dev.conf /etc/nginx/nginx.conf # Run script uses standard ways to run the application diff --git a/lb/Dockerfile.rhel b/lb/Dockerfile.rhel new file mode 100644 index 00000000..529ea07a --- /dev/null +++ b/lb/Dockerfile.rhel @@ -0,0 +1,12 @@ +ARG BASE_IMAGE=registry.access.redhat.com/ubi8/nginx-124 +FROM $BASE_IMAGE + +# Create File Links +RUN ln -sf /dev/stdout /var/log/nginx/access.log +RUN ln -sf /dev/stderr /var/log/nginx/error.log + +# Copy NGINX Config +COPY nginx.conf /etc/nginx/nginx.conf + +# Run script uses standard ways to run the application +CMD nginx -g "daemon off;" diff --git a/migrate/main.bicep b/migrate/main.bicep new file mode 100644 index 00000000..4901e49c --- /dev/null +++ b/migrate/main.bicep @@ -0,0 +1,124 @@ +// Global parameters +targetScope = 'subscription' + +// Existing Resource Object for Migration +type resourceObject = { + appService: { + appServiceName: string + appServiceRG: string + } + appServicePlan: { + appServicePlanName: string + appServicePlanRG: string + } + cosmosAccount: { + cosmosAccountName: string + cosmosAccountRG: string + cosmosContainerName: string + cosmosDatabaseName: string + } + keyVault: { + keyVaultName: string + keyVaultRG: string + } + logAnalytics: { + workspaceName: string + workspaceRG: string + } + managedIdentity: { + managedIdentityName: string + managedIdentityRG: string + } + containerRegistry: { + containerRegistryName: string? + containerRegistryRG: string? + } +} + +@description('Deployment Location') +param location string = deployment().location + +@description('Azure Cloud Enviroment') +param azureCloud string = 'AZURE_PUBLIC' + +@description('Flag to Deploy Private Container Registry') +param privateAcr bool = false + +@description('IPAM Resources') +param resourceDetails resourceObject + +// Log Analytics Workspace +module logAnalyticsWorkspace './modules/logAnalyticsWorkspace.bicep' ={ + name: 'logAnalyticsWorkspaceModule' + scope: resourceGroup(resourceDetails.logAnalytics.workspaceRG) + params: { + location: location + workspaceName: resourceDetails.logAnalytics.workspaceName + } +} + +// Managed Identity +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' existing = { + name: resourceDetails.managedIdentity.managedIdentityName + scope: resourceGroup(resourceDetails.managedIdentity.managedIdentityRG) +} + +// Key Vault +module keyVault './modules/keyVault.bicep' = { + name: 'keyVaultModule' + scope: resourceGroup(resourceDetails.keyVault.keyVaultRG) + params: { + location: location + keyVaultName: resourceDetails.keyVault.keyVaultName + identityPrincipalId: managedIdentity.properties.principalId + identityClientId: managedIdentity.properties.clientId + workspaceId: logAnalyticsWorkspace.outputs.workspaceId + } +} + +// Cosmos DB +module cosmos './modules/cosmos.bicep' = { + name: 'cosmosModule' + scope: resourceGroup(resourceDetails.cosmosAccount.cosmosAccountRG) + params: { + cosmosAccountName: resourceDetails.cosmosAccount.cosmosAccountName + workspaceId: logAnalyticsWorkspace.outputs.workspaceId + principalId: managedIdentity.properties.principalId + } +} + +// Container Registry +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-12-01' existing = if (privateAcr) { + name: resourceDetails.containerRegistry.containerRegistryName! + scope: resourceGroup(resourceDetails.containerRegistry.containerRegistryRG!) +} + +// App Service +module appService './modules/appService.bicep' = { + scope: resourceGroup(resourceDetails.appService.appServiceRG) + name: 'appServiceModule' + params: { + location: location + azureCloud: azureCloud + appServiceName: resourceDetails.appService.appServiceName + appServicePlanName: resourceDetails.appServicePlan.appServicePlanName + keyVaultUri: keyVault.outputs.keyVaultUri + cosmosDbUri: cosmos.outputs.cosmosDocumentEndpoint + databaseName: resourceDetails.cosmosAccount.cosmosDatabaseName + containerName: resourceDetails.cosmosAccount.cosmosContainerName + managedIdentityId: managedIdentity.id + managedIdentityClientId: managedIdentity.properties.clientId + workspaceId: logAnalyticsWorkspace.outputs.workspaceId + privateAcr: privateAcr + privateAcrUri: privateAcr ? containerRegistry!.properties.loginServer : '' + } +} + +// Outputs +// output suffix string = uniqueString(guid) +// output subscriptionId string = subscription().subscriptionId +// output resourceGroupName string = resourceGroup.name +// output appServiceName string = deployAsFunc ? resourceNames.functionName : resourceNames.appServiceName +// output appServiceHostName string = deployAsFunc ? functionApp.outputs.functionAppHostName : appService.outputs.appServiceHostName +// output acrName string = privateAcr ? containerRegistry.outputs.acrName : '' +// output acrUri string = privateAcr ? containerRegistry.outputs.acrUri : '' diff --git a/migrate/main.parameters.example.bicepparam b/migrate/main.parameters.example.bicepparam new file mode 100644 index 00000000..291868a8 --- /dev/null +++ b/migrate/main.parameters.example.bicepparam @@ -0,0 +1,34 @@ +using './main.bicep' + +param resourceDetails = { + keyVault: { + keyVaultRG: '' + keyVaultName: '' + } + logAnalytics: { + workspaceRG: '' + workspaceName: '' + } + managedIdentity: { + managedIdentityRG: '' + managedIdentityName: '' + } + appServicePlan: { + appServicePlanRG: '' + appServicePlanName: '' + } + appService: { + appServiceRG: '' + appServiceName: '' + } + cosmosAccount: { + cosmosAccountRG: '' + cosmosAccountName: '' + cosmosDatabaseName: ' | ipam-db' + cosmosContainerName: ' | ipam-ctr' + } + containerRegistry: { + containerRegistryRG: ' | NULL' + containerRegistryName: ' | NULL' + } +} diff --git a/migrate/migrate.ps1 b/migrate/migrate.ps1 new file mode 100644 index 00000000..2513c509 --- /dev/null +++ b/migrate/migrate.ps1 @@ -0,0 +1,1540 @@ +############################################################################################################### +## +## Azure IPAM Solution Migration Script +## +## This script migrates existing Azure IPAM deployments from Docker Compose to modern Azure infrastructure +## by discovering current resources, validating their configuration, and deploying updated Bicep templates +## while preserving existing data and configuration settings. +## +############################################################################################################### + +# Set minimum version requirements +#Requires -Version 7.2 +#Requires -Modules @{ ModuleName="Az.Accounts"; ModuleVersion="2.16.0" } +#Requires -Modules @{ ModuleName="Az.KeyVault"; ModuleVersion="5.2.1" } +#Requires -Modules @{ ModuleName="Az.Monitor"; ModuleVersion="5.1.0" } +#Requires -Modules @{ ModuleName="Az.Resources"; ModuleVersion="6.16.0" } +#Requires -Modules @{ ModuleName="Az.Websites"; ModuleVersion="3.2.0" } +#Requires -Modules @{ ModuleName="Az.ContainerRegistry"; ModuleVersion="4.1.3" } + +# Intake and set global parameters +param( + [Parameter(ValueFromPipelineByPropertyName = $true, + Mandatory = $true)] + [string] + $AppName, + + [Parameter(ValueFromPipelineByPropertyName = $true, + Mandatory = $true)] + [string] + $ResourceGroupName, + + [Parameter(ValueFromPipelineByPropertyName = $true, + Mandatory = $false)] + [ValidateScript({ + if (-Not ($_ | Test-Path) ) { + throw [System.ArgumentException]::New("Target file or does not exist.") + } + if (-Not ($_ | Test-Path -PathType Leaf) ) { + throw [System.ArgumentException]::New("The 'JsonFile' argument must be a file, folder paths are not allowed.") + } + if ($_ -notmatch "(\.json|\.jsonc)$") { + throw [System.ArgumentException]::New("The file specified in the 'JsonFile' argument must be of type json or jsonc.") + } + try { + $content = Get-Content -Path $_ -Raw -ErrorAction Stop + $null = ConvertFrom-Json -InputObject $content -ErrorAction Stop + } + catch { + throw [System.ArgumentException]::New("The file specified in the 'JsonFile' argument contains invalid JSON.") + } + return $true + })] + [System.IO.FileInfo] + $JsonFile, + + [Parameter(ValueFromPipelineByPropertyName = $true, + Mandatory = $false)] + [switch] + $NoVerify, + + [Parameter(ValueFromPipelineByPropertyName = $true, + Mandatory = $false)] + [switch] + $Force +) + +# Root Directory +$ROOT_DIR = (Get-Item $($MyInvocation.MyCommand.Path)).Directory.Parent.FullName + +# Minimum Required Azure CLI Version - Required for ACR build functionality +$MIN_AZ_CLI_VER = [System.Version]'2.35.0' + +# Set preference variables +$ErrorActionPreference = "Stop" + +# Set Log File Location +$logPath = Join-Path -Path $ROOT_DIR -ChildPath "logs" +New-Item -ItemType Directory -Path $logpath -Force | Out-Null + +# Initialize detailed logging system +$timestamp = Get-Date -Format "yyyyMMdd-HHmmss" +$logFile = Join-Path -Path $logPath -ChildPath "migrate_$timestamp.log" + +# Logging function +function Write-LogFile { + param( + [string]$Message, + [string]$Level = "INFO", + [switch]$ToConsole, + [System.Management.Automation.ErrorRecord]$ErrorRecord = $null + ) + + $logEntry = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [$Level] $Message" + + # Always write to log file + Add-Content -Path $logFile -Value $logEntry -ErrorAction SilentlyContinue + + # Write detailed error information for ERROR level entries + if ($Level -eq "ERROR" -and $ErrorRecord) { + $errorDetails = @" +[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [ERROR-DETAILS] Exception Type: $($ErrorRecord.Exception.GetType().FullName) +[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [ERROR-DETAILS] Exception Message: $($ErrorRecord.Exception.Message) +[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [ERROR-DETAILS] Stack Trace: $($ErrorRecord.ScriptStackTrace) +[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [ERROR-DETAILS] Position: $($ErrorRecord.InvocationInfo.PositionMessage) +[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [ERROR-DETAILS] Command: $($ErrorRecord.InvocationInfo.MyCommand) +[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [ERROR-DETAILS] Line: $($ErrorRecord.InvocationInfo.ScriptLineNumber) +[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [ERROR-DETAILS] ---------------------------------------- +"@ + Add-Content -Path $logFile -Value $errorDetails -ErrorAction SilentlyContinue + } + + # Optionally write to console (for backwards compatibility) + if ($ToConsole) { + switch ($Level) { + "ERROR" { Write-Host $Message -ForegroundColor Red } + "WARNING" { Write-Host $Message -ForegroundColor Yellow } + "SUCCESS" { Write-Host $Message -ForegroundColor Green } + default { Write-Host $Message } + } + } +} + +# Override error handling to capture detailed logs +$originalErrorActionPreference = $ErrorActionPreference +$ErrorActionPreference = "Continue" + +# Trap for unhandled errors +trap { + Write-LogFile -Message "UNHANDLED ERROR: $($_.Exception.Message)" -Level "ERROR" -ErrorRecord $_ + $ErrorActionPreference = $originalErrorActionPreference + throw $_ +} + +$ErrorActionPreference = $originalErrorActionPreference + +# Global variables set during resource discovery +$location = $null # Azure region extracted from source WebApp +$azureCloud = $null # Azure cloud environment (AZURE_PUBLIC, AZURE_US_GOV, etc.) +$privateAcr = $false # Flag indicating if private ACR is used vs public registry + +# Helper Functions +function Get-UserConfirmation { + param( + [Parameter(Mandatory = $true)] + [string]$Message, + [string]$PromptText = "Enter Y to continue or N to exit (Y/N)" + ) + + Write-Host $Message -ForegroundColor Yellow + do { + $confirmation = Read-Host $PromptText + switch ($confirmation.Trim().ToUpper()) { + { $_ -in @("Y", "YES") } { + Write-Host + return $true + } + { $_ -in @("N", "NO") } { + Write-Host + return $false + } + default { + Write-Host "Invalid input. Please enter Y or N." -ForegroundColor Red + } + } + } while ($true) +} + +function New-ResourceId { + param( + [Parameter(Mandatory = $true)] + [PSCustomObject]$Resource + ) + + # Validate input object + if ($null -eq $Resource) { + throw [System.ArgumentNullException]::new("Resource", "Resource parameter cannot be null") + } + + # Validate required properties exist + $requiredProperties = @('Subscription', 'ResourceGroup', 'ResourceType', 'ResourceName') + $missingProperties = @() + + foreach ($property in $requiredProperties) { + if (-not $Resource.PSObject.Properties[$property]) { + $missingProperties += $property + } + elseif ([string]::IsNullOrWhiteSpace($Resource.$property)) { + throw [System.ArgumentException]::new("Resource property '$property' cannot be null or empty", "Resource") + } + } + + if ($missingProperties.Count -gt 0) { + $availableProperties = $Resource.PSObject.Properties.Name -join ', ' + throw [System.ArgumentException]::new("Resource object is missing required properties: $($missingProperties -join ', '). Available properties: $availableProperties", "Resource") + } + + return "/subscriptions/$($Resource.Subscription)/resourceGroups/$($Resource.ResourceGroup)/providers/$($Resource.ResourceType)/$($Resource.ResourceName)" +} + +function Get-Subdomain { + param( + [Parameter(Mandatory = $true)] + [string]$Url + ) + + try { + $uri = [System.Uri]::new($Url) + $serviceName = $uri.Host.Split('.')[0] + + if ([string]::IsNullOrWhiteSpace($serviceName)) { + throw [System.ArgumentException]::new("Could not extract subdomain from URL hostname: '$($uri.Host)'", "Url") + } + + return $serviceName + } + catch [System.UriFormatException] { + throw [System.ArgumentException]::new("Invalid URL format: '$Url'. Please verify the URL is valid.", "Url") + } +} + +function Get-ResourceDetailsFromId { + param( + [Parameter(Mandatory = $true)] + [string]$ResourceId + ) + + if ([string]::IsNullOrWhiteSpace($ResourceId)) { + throw [System.ArgumentException]::new("ResourceId cannot be null or empty", "ResourceId") + } + + $parts = $ResourceId.Split('/') + if ($parts.Length -lt 9) { + throw [System.ArgumentException]::new("Invalid ResourceId format: '$ResourceId'", "ResourceId") + } + + return [PSCustomObject]@{ + SubscriptionId = $parts[2] + ResourceGroupName = $parts[4] + ResourceType = "$($parts[6])/$($parts[7])" + ResourceName = $parts[8] + FullResourceId = $ResourceId + } +} + +Function Get-BuildLogs { + Param( + [Parameter(Mandatory=$true)] + [string]$SubscriptionId, + [Parameter(Mandatory=$true)] + [string]$ResourceGroupName, + [Parameter(Mandatory=$true)] + [string]$RegistryName, + [Parameter(Mandatory=$true)] + [string]$BuildId + ) + + # Azure management endpoint mapping for different cloud environments + $msArmMap = @{ + AZURE_PUBLIC = "management.azure.com" + AZURE_US_GOV = "management.usgovcloudapi.net" + AZURE_US_GOV_SECRET = "management.azure.microsoft.scloud" + AZURE_GERMANY = "management.microsoftazure.de" + AZURE_CHINA = "management.chinacloudapi.cn" + }; + + $accessToken = (Get-AzAccessToken).Token + + $response = Invoke-RestMethod ` + -Method POST ` + -Uri "https://$($msArmMap[$script:azureCloud])/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.ContainerRegistry/registries/$RegistryName/runs/$BuildId/listLogSasUrl?api-version=2019-04-01" ` + -Authentication Bearer ` + -Token $accessToken + + $logLink = $response.logLink + + $logs = Invoke-RestMethod ` + -Method GET ` + -Uri $logLink + + return $logs +} + +function Get-WebAppDetails { + param( + [Parameter(Mandatory = $true)] + [string]$ResourceGroupName, + [Parameter(Mandatory = $true)] + [string]$AppName + ) + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "Discovering Azure IPAM Resources..." -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Blue + + $missingResources = @() + $discoveredCount = 0 + $totalExpectedResources = 7 # All resources including Container Registry + + # Get the WebApp resource (single call) + Write-Host "🔍 Discovering Web App '$AppName'..." -ForegroundColor Cyan -NoNewline + Write-LogFile -Message "Starting Web App discovery for '$AppName' in resource group '$ResourceGroupName'" -Level "INFO" + try { + $webApp = Get-AzWebApp -ResourceGroupName $ResourceGroupName -Name $AppName -ErrorAction Stop + Write-Host " ✅ Found" -ForegroundColor Green + Write-LogFile -Message "Successfully discovered Web App '$AppName'" -Level "SUCCESS" + $discoveredCount++ + } + catch { + Write-Host " ❌ Missing" -ForegroundColor Red + Write-LogFile -Message "Failed to discover Web App '$AppName': $($_.Exception.Message)" -Level "ERROR" -ErrorRecord $_ + $missingResources += [PSCustomObject]@{ + ResourceType = "Web App" + ResourceName = $AppName + Error = $_.Exception.Message + } + throw [System.InvalidOperationException]::new("Failed to find Web App '$AppName' in resource group '$ResourceGroupName': $($_.Exception.Message)", $_.Exception) + } + + # Extract User-Assigned Managed Identity - Required for Azure IPAM authentication + Write-Host "🔍 Discovering User-Assigned Managed Identity..." -ForegroundColor Cyan -NoNewline + Write-LogFile -Message "Starting Managed Identity discovery for WebApp '$AppName'" -Level "INFO" + try { + $userAssignedIdentities = $webApp.Identity.UserAssignedIdentities + if ($userAssignedIdentities.Count -eq 0) { + throw [System.InvalidOperationException]::new("No User-Assigned Managed Identity found for WebApp '$AppName' in resource group '$ResourceGroupName'. This is required for migration.") + } + # Take first identity if multiple exist - Azure IPAM typically uses single identity + $managedIdentityResourceId = $userAssignedIdentities.Keys | Select-Object -First 1 + $managedIdentityName = $managedIdentityResourceId.Split('/')[-1] + Write-Host " ✅ Found '$managedIdentityName'" -ForegroundColor Green + Write-LogFile -Message "Successfully discovered Managed Identity '$managedIdentityName' with Resource ID: $managedIdentityResourceId" -Level "SUCCESS" + $discoveredCount++ + } + catch { + Write-Host " ❌ Missing" -ForegroundColor Red + Write-LogFile -Message "Failed to discover Managed Identity: $($_.Exception.Message)" -Level "ERROR" -ErrorRecord $_ + $missingResources += [PSCustomObject]@{ + ResourceType = "Managed Identity" + ResourceName = "Unknown" + Error = $_.Exception.Message + } + throw $_.Exception + } + + # Extract Log Analytics Workspace ResourceId from diagnostic settings (required) + Write-Host "🔍 Discovering Log Analytics Workspace configuration..." -ForegroundColor Cyan -NoNewline + Write-LogFile -Message "Starting Log Analytics Workspace discovery from diagnostic settings" -Level "INFO" + try { + $diagnosticSettings = Get-AzDiagnosticSetting -ResourceId $webApp.Id -ErrorAction SilentlyContinue + if (-not $diagnosticSettings) { + throw [System.InvalidOperationException]::new("No diagnostic settings found for WebApp '$AppName'. Log Analytics configuration is required for migration.") + } + + $logAnalyticsWorkspaceId = $diagnosticSettings | + ForEach-Object { $_.WorkspaceId } | + Where-Object { $_ } | + Select-Object -First 1 + + if (-not $logAnalyticsWorkspaceId) { + throw [System.InvalidOperationException]::new("No Log Analytics Workspace configured in diagnostic settings for WebApp '$AppName'. This is required for migration.") + } + $logAnalyticsWorkspaceName = $logAnalyticsWorkspaceId.Split('/')[-1] + Write-Host " ✅ Found '$logAnalyticsWorkspaceName'" -ForegroundColor Green + Write-LogFile -Message "Successfully discovered Log Analytics Workspace '$logAnalyticsWorkspaceName' with Resource ID: $logAnalyticsWorkspaceId" -Level "SUCCESS" + $discoveredCount++ + } + catch { + Write-Host " ❌ Missing" -ForegroundColor Red + Write-LogFile -Message "Failed to discover Log Analytics Workspace: $($_.Exception.Message)" -Level "ERROR" -ErrorRecord $_ + $missingResources += [PSCustomObject]@{ + ResourceType = "Log Analytics Workspace" + ResourceName = "Unknown" + Error = $_.Exception.Message + } + throw $_.Exception + } + + # Get app settings once for efficiency across multiple lookups + $appSettings = $webApp.SiteConfig.AppSettings + + # Extract CosmosDB Account Name from COSMOS_URL and find the actual resource + # Azure IPAM stores all IP address data in Cosmos DB + Write-Host "🔍 Discovering Cosmos DB Account configuration..." -ForegroundColor Cyan -NoNewline + Write-LogFile -Message "Starting Cosmos DB Account discovery" -Level "INFO" + try { + $cosmosSetting = $appSettings | Where-Object { $_.Name -eq "COSMOS_URL" } + if (-not $cosmosSetting) { + throw [System.ArgumentException]::new("No COSMOS_URL environment variable found for WebApp '$AppName'. This is required for migration.", "COSMOS_URL") + } + Write-LogFile -Message "Found COSMOS_URL setting: $($cosmosSetting.Value)" -Level "INFO" + $cosmosDbAccountName = Get-Subdomain -Url $cosmosSetting.Value + Write-LogFile -Message "Extracted Cosmos DB account name: $cosmosDbAccountName" -Level "INFO" + + # Get current Azure PowerShell context for subscription ID + $azContext = Get-AzContext + if (-not $azContext -or -not $azContext.Subscription) { + throw [System.InvalidOperationException]::new("No active Azure PowerShell context found. Please run Connect-AzAccount to authenticate.") + } + $subscriptionId = $azContext.Subscription.Id + Write-LogFile -Message "Using subscription ID: $subscriptionId" -Level "INFO" + + # Search all Cosmos DB accounts in subscription since resource group may differ + # This handles scenarios where Cosmos DB was deployed in a different RG + $cosmosApiUri = "https://management.azure.com/subscriptions/$subscriptionId/providers/Microsoft.DocumentDB/databaseAccounts?api-version=2025-04-15" + Write-LogFile -Message "Querying Cosmos DB accounts via REST API: $cosmosApiUri" -Level "INFO" + $cosmosResponse = Invoke-AzRestMethod -Uri $cosmosApiUri -Method GET + + if ($cosmosResponse.StatusCode -ne 200) { + throw [System.InvalidOperationException]::new("Failed to retrieve Cosmos DB accounts from subscription '$subscriptionId'. Status: $($cosmosResponse.StatusCode)") + } + + $cosmosData = $cosmosResponse.Content | ConvertFrom-Json + $cosmosAccounts = $cosmosData.value + Write-LogFile -Message "Found $($cosmosAccounts.Count) Cosmos DB accounts in subscription" -Level "INFO" + + if ($cosmosAccounts.Count -eq 0) { + throw [System.InvalidOperationException]::new("No Cosmos DB accounts found in subscription '$subscriptionId'.") + } + + # Prioritize exact name match, fallback to hostname match for flexibility + $matchedAccount = $cosmosAccounts | Where-Object { $_.name -eq $cosmosDbAccountName } + Write-LogFile -Message "Exact name match result: $(if ($matchedAccount) { $matchedAccount.name } else { 'No match' })" -Level "INFO" + + if (-not $matchedAccount) { + # If no exact match, try to find by URL hostname match + $targetHostname = ([System.Uri]::new($cosmosSetting.Value)).Host + Write-LogFile -Message "Attempting hostname match for: $targetHostname" -Level "INFO" + $matchedAccount = $cosmosAccounts | Where-Object { + $_.properties.documentEndpoint -and ([System.Uri]::new($_.properties.documentEndpoint)).Host -eq $targetHostname + } + Write-LogFile -Message "Hostname match result: $(if ($matchedAccount) { $matchedAccount.name } else { 'No match' })" -Level "INFO" + } + + if (-not $matchedAccount) { + $availableAccounts = $cosmosAccounts | ForEach-Object { "$($_.name) (Endpoint: $($_.properties.documentEndpoint))" } + Write-LogFile -Message "Available Cosmos DB accounts: $($availableAccounts -join ', ')" -Level "ERROR" + throw [System.InvalidOperationException]::new("No Cosmos DB account found matching name '$cosmosDbAccountName' or URL '$($cosmosSetting.Value)'. Available accounts: $($availableAccounts -join ', ')") + } + + Write-Host " ✅ Found '$($matchedAccount.name)'" -ForegroundColor Green + Write-LogFile -Message "Successfully discovered Cosmos DB Account '$($matchedAccount.name)' with Resource ID: $($matchedAccount.id)" -Level "SUCCESS" + $cosmosDbResourceId = $matchedAccount.id + $discoveredCount++ + } + catch { + Write-Host " ❌ Missing" -ForegroundColor Red + Write-LogFile -Message "Failed to discover Cosmos DB Account: $($_.Exception.Message)" -Level "ERROR" -ErrorRecord $_ + $missingResources += [PSCustomObject]@{ + ResourceType = "Cosmos DB Account" + ResourceName = if ($cosmosDbAccountName) { $cosmosDbAccountName } else { "Unknown" } + Error = $_.Exception.Message + } + throw [System.InvalidOperationException]::new("Failed to find Cosmos DB resource for URL '$($cosmosSetting.Value)': $($_.Exception.Message)", $_.Exception) + } + + # Extract KeyVault Name from KEYVAULT_URL and find the actual resource (required) + Write-Host "🔍 Discovering Key Vault configuration..." -ForegroundColor Cyan -NoNewline + try { + $keyVaultSetting = $appSettings | Where-Object { $_.Name -eq "KEYVAULT_URL" } + if (-not $keyVaultSetting) { + throw [System.ArgumentException]::new("No KEYVAULT_URL environment variable found for WebApp '$AppName'. This is required for migration.", "KEYVAULT_URL") + } + $keyVaultName = Get-Subdomain -Url $keyVaultSetting.Value + + # Search all Key Vaults in subscription since resource group may differ + $keyVaults = Get-AzKeyVault -ErrorAction Stop + + if ($keyVaults.Count -eq 0) { + throw [System.InvalidOperationException]::new("No Key Vaults found in subscription '$subscriptionId'.") + } + + # Prioritize exact name match, fallback to hostname match for flexibility + $matchedVault = $keyVaults | Where-Object { $_.VaultName -eq $keyVaultName } + + if (-not $matchedVault) { + # If no exact match, try to find by URL hostname match + $targetHostname = ([System.Uri]::new($keyVaultSetting.Value)).Host + $matchedVault = $keyVaults | Where-Object { + $_.VaultUri -and ([System.Uri]::new($_.VaultUri)).Host -eq $targetHostname + } + } + + if (-not $matchedVault) { + $availableVaults = $keyVaults | ForEach-Object { "$($_.VaultName) (URI: $($_.VaultUri))" } + throw [System.InvalidOperationException]::new("No Key Vault found matching name '$keyVaultName' or URL '$($keyVaultSetting.Value)'. Available vaults: $($availableVaults -join ', ')") + } + + Write-Host " ✅ Found '$($matchedVault.VaultName)'" -ForegroundColor Green + $keyVaultResourceId = $matchedVault.ResourceId + $discoveredCount++ + } + catch { + Write-Host " ❌ Missing" -ForegroundColor Red + $missingResources += [PSCustomObject]@{ + ResourceType = "Key Vault" + ResourceName = if ($keyVaultName) { $keyVaultName } else { "Unknown" } + Error = $_.Exception.Message + } + throw [System.InvalidOperationException]::new("Failed to find Key Vault resource for URL '$($keyVaultSetting.Value)': $($_.Exception.Message)", $_.Exception) + } + + # Extract App Service Plan name (required) + Write-Host "🔍 Discovering App Service Plan configuration..." -ForegroundColor Cyan -NoNewline + try { + if (-not $webApp.ServerFarmId) { + throw [System.InvalidOperationException]::new("No App Service Plan found for WebApp '$AppName'. This is required for migration.") + } + $appServicePlanName = $webApp.ServerFarmId.Split('/')[-1] + Write-Host " ✅ Found '$appServicePlanName'" -ForegroundColor Green + $discoveredCount++ + } + catch { + Write-Host " ❌ Missing" -ForegroundColor Red + $missingResources += [PSCustomObject]@{ + ResourceType = "App Service Plan" + ResourceName = "Unknown" + Error = $_.Exception.Message + } + throw $_.Exception + } + + # Check for Container Registry configuration from Docker Compose + # Azure IPAM can use either public azureipam.azurecr.io or private ACR + Write-Host "🔍 Discovering Container Registry configuration..." -ForegroundColor Cyan -NoNewline + $containerRegistryResourceId = $null + try { + # Get Docker Compose file content from LinuxFxVersion if available + $dockerComposeContent = $null + if ($webApp.SiteConfig.LinuxFxVersion -and $webApp.SiteConfig.LinuxFxVersion.Contains("COMPOSE|")) { + # Extract and decode the Base64 Docker Compose content + $base64Content = $webApp.SiteConfig.LinuxFxVersion.Substring($webApp.SiteConfig.LinuxFxVersion.IndexOf("COMPOSE|") + 8) + try { + $dockerComposeBytes = [System.Convert]::FromBase64String($base64Content) + $dockerComposeContent = [System.Text.Encoding]::UTF8.GetString($dockerComposeBytes) + } + catch { + throw [System.InvalidOperationException]::new("Failed to decode Docker Compose Base64 content from LinuxFxVersion: $($_.Exception.Message). This WebApp does not appear to be properly configured for Docker Compose migration.", $_.Exception) + } + } + elseif ($webApp.SiteConfig.LinuxFxVersion -and $webApp.SiteConfig.LinuxFxVersion.Contains("DOCKER|")) { + # If only DOCKER| is present, extract the Docker image reference + $dockerImageRef = $webApp.SiteConfig.LinuxFxVersion.Substring($webApp.SiteConfig.LinuxFxVersion.IndexOf("DOCKER|") + 7) + + if ($dockerImageRef -match "azureipam\.azurecr\.io") { + # Using public Microsoft-provided Azure IPAM registry + Write-Host " ℹ️ Skipped (using public registry)" -ForegroundColor Cyan + $containerRegistryResourceId = $null + $discoveredCount++ # Count as discovered even when using public registry + } + elseif ($dockerImageRef -match "([a-zA-Z0-9\-]+)\.azurecr\.io") { + # Using private ACR - need to locate the registry + $acrName = $matches[1] + + # Search for the ACR in the subscription + $acrResources = Get-AzContainerRegistry -ErrorAction SilentlyContinue + $matchedAcr = $acrResources | Where-Object { $_.Name -eq $acrName } + + if ($matchedAcr) { + $containerRegistryResourceId = $matchedAcr.Id + Write-Host " ✅ Found '$($matchedAcr.Name)'" -ForegroundColor Green + $discoveredCount++ + } + else { + Write-Host " ⚠️ ACR '$acrName' referenced in Docker Compose but not found in subscription" -ForegroundColor Yellow + $containerRegistryResourceId = $null + } + } + } + else { + throw [System.InvalidOperationException]::new("No Docker Compose configuration or Docker Image references found in LinuxFxVersion for WebApp '$AppName'. This script is designed to migrate Docker Compose-based Azure IPAM deployments. Please verify this is the correct WebApp or check the LinuxFxVersion property.") + } + + # Parse Docker Compose content to determine registry type + if ($dockerComposeContent) { + if ($dockerComposeContent -match "azureipam\.azurecr\.io") { + # Using public Microsoft-provided Azure IPAM registry + Write-Host " ℹ️ Skipped (using public registry)" -ForegroundColor Cyan + $containerRegistryResourceId = $null + $discoveredCount++ # Count as discovered even when using public registry + } + elseif ($dockerComposeContent -match "([a-zA-Z0-9\-]+)\.azurecr\.io") { + # Using private ACR - need to locate the registry + $acrName = $matches[1] + + # Search for the ACR in the subscription + $acrResources = Get-AzContainerRegistry -ErrorAction SilentlyContinue + $matchedAcr = $acrResources | Where-Object { $_.Name -eq $acrName } + + if ($matchedAcr) { + $containerRegistryResourceId = $matchedAcr.Id + Write-Host " ✅ Found '$($matchedAcr.Name)'" -ForegroundColor Green + $discoveredCount++ + } + else { + Write-Host " ⚠️ ACR '$acrName' referenced in Docker Compose but not found in subscription" -ForegroundColor Yellow + $containerRegistryResourceId = $null + } + } + else { + throw [System.InvalidOperationException]::new("No Azure Container Registry references found in Docker Compose configuration for WebApp '$AppName'. Azure IPAM requires ACR references (either azureipam.azurecr.io or a private registry). Please verify the Docker Compose configuration is valid for Azure IPAM migration.") + } + } + } + catch { + Write-Host " ❌ Error" -ForegroundColor Red + $missingResources += [PSCustomObject]@{ + ResourceType = "Docker Compose Configuration" + ResourceName = "LinuxFxVersion" + Error = $_.Exception.Message + } + throw $_.Exception + } + + # Fail if no Docker Compose content was found + if(-not $dockerComposeContent) { + Write-Host + + throw [System.InvalidOperationException]::new("No Docker Compose configuration found in LinuxFxVersion for WebApp '$AppName'. This script is designed to migrate Docker Compose-based Azure IPAM deployments. Please verify this is the correct WebApp or check the LinuxFxVersion property.") + } + + # Set global variables for Location, Azure Cloud and Private ACR for use in later functions + $script:location = $webApp.Location + $script:azureCloud = $($appSettings | Where-Object { $_.Name -eq "AZURE_ENV" }).Value + $script:privateAcr = -not [string]::IsNullOrWhiteSpace($containerRegistryResourceId) + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "Discovery Summary: $discoveredCount/$totalExpectedResources resources found" -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Blue + + # If any resources are missing, display details and throw error + if ($missingResources.Count -gt 0) { + Write-Host + Write-Host "❌ Missing Resources Details:" -ForegroundColor Red + Write-Host "=====================================================" -ForegroundColor Red + + foreach ($missing in $missingResources) { + Write-Host "Resource Type: $($missing.ResourceType)" -ForegroundColor Yellow + Write-Host "Resource Name: $($missing.ResourceName)" -ForegroundColor White + Write-Host "Error: $($missing.Error)" -ForegroundColor Red + Write-Host "----------------------------------------------------" -ForegroundColor Red + } + + $missingCount = $missingResources.Count + $missingNames = $missingResources | ForEach-Object { "$($_.ResourceType) '$($_.ResourceName)'" } + + throw [System.InvalidOperationException]::new("Resource discovery failed. $missingCount out of $totalExpectedResources resources could not be found: $($missingNames -join ', '). Please ensure all resources exist and are properly configured.") + } + else { + Write-Host "✅ All required resources discovered successfully!" -ForegroundColor Green + } + + Write-Host + + return [PSCustomObject]@{ + WebAppResourceId = $webApp.Id + ManagedIdentityResourceId = $managedIdentityResourceId + LogAnalyticsWorkspaceId = $logAnalyticsWorkspaceId + CosmosDbResourceId = $cosmosDbResourceId + KeyVaultResourceId = $keyVaultResourceId + AppServicePlanResourceId = $webApp.ServerFarmId + ContainerRegistryResourceId = $containerRegistryResourceId + } +} + +function Format-WebAppDetailsTable { + param( + [Parameter(Mandatory = $true)] + [PSCustomObject]$Details + ) + + # Define resource types and their property names + $resourceMap = @{ + "Web App" = "WebAppResourceId" + "Managed Identity" = "ManagedIdentityResourceId" + "Log Analytics Workspace" = "LogAnalyticsWorkspaceId" + "Cosmos DB Account" = "CosmosDbResourceId" + "Key Vault" = "KeyVaultResourceId" + "App Service Plan" = "AppServicePlanResourceId" + } + + $tableData = foreach ($resourceType in $resourceMap.Keys) { + $resourceId = $Details.($resourceMap[$resourceType]) + if (-not [string]::IsNullOrWhiteSpace($resourceId)) { + $resourceDetails = Get-ResourceDetailsFromId -ResourceId $resourceId + [PSCustomObject]@{ + ResourceType = $resourceType + ResourceName = $resourceDetails.ResourceName + ResourceGroup = $resourceDetails.ResourceGroupName + Subscription = $resourceDetails.SubscriptionId + } + } + } + + # Handle Container Registry separately + if (-not [string]::IsNullOrWhiteSpace($Details.ContainerRegistryResourceId)) { + $containerRegistryDetails = Get-ResourceDetailsFromId -ResourceId $Details.ContainerRegistryResourceId + $tableData += [PSCustomObject]@{ + ResourceType = "Container Registry" + ResourceName = $containerRegistryDetails.ResourceName + ResourceGroup = $containerRegistryDetails.ResourceGroupName + Subscription = $containerRegistryDetails.SubscriptionId + } + } + elseif ($Details.PSObject.Properties.Name -contains "ContainerRegistryResourceId") { + $tableData += [PSCustomObject]@{ + ResourceType = "Container Registry" + ResourceName = "azureipam (Public)" + ResourceGroup = "N/A" + Subscription = "N/A" + } + } + + Write-Host "=====================================================" -ForegroundColor Red + Write-Host "Resource Summary Table:" -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Red + $tableData | Format-Table -AutoSize + Write-Host "=====================================================" -ForegroundColor Red + Write-Host +} + +function Test-ResourceExistence { + param( + [Parameter(Mandatory = $true)] + [PSCustomObject]$ResourceDetails + ) + + # Define property names and their corresponding display names + $resourcePropertyMap = @{ + "WebAppResourceId" = "Web App" + "ManagedIdentityResourceId" = "Managed Identity" + "LogAnalyticsWorkspaceId" = "Log Analytics Workspace" + "CosmosDbResourceId" = "Cosmos DB Account" + "KeyVaultResourceId" = "Key Vault" + "AppServicePlanResourceId" = "App Service Plan" + "ContainerRegistryResourceId" = "Container Registry" + } + + # Validate input parameter + if ($null -eq $ResourceDetails) { + throw [System.ArgumentException]::new("ResourceDetails parameter cannot be null.", "ResourceDetails") + } + + if ($ResourceDetails -isnot [PSCustomObject]) { + throw [System.ArgumentException]::new("ResourceDetails parameter must be a PSCustomObject. Received type: $($ResourceDetails.GetType().Name)", "ResourceDetails") + } + + # Get all Resource ID properties from the input object (including ContainerRegistryResourceId even if null) + $resourceIdProperties = $ResourceDetails.PSObject.Properties | Where-Object { + $_.Name -in $resourcePropertyMap.Keys -and + ($_.Name -eq "ContainerRegistryResourceId" -or -not [string]::IsNullOrWhiteSpace($_.Value)) + } + + if ($resourceIdProperties.Count -eq 0) { + $availableProperties = $ResourceDetails.PSObject.Properties | ForEach-Object { $_.Name } + $expectedProperties = $resourcePropertyMap.Keys + throw [System.ArgumentException]::new("No valid Resource ID properties found in the provided object. Expected properties: $($expectedProperties -join ', '). Available properties: $($availableProperties -join ', ').", "ResourceDetails") + } + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "Verifying Resource Existence..." -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Blue + + $missingResources = @() + $verifiedCount = 0 + $totalCount = $resourceIdProperties.Count + + foreach ($property in $resourceIdProperties) { + $resourceId = $property.Value + + if ([string]::IsNullOrWhiteSpace($resourceId)) { + # Special handling for ContainerRegistryResourceId - it can be null (indicating public ACR) + if ($property.Name -eq "ContainerRegistryResourceId") { + Write-Host "🔍 Verifying Container Registry..." -ForegroundColor Cyan -NoNewline + Write-Host " ℹ️ Skipped (using public registry)" -ForegroundColor Cyan + $verifiedCount++ + continue + } + else { + Write-Host "⚠️ Skipping empty resource ID for property: $($property.Name)" -ForegroundColor Yellow + continue + } + } + + try { + # Get display name from property mapping + $displayName = $resourcePropertyMap[$property.Name] + + # Extract resource details for additional info + $resourceInfo = Get-ResourceDetailsFromId -ResourceId $resourceId + + Write-Host "🔍 Verifying $displayName '$($resourceInfo.ResourceName)'..." -ForegroundColor Cyan -NoNewline + + # Test resource existence + $resource = Get-AzResource -ResourceId $resourceId -ErrorAction SilentlyContinue + + if ($resource) { + Write-Host " ✅ Verified" -ForegroundColor Green + $verifiedCount++ + } + else { + Write-Host " ❌ Missing" -ForegroundColor Red + $missingResources += [PSCustomObject]@{ + ResourceType = $displayName + ResourceName = $resourceInfo.ResourceName + ResourceGroup = $resourceInfo.ResourceGroupName + Subscription = $resourceInfo.SubscriptionId + ResourceId = $resourceId + } + } + } + catch { + Write-Host " ❌ Error: $($_.Exception.Message)" -ForegroundColor Red + $displayName = $resourcePropertyMap[$property.Name] + $resourceInfo = Get-ResourceDetailsFromId -ResourceId $resourceId + + $missingResources += [PSCustomObject]@{ + ResourceType = $displayName + ResourceName = $resourceInfo.ResourceName + ResourceGroup = $resourceInfo.ResourceGroupName + Subscription = $resourceInfo.SubscriptionId + ResourceId = $resourceId + Error = $_.Exception.Message + } + } + } + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "Verification Summary: $verifiedCount/$totalCount resources found" -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Blue + + # If any resources are missing, display details and throw error + if ($missingResources.Count -gt 0) { + Write-Host + Write-Host "❌ Missing Resources Details:" -ForegroundColor Red + Write-Host "=====================================================" -ForegroundColor Red + + foreach ($missing in $missingResources) { + Write-Host "Resource Type: $($missing.ResourceType)" -ForegroundColor Yellow + Write-Host "Resource Name: $($missing.ResourceName)" -ForegroundColor White + Write-Host "Resource Group: $($missing.ResourceGroup)" -ForegroundColor White + Write-Host "Subscription: $($missing.Subscription)" -ForegroundColor White + Write-Host "Resource ID: $($missing.ResourceId)" -ForegroundColor Gray + if ($missing.Error) { + Write-Host "Error: $($missing.Error)" -ForegroundColor Red + } + Write-Host "----------------------------------------------------" -ForegroundColor Red + } + + Write-Host + + $missingCount = $missingResources.Count + $missingNames = $missingResources | ForEach-Object { "$($_.ResourceType) '$($_.ResourceName)'" } + + throw [System.InvalidOperationException]::new("Resource verification failed. $missingCount out of $totalCount resources could not be found or accessed: $($missingNames -join ', '). Please ensure all resources exist and you have appropriate permissions.") + } + else { + Write-Host "✅ All resources verified successfully!" -ForegroundColor Green + } + + Write-Host +} + +function Read-JsonData { + param( + [Parameter(Mandatory = $true)] + [System.IO.FileInfo] + $JsonFilePath + ) + + $DATA_MAP = @{ + "Web App" = @{ + ResourceType = "Microsoft.Web/sites" + Required = $true + } + "App Service Plan" = @{ + ResourceType = "Microsoft.Web/serverfarms" + Required = $true + } + "Managed Identity" = @{ + ResourceType = "Microsoft.ManagedIdentity/userAssignedIdentities" + Required = $true + } + "Log Analytics Workspace" = @{ + ResourceType = "Microsoft.OperationalInsights/workspaces" + Required = $true + } + "Cosmos DB Account" = @{ + ResourceType = "Microsoft.DocumentDB/databaseAccounts" + Required = $true + } + "Key Vault" = @{ + ResourceType = "Microsoft.KeyVault/vaults" + Required = $true + } + "Container Registry" = @{ + ResourceType = "Microsoft.ContainerRegistry/registries" + Required = $false + } + } + + $jsonData = Get-Content -Path $JsonFilePath.FullName -Raw | ConvertFrom-Json + + $resourceTypes = $jsonData | ForEach-Object { $_.ResourceType } + + # Extract required and optional resource types from DATA_MAP + $requiredResourceTypes = $DATA_MAP.Values | Where-Object { $_.Required -eq $true } | ForEach-Object { $_.ResourceType } + $optionalResourceTypes = $DATA_MAP.Values | Where-Object { $_.Required -eq $false } | ForEach-Object { $_.ResourceType } + $allValidResourceTypes = $requiredResourceTypes + $optionalResourceTypes + + # Check for missing required resource types + $missingRequired = $requiredResourceTypes | Where-Object { $_ -notin $resourceTypes } + $invalidTypes = $resourceTypes | Where-Object { $_ -notin $allValidResourceTypes } + + if ($missingRequired.Count -gt 0 -or $invalidTypes.Count -gt 0) { + $errorMessages = @() + if ($missingRequired.Count -gt 0) { + $errorMessages += "Missing required resource types: $($missingRequired -join ', ')" + } + if ($invalidTypes.Count -gt 0) { + $errorMessages += "Invalid resource types found: $($invalidTypes -join ', ')" + } + + $errorMessage = "The JSON file '$($JsonFilePath.Name)' has resource type issues. $($errorMessages -join '. ')" + throw [System.ArgumentException]::New($errorMessage, "JsonFilePath") + } + + # Create a hashtable to store resources by type for easy lookup + $resourceLookup = @{} + foreach ($resource in $jsonData) { + $resourceLookup[$resource.ResourceType] = $resource + } + + # Build Resource IDs using the helper function + $resourceIds = @{} + $requiredResourceTypes + $optionalResourceTypes | ForEach-Object { + $resource = $resourceLookup[$_] + if ($resource) { + $resourceIds[$_] = New-ResourceId -Resource $resource + } + } + + # Return PSCustomObject matching the structure from Get-WebAppDetails + return [PSCustomObject]@{ + WebAppResourceId = $resourceIds["Microsoft.Web/sites"] + ManagedIdentityResourceId = $resourceIds["Microsoft.ManagedIdentity/userAssignedIdentities"] + LogAnalyticsWorkspaceId = $resourceIds["Microsoft.OperationalInsights/workspaces"] + CosmosDbResourceId = $resourceIds["Microsoft.DocumentDB/databaseAccounts"] + KeyVaultResourceId = $resourceIds["Microsoft.KeyVault/vaults"] + AppServicePlanResourceId = $resourceIds["Microsoft.Web/serverfarms"] + ContainerRegistryResourceId = $resourceIds["Microsoft.ContainerRegistry/registries"] + } +} + +function Get-WebAppStatusDetails { + param( + [Parameter(Mandatory = $true)] + [PSCustomObject]$Details + ) + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "Probing WebApp Status API..." -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Blue + + try { + $webAppDetails = Get-ResourceDetailsFromId -ResourceId $Details.WebAppResourceId + + Write-Host "🔍 Getting WebApp hostname..." -ForegroundColor Cyan -NoNewline + $webApp = Get-AzWebApp -ResourceGroupName $webAppDetails.ResourceGroupName -Name $webAppDetails.ResourceName -ErrorAction Stop + + if (-not $webApp.HostNames -or $webApp.HostNames.Count -eq 0) { + throw [System.InvalidOperationException]::new("No hostnames found for WebApp '$($webAppDetails.ResourceName)'.") + } + + $appUri = $webApp.HostNames[0] + Write-Host " ✅ Found '$appUri'" -ForegroundColor Green + + $statusApiUrl = "https://$appUri/api/status" + Write-Host "🔍 Querying status API..." -ForegroundColor Cyan -NoNewline + + $statusResponse = Invoke-RestMethod -Uri $statusApiUrl -Method GET -TimeoutSec 30 -ErrorAction Stop + Write-Host " ✅ Success" -ForegroundColor Green + + # Simplified property extraction - PowerShell 7.2+ supports null-conditional operators + $containerStack = $statusResponse.stack + $containerImage = $statusResponse.container?.image_id + + if (-not $containerStack) { + Write-Warning "No 'stack' property found in status API response." + } + + if (-not $containerImage) { + Write-Warning "No 'image_id' property found in container object." + } + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "✅ Status API probing completed successfully!" -ForegroundColor Green + Write-Host + + return [PSCustomObject]@{ + ContainerStack = $containerStack + ContainerImage = $containerImage + StatusApiUrl = $statusApiUrl + } + } + catch [System.Net.WebException] { + Write-Host " ❌ Network Error" -ForegroundColor Red + throw [System.InvalidOperationException]::new("Failed to connect to status API at '$statusApiUrl'. Error: $($_.Exception.Message). This may indicate the WebApp is not running or the status API endpoint is not available.", $_.Exception) + } + catch { + Write-Host " ❌ Error" -ForegroundColor Red + throw [System.InvalidOperationException]::new("Failed to probe status API at '$statusApiUrl': $($_.Exception.Message).", $_.Exception) + } +} + +function ConvertTo-MigrationObject { + param( + [Parameter(Mandatory = $true)] + [PSCustomObject]$Details + ) + + # Create a mapping of resource types to their property names + $resourceMapping = @{ + webApp = "WebAppResourceId" + managedIdentity = "ManagedIdentityResourceId" + logAnalytics = "LogAnalyticsWorkspaceId" + appServicePlan = "AppServicePlanResourceId" + cosmosDb = "CosmosDbResourceId" + keyVault = "KeyVaultResourceId" + containerRegistry = "ContainerRegistryResourceId" + } + + # Extract all resource details at once for efficiency + $resourceDetails = @{} + foreach ($key in $resourceMapping.Keys) { + $resourceId = $Details.($resourceMapping[$key]) + if (-not [string]::IsNullOrWhiteSpace($resourceId)) { + $resourceDetails[$key] = Get-ResourceDetailsFromId -ResourceId $resourceId + } + } + + # Get WebApp settings for Cosmos DB configuration + $webApp = Get-AzWebApp -ResourceGroupName $resourceDetails.webApp.ResourceGroupName -Name $resourceDetails.webApp.ResourceName -ErrorAction Stop + $appSettings = $webApp.SiteConfig.AppSettings + + # Extract Cosmos DB names with Azure IPAM defaults if environment variables missing + $databaseNameSetting = $appSettings | Where-Object { $_.Name -eq "DATABASE_NAME" } + $containerNameSetting = $appSettings | Where-Object { $_.Name -eq "CONTAINER_NAME" } + + # Use Azure IPAM default values if environment variables are not found + if (-not $databaseNameSetting) { + Write-Warning "No DATABASE_NAME environment variable found for WebApp '$($resourceDetails.webApp.ResourceName)'. Using default value 'ipam-db'." + $cosmosDatabaseName = "ipam-db" + } + else { + $cosmosDatabaseName = $databaseNameSetting.Value + } + + if (-not $containerNameSetting) { + Write-Warning "No CONTAINER_NAME environment variable found for WebApp '$($resourceDetails.webApp.ResourceName)'. Using default value 'ipam-ctr'." + $cosmosContainerName = "ipam-ctr" + } + else { + $cosmosContainerName = $containerNameSetting.Value + } + + return @{ + appService = @{ + appServiceName = $resourceDetails.webApp.ResourceName + appServiceRG = $resourceDetails.webApp.ResourceGroupName + } + appServicePlan = @{ + appServicePlanName = $resourceDetails.appServicePlan.ResourceName + appServicePlanRG = $resourceDetails.appServicePlan.ResourceGroupName + } + cosmosAccount = @{ + cosmosAccountName = $resourceDetails.cosmosDb.ResourceName + cosmosAccountRG = $resourceDetails.cosmosDb.ResourceGroupName + cosmosContainerName = $cosmosContainerName + cosmosDatabaseName = $cosmosDatabaseName + } + keyVault = @{ + keyVaultName = $resourceDetails.keyVault.ResourceName + keyVaultRG = $resourceDetails.keyVault.ResourceGroupName + } + logAnalytics = @{ + workspaceName = $resourceDetails.logAnalytics.ResourceName + workspaceRG = $resourceDetails.logAnalytics.ResourceGroupName + } + managedIdentity = @{ + managedIdentityName = $resourceDetails.managedIdentity.ResourceName + managedIdentityRG = $resourceDetails.managedIdentity.ResourceGroupName + } + # Handle optional container registry details (PowerShell 7.2+ null-conditional operator) + containerRegistry = @{ + containerRegistryName = $resourceDetails.containerRegistry?.ResourceName + containerRegistryRG = $resourceDetails.containerRegistry?.ResourceGroupName + } + } +} + +Function Deploy-Bicep { + Param( + [Parameter(Mandatory = $false)] + [PSCustomObject]$Details + ) + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "Deploying IPAM Migration Templates..." -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Blue + + Write-LogFile -Message "=== Starting Bicep Deployment ===" -Level "INFO" + Write-LogFile -Message "Location: $script:location" -Level "INFO" + Write-LogFile -Message "Azure Cloud: $script:azureCloud" -Level "INFO" + Write-LogFile -Message "Private ACR: $script:privateAcr" -Level "INFO" + Write-LogFile -Message "Resource Details: $(ConvertTo-Json $Details -Depth 5 -Compress)" -Level "INFO" + + try { + # Instantiate deployment parameter object + Write-Host "📄 Preparing deployment parameters..." -ForegroundColor Cyan -NoNewline + $deploymentParameters = @{ + location = $script:location + azureCloud = $script:azureCloud + privateAcr = $script:privateAcr + resourceDetails = $Details + } + Write-LogFile -Message "Deployment parameters prepared: $(ConvertTo-Json $deploymentParameters -Depth 5 -Compress)" -Level "INFO" + Write-Host " ✅ Success" -ForegroundColor Green + + # Generate unique deployment name + $deploymentName = "ipamInfraMigrate-$(Get-Date -Format `"yyyyMMddhhmmsstt`")" + Write-LogFile -Message "Generated deployment name: $deploymentName" -Level "INFO" + Write-Host "🚀 Starting Bicep deployment..." -ForegroundColor Cyan -NoNewline + + # Verify main.bicep file exists + $bicepPath = Join-Path -Path $ROOT_DIR -ChildPath "migrate\main.bicep" + if (-not (Test-Path -Path $bicepPath)) { + Write-LogFile -Message "Bicep template not found at: $bicepPath" -Level "ERROR" + throw [System.InvalidOperationException]::new("Bicep template file not found at path: $bicepPath") + } + Write-LogFile -Message "Bicep template verified at: $bicepPath" -Level "INFO" + + # Deploy IPAM bicep template + Write-LogFile -Message "Executing New-AzDeployment with template: main.bicep" -Level "INFO" + $deployment = New-AzDeployment ` + -Name $deploymentName ` + -Location $script:location ` + -TemplateFile main.bicep ` + -TemplateParameterObject $deploymentParameters ` + -ErrorAction Stop + + Write-Host " ✅ Success" -ForegroundColor Green + Write-LogFile -Message "Bicep deployment completed successfully" -Level "SUCCESS" + Write-LogFile -Message "Deployment result: $(ConvertTo-Json $deployment -Depth 3 -Compress)" -Level "INFO" + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "✅ IPAM migration templates deployed successfully!" -ForegroundColor Green + Write-Host + + return $deployment + } + catch { + Write-Host " ❌ Failed" -ForegroundColor Red + Write-Host + Write-Host "❌ IPAM Bicep template deployment failed!" -ForegroundColor Red + Write-LogFile -Message "Bicep deployment failed: $($_.Exception.Message)" -Level "ERROR" -ErrorRecord $_ + throw [System.InvalidOperationException]::new("Failed to deploy IPAM Bicep templates: $($_.Exception.Message)", $_.Exception) + } +} + +Function Build-ContainerImage { + Param( + [Parameter(Mandatory = $true)] + [PSCustomObject]$ContainerDetails, + [Parameter(Mandatory = $true)] + [string]$TargetAcrResourceId + ) + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "Building and Pushing Container Image..." -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Blue + + Write-LogFile -Message "=== Starting Container Image Build ===" -Level "INFO" + Write-LogFile -Message "Target ACR Resource ID: $TargetAcrResourceId" -Level "INFO" + Write-LogFile -Message "Container Details: $(ConvertTo-Json $ContainerDetails -Depth 3 -Compress)" -Level "INFO" + + try { + # Extract ACR details from Resource ID + Write-LogFile -Message "Extracting ACR details from Resource ID" -Level "INFO" + $acrDetails = Get-ResourceDetailsFromId -ResourceId $TargetAcrResourceId + $targetAcrName = $acrDetails.ResourceName + $acrResourceGroup = $acrDetails.ResourceGroupName + $subscriptionId = $acrDetails.SubscriptionId + Write-LogFile -Message "ACR Details - Name: $targetAcrName, ResourceGroup: $acrResourceGroup, Subscription: $subscriptionId" -Level "INFO" + + # Determine container type from the container details + $containerType = $ContainerDetails.ContainerImage + Write-LogFile -Message "Container type determined: $containerType" -Level "INFO" + if ([string]::IsNullOrWhiteSpace($containerType)) { + throw [System.InvalidOperationException]::new("Container image information is not available from the status API. Cannot determine the appropriate Dockerfile to use for building the image.") + } + + # Define container configuration mapping + $containerMap = @{ + debian = @{ + Extension = 'deb' + Port = 80 + Images = @{ + Build = 'node:22-slim' + Serve = 'python:3.11-slim' + } + } + rhel = @{ + Extension = 'rhel' + Port = 8080 + Images = @{ + Build = 'registry.access.redhat.com/ubi8/nodejs-22' + Serve = 'registry.access.redhat.com/ubi8/python-311' + } + } + } + Write-LogFile -Message "Container mapping configuration loaded for types: $(($containerMap.Keys -join ', '))" -Level "INFO" + + # Validate container type + if (-not $containerMap.ContainsKey($containerType)) { + $availableTypes = $containerMap.Keys -join ', ' + Write-LogFile -Message "Invalid container type '$containerType'. Available types: $availableTypes" -Level "ERROR" + throw [System.InvalidOperationException]::new("Unsupported container type '$containerType'. Supported types are: $availableTypes") + } + + # Determine Dockerfile path + $dockerFile = 'Dockerfile.' + $containerMap[$containerType].Extension + $dockerFilePath = Join-Path -Path $ROOT_DIR -ChildPath $dockerFile + Write-LogFile -Message "Dockerfile path determined: $dockerFilePath" -Level "INFO" + + # Verify Dockerfile exists + if (-not (Test-Path -Path $dockerFilePath)) { + Write-LogFile -Message "Dockerfile not found at path: $dockerFilePath" -Level "ERROR" + throw [System.InvalidOperationException]::new("Dockerfile not found at path '$dockerFilePath'. Please ensure the required Dockerfile exists for container type '$containerType'.") + } + Write-LogFile -Message "Dockerfile verified at path: $dockerFilePath" -Level "SUCCESS" + + Write-Host "🔨 Building IPAM container image (" -ForegroundColor Cyan -NoNewline + Write-Host "$containerType" -ForegroundColor Yellow -NoNewline + Write-Host ")..." -ForegroundColor Cyan -NoNewline + + # Log build parameters for troubleshooting + $buildArgs = @{ + Port = $containerMap[$containerType].Port + BuildImage = $containerMap[$containerType].Images.Build + ServeImage = $containerMap[$containerType].Images.Serve + } + Write-LogFile -Message "ACR build parameters: $(ConvertTo-Json $buildArgs -Compress)" -Level "INFO" + Write-LogFile -Message "Build command: az acr build -r $targetAcrName -t ipam:latest -f $dockerFilePath $ROOT_DIR --build-arg PORT=$($buildArgs.Port) --build-arg BUILD_IMAGE=$($buildArgs.BuildImage) --build-arg SERVE_IMAGE=$($buildArgs.ServeImage) --no-logs" -Level "INFO" + + # Execute ACR build command + $appBuildOutput = $( + az acr build -r $targetAcrName ` + -t ipam:latest ` + -f $dockerFilePath $ROOT_DIR ` + --build-arg PORT=$($containerMap[$containerType].Port) ` + --build-arg BUILD_IMAGE=$($containerMap[$containerType].Images.Build) ` + --build-arg SERVE_IMAGE=$($containerMap[$containerType].Images.Serve) ` + --no-logs + ) *>&1 + + Write-LogFile -Message "ACR build command exit code: $LASTEXITCODE" -Level "INFO" + Write-LogFile -Message "ACR build output: $($appBuildOutput -join ' ')" -Level "INFO" + + # Check build result + if ($LASTEXITCODE -ne 0) { + Write-Host " ❌ Failed" -ForegroundColor Red + Write-LogFile -Message "ACR build failed with exit code: $LASTEXITCODE" -Level "ERROR" + + # Extract build ID for error logging + $buildId = [regex]::Matches($appBuildOutput, "(?<=Queued a build with ID: )[\w]*").Value.Trim() + Write-LogFile -Message "Extracted build ID: $buildId" -Level "INFO" + + if ($buildId) { + Write-Host "📋 Fetching build logs for ID: $buildId..." -ForegroundColor Cyan -NoNewline + Write-LogFile -Message "Attempting to fetch detailed build logs for build ID: $buildId" -Level "INFO" + + try { + # Get current subscription and resource group for the ACR + $azContext = Get-AzContext + $currentSubscriptionId = $azContext.Subscription.Id + Write-LogFile -Message "Current PowerShell context subscription: $currentSubscriptionId" -Level "INFO" + + # Validate we're working with the same subscription + if ($subscriptionId -ne $currentSubscriptionId) { + Write-Warning "ACR is in subscription '$subscriptionId' but current context is '$currentSubscriptionId'. This may affect log retrieval." + Write-LogFile -Message "Subscription mismatch detected - ACR: $subscriptionId, Current: $currentSubscriptionId" -Level "WARNING" + } + + # Fetch detailed build logs + Write-LogFile -Message "Calling Get-BuildLogs function for detailed error analysis" -Level "INFO" + $buildLogs = Get-BuildLogs -SubscriptionId $subscriptionId -ResourceGroupName $acrResourceGroup -RegistryName $targetAcrName -BuildId $buildId + + Write-Host " ✅ Success" -ForegroundColor Green + # Write-Host "📋 Build logs retrieved successfully:" -ForegroundColor Yellow + # Write-Host $buildLogs -ForegroundColor Gray + Write-LogFile -Message "=== DETAILED BUILD LOGS ===" -Level "ERROR" + Write-LogFile -Message $buildLogs -Level "ERROR" + Write-LogFile -Message "=== END BUILD LOGS ===" -Level "ERROR" + + $errorMessage = "Container build failed with exit code $LASTEXITCODE. Build ID: $buildId. Detailed logs retrieved above." + } + catch { + Write-Host " ❌ Failed" -ForegroundColor Red + Write-Host "⚠️ Failed to retrieve build logs: $($_.Exception.Message)" -ForegroundColor Yellow + Write-LogFile -Message "Failed to retrieve build logs: $($_.Exception.Message)" -Level "ERROR" -ErrorRecord $_ + $errorMessage = "Container build failed with exit code $LASTEXITCODE. Build ID: $buildId. Output: $($appBuildOutput -join ' '). Failed to retrieve detailed logs: $($_.Exception.Message)" + } + } + else { + Write-LogFile -Message "No build ID found in ACR build output" -Level "ERROR" + $errorMessage = "Container build failed with exit code $LASTEXITCODE. Output: $($appBuildOutput -join ' '). No build ID found in output." + } + + Write-LogFile -Message "Final error message: $errorMessage" -Level "ERROR" + throw [System.InvalidOperationException]::new($errorMessage) + } + else { + Write-Host " ✅ Success" -ForegroundColor Green + Write-LogFile -Message "ACR build completed successfully" -Level "SUCCESS" + } + + # Generate new image reference + $newImageReference = "$targetAcrName.azurecr.io/ipam:latest" + + # Restart the WebApp to pick up the new image + Write-Host "🔄 Restarting WebApp to apply new container image..." -ForegroundColor Cyan -NoNewline + try { + # Extract WebApp details for restart + $webAppDetails = Get-ResourceDetailsFromId -ResourceId $details.WebAppResourceId + Restart-AzWebApp -ResourceGroupName $webAppDetails.ResourceGroupName -Name $webAppDetails.ResourceName -ErrorAction Stop | Out-Null + Write-Host " ✅ Success" -ForegroundColor Green + } + catch { + Write-Host " ❌ Failed" -ForegroundColor Red + Write-Warning "WebApp restart failed: $($_.Exception.Message). You may need to manually restart the WebApp to apply the new container image." + } + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "✅ Container image build and push completed successfully!" -ForegroundColor Green + Write-Host + + # Return only the image reference string + return $newImageReference + } + catch { + # Write-Host + Write-Host "❌ Container image build and push failed!" -ForegroundColor Red + throw [System.InvalidOperationException]::new("Failed to build and push container image: $($_.Exception.Message)", $_.Exception) + } +} + +# Log script start +Write-LogFile -Message "=== Azure IPAM Migration Script Started ===" -Level "INFO" +Write-LogFile -Message "Parameters: AppName=$AppName, ResourceGroupName=$ResourceGroupName, JsonFile=$JsonFile, NoVerify=$NoVerify, Force=$Force" -Level "INFO" +Write-LogFile -Message "Root Directory: $ROOT_DIR" -Level "INFO" +Write-LogFile -Message "Log File: $logFile" -Level "INFO" + +# Console output start +Write-Host + +# Determine resource discovery method based on whether JSON override file is provided +if ([string]::IsNullorEmpty($JsonFile)) { + # Auto-discovery mode: Extract configuration from existing WebApp + Write-Host "⚙️ Auto-Discovering Resource Details from WebApp Config..." -ForegroundColor Magenta + Write-Host + Write-LogFile -Message "Starting auto-discovery mode for ResourceGroup: $ResourceGroupName, AppName: $AppName" -Level "INFO" + + $details = Get-WebAppDetails -ResourceGroupName $ResourceGroupName -AppName $AppName + Write-LogFile -Message "Auto-discovery completed successfully" -Level "SUCCESS" + + # Display the table summary + Format-WebAppDetailsTable -Details $details +} +else { + # Override mode: Use administrator-provided JSON configuration + Write-Host "📄 Reading Resource Overrides from specified JSON File: " -ForegroundColor Magenta -NoNewline + Write-Host "$($JsonFile.FullName)" -ForegroundColor Yellow + Write-Host + Write-LogFile -Message "Using JSON override mode with file: $($JsonFile.FullName)" -Level "INFO" + + $jsonFilePath = Get-Item -Path $script:JsonFile + $details = Read-JsonData -JsonFilePath $jsonFilePath + Write-LogFile -Message "JSON override data loaded successfully" -Level "SUCCESS" + + # Display the table summary + Format-WebAppDetailsTable -Details $details +} + +# User confirmation before proceeding with migration +if (-not $Force -and -not (Get-UserConfirmation -Message "Please confirm the above resources should be used for the conversion process." -PromptText "Is this information accurate? Enter Y to continue or N to exit (Y/N)")) { + exit 1 +} + +# Verify resource existence before proceeding (unless NoVerify switch is set) +if ($NoVerify -eq $false) { + # Verify that all resources exist before proceeding + Test-ResourceExistence -ResourceDetails $details +} + +# Probe the WebApp Status API to get container details for image building +$containerDetails = Get-WebAppStatusDetails -Details $details + +# Azure CLI validation only required for private ACR scenarios (for ACR build command) +if($script:privateAcr) { + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "Verifying Azure CLI Configuration..." -ForegroundColor Yellow + Write-Host "=====================================================" -ForegroundColor Blue + + # Verify Minimum Azure CLI Version + Write-Host "🔍 Checking Azure CLI version..." -ForegroundColor Cyan -NoNewline + try { + $azureCliVer = [System.Version](az version | ConvertFrom-Json).'azure-cli' + + if($azureCliVer -lt $MIN_AZ_CLI_VER) { + Write-Host " ❌ Version $azureCliVer (Required: $MIN_AZ_CLI_VER or greater)" -ForegroundColor Red + throw [System.InvalidOperationException]::new("Azure CLI must be version $MIN_AZ_CLI_VER or greater! Current version: $azureCliVer") + } + else { + Write-Host " ✅ v$azureCliVer" -ForegroundColor Green + } + } + catch { + Write-Host " ❌ Error checking version" -ForegroundColor Red + throw [System.InvalidOperationException]::new("Failed to verify Azure CLI version: $($_.Exception.Message)", $_.Exception) + } + + # Verify Azure PowerShell and Azure CLI Contexts Match + # This ensures ACR build operations target the correct subscription + Write-Host "🔍 Verifying Azure CLI authentication..." -ForegroundColor Cyan -NoNewline + try { + $azureCliContext = $(az account show | ConvertFrom-Json) 2>$null + + if(-not $azureCliContext) { + Write-Host " ❌ Not authenticated" -ForegroundColor Red + throw [System.InvalidOperationException]::new("Azure CLI not logged in or no subscription has been selected!") + } + else { + Write-Host " ✅ Authenticated" -ForegroundColor Green + } + } + catch { + Write-Host " ❌ Authentication failed" -ForegroundColor Red + throw [System.InvalidOperationException]::new("Azure CLI authentication verification failed: $($_.Exception.Message)", $_.Exception) + } + + # Synchronize Azure PowerShell and CLI contexts to prevent deployment/build mismatches + Write-Host "🔍 Verifying Azure CLI context..." -ForegroundColor Cyan -NoNewline + try { + $azureCliSub = $azureCliContext.id + $azurePowerShellSub = (Get-AzContext).Subscription.Id + + if ($azurePowerShellSub -ne $azureCliSub) { + Write-Host " ❌ Context Mismatch" -ForegroundColor Red + Write-Host " ↳Azure PowerShell: $azurePowerShellSub" -ForegroundColor Gray + Write-Host " ↳Azure CLI: $azureCliSub" -ForegroundColor Gray + + Write-Host "🔧 Switching Azure CLI context..." -ForegroundColor Cyan -NoNewline + + $null = az account set --subscription $azurePowerShellSub 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host " ❌ Failed" -ForegroundColor Red + throw "Failed to switch Azure CLI context to subscription '$azurePowerShellSub'" + } + + Write-Host " ✅ Success" -ForegroundColor Green + } + else { + Write-Host " ✅ Synchronized" -ForegroundColor Green + } + } + catch { + Write-Host " ❌ Context verification failed" -ForegroundColor Red + throw [System.InvalidOperationException]::new("Failed to verify context synchronization: $($_.Exception.Message)", $_.Exception) + } + + Write-Host "=====================================================" -ForegroundColor Blue + Write-Host "✅ Azure CLI configuration verified successfully!" -ForegroundColor Green + Write-Host +} + +Write-Host "🔄 Converting Resource Details to Migration Object..." -ForegroundColor Cyan +# Transform discovered resource details into format expected by Bicep templates +$migrationObject = ConvertTo-MigrationObject -Details $details +Write-Host + +# Final user confirmation before executing deployment and container operations +if (-not $Force -and -not (Get-UserConfirmation -Message "Please confirm you are ready to proceed with the Azure IPAM migration process." -PromptText "Proceed with migration? Enter Y to continue or N to exit (Y/N)")) { + Write-LogFile -Message "User declined to proceed with migration" -Level "INFO" + exit 1 +} + +Write-LogFile -Message "User confirmed migration process, beginning deployment phase" -Level "INFO" + +try { + # Deploy Bicep templates to update Azure IPAM infrastructure + Write-LogFile -Message "Starting Bicep template deployment" -Level "INFO" + Deploy-Bicep -Details $migrationObject | Out-Null + Write-LogFile -Message "Bicep template deployment completed successfully" -Level "SUCCESS" + + # Build and push container image to target ACR (if using private ACR) + if ($script:privateAcr) { + Write-LogFile -Message "Private ACR enabled, proceeding with container image build" -Level "INFO" + # Get target ACR resource ID from the details object + $targetAcrResourceId = $details.ContainerRegistryResourceId + + if ([string]::IsNullOrWhiteSpace($targetAcrResourceId)) { + Write-Warning "Private ACR is enabled but no target ACR resource ID found in details object. Skipping container image build and push." + Write-LogFile -Message "Private ACR enabled but no target ACR resource ID found, skipping container build" -Level "WARNING" + } + else { + Write-LogFile -Message "Building and pushing container image to ACR: $targetAcrResourceId" -Level "INFO" + # Build custom container image and push to private ACR, then restart WebApp + $newImageReference = Build-ContainerImage -ContainerDetails $containerDetails -TargetAcrResourceId $targetAcrResourceId + Write-Host "🎉 Migration complete!" -ForegroundColor Green + Write-LogFile -Message "Migration completed successfully with private ACR. New image reference: $newImageReference" -Level "SUCCESS" + } + } + else { + # Using public Azure IPAM registry - no custom build required + Write-Host "🎉 Migration completed using public Azure IPAM registry!" -ForegroundColor Green + Write-LogFile -Message "Migration completed successfully using public Azure IPAM registry" -Level "SUCCESS" + } + + Write-LogFile -Message "=== Azure IPAM Migration Script Completed Successfully ===" -Level "SUCCESS" +} +catch { + Write-LogFile -Message "=== CRITICAL ERROR: Migration Failed ===" -Level "ERROR" -ErrorRecord $_ + Write-LogFile -Message "Migration failed at main execution level: $($_.Exception.Message)" -Level "ERROR" -ErrorRecord $_ + Write-Host + Write-Host "💥 Migration failed! Check the log file for details." -ForegroundColor Red + Write-Host "📋 Log file location: $logFile" -ForegroundColor Yellow + throw +} + +Write-Host diff --git a/migrate/modules/appService.bicep b/migrate/modules/appService.bicep new file mode 100644 index 00000000..c4da561a --- /dev/null +++ b/migrate/modules/appService.bicep @@ -0,0 +1,265 @@ +@description('App Service Name') +param appServiceName string + +@description('App Service Plan Name') +param appServicePlanName string + +@description('CosmosDB URI') +param cosmosDbUri string + +@description('CosmosDB Database Name') +param databaseName string + +@description('CosmosDB Container Name') +param containerName string + +@description('KeyVault URI') +param keyVaultUri string + +@description('Deployment Location') +param location string = resourceGroup().location + +@description('Azure Cloud Enviroment') +param azureCloud string = 'AZURE_PUBLIC' + +@description('Managed Identity Id') +param managedIdentityId string + +@description('Managed Identity ClientId') +param managedIdentityClientId string + +@description('Log Analytics Worskpace ID') +param workspaceId string + +@description('Flag to Deploy Private Container Registry') +param privateAcr bool + +@description('Uri for Private Container Registry') +param privateAcrUri string + +// ACR Uri Variable +var acrUri = privateAcr ? privateAcrUri : 'azureipam.azurecr.io' + +resource appServicePlan 'Microsoft.Web/serverfarms@2021-02-01' = { + name: appServicePlanName + location: location + sku: { + name: 'P1v3' + size: 'P1v3' + tier: 'PremiumV3' + capacity: 1 + } + kind: 'linux' + properties: { + reserved: true + } +} + +resource appService 'Microsoft.Web/sites@2021-02-01' = { + name: appServiceName + location: location + kind: 'app,linux,container' + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${managedIdentityId}': {} + } + } + properties: { + httpsOnly: true + serverFarmId: appServicePlan.id + keyVaultReferenceIdentity: managedIdentityId + siteConfig: { + acrUseManagedIdentityCreds: privateAcr ? true : false + acrUserManagedIdentityID: privateAcr ? managedIdentityClientId : null + alwaysOn: true + linuxFxVersion: 'DOCKER|${acrUri}/ipam:latest' + appCommandLine: '' + healthCheckPath: '/api/status' + appSettings: concat( + [ + { + name: 'AZURE_ENV' + value: azureCloud + } + { + name: 'COSMOS_URL' + value: cosmosDbUri + } + // { + // name: 'COSMOS_KEY' + // value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/COSMOS-KEY/)' + // } + { + name: 'DATABASE_NAME' + value: databaseName + } + { + name: 'CONTAINER_NAME' + value: containerName + } + { + name: 'MANAGED_IDENTITY_ID' + value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/IDENTITY-ID/)' + } + { + name: 'UI_APP_ID' + value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/UI-ID/)' + } + { + name: 'ENGINE_APP_ID' + value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/ENGINE-ID/)' + } + { + name: 'ENGINE_APP_SECRET' + value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/ENGINE-SECRET/)' + } + { + name: 'TENANT_ID' + value: '@Microsoft.KeyVault(SecretUri=${keyVaultUri}secrets/TENANT-ID/)' + } + { + name: 'KEYVAULT_URL' + value: keyVaultUri + } + { + name: 'WEBSITE_HEALTHCHECK_MAXPINGFAILURES' + value: '2' + } + { + name: 'WEBSITE_ENABLE_SYNC_UPDATE_SITE' + value: 'true' + } + { + name: 'DOCKER_REGISTRY_SERVER_URL' + value: privateAcr ? 'https://${privateAcrUri}' : 'https://index.docker.io/v1' + } + ] + ) + } + } +} + +resource appConfigLogs 'Microsoft.Web/sites/config@2021-02-01' = { + name: 'logs' + parent: appService + properties: { + detailedErrorMessages: { + enabled: true + } + failedRequestsTracing: { + enabled: true + } + httpLogs: { + fileSystem: { + enabled: true + retentionInDays: 7 + retentionInMb: 50 + } + } + } +} + +resource diagnosticSettingsPlan 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + name: 'diagSettings' + scope: appServicePlan + properties: { + metrics: [ + { + category: 'AllMetrics' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + ] + workspaceId: workspaceId + } +} + +resource diagnosticSettingsApp 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + name: 'diagSettings' + scope: appService + properties: { + logs: [ + { + category: 'AppServiceAntivirusScanAuditLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'AppServiceHTTPLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'AppServiceConsoleLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'AppServiceAppLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'AppServiceFileAuditLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'AppServiceAuditLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'AppServiceIPSecAuditLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'AppServicePlatformLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + ] + workspaceId: workspaceId + } +} + +output appServiceHostName string = appService.properties.defaultHostName diff --git a/migrate/modules/cosmos.bicep b/migrate/modules/cosmos.bicep new file mode 100644 index 00000000..f3279aae --- /dev/null +++ b/migrate/modules/cosmos.bicep @@ -0,0 +1,89 @@ +@description('CosmosDB Account Name') +param cosmosAccountName string + +@description('Log Analytics Workspace ID') +param workspaceId string + +@description('Managed Identity PrincipalId') +param principalId string + +var dbContributor = '00000000-0000-0000-0000-000000000002' +var dbContributorId = '${resourceGroup().id}/providers/Microsoft.DocumentDB/databaseAccounts/${cosmosAccount.name}/sqlRoleDefinitions/${dbContributor}' +var dbContributorRoleAssignmentId = guid(dbContributor, principalId, cosmosAccount.id) + +resource cosmosAccount 'Microsoft.DocumentDB/databaseAccounts@2021-06-15' existing = { + name: cosmosAccountName +} + +resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + name: 'diagSettings' + scope: cosmosAccount + properties: { + logs: [ + { + category: 'DataPlaneRequests' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'QueryRuntimeStatistics' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'PartitionKeyStatistics' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'PartitionKeyRUConsumption' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + { + category: 'ControlPlaneRequests' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + ] + logAnalyticsDestinationType: 'Dedicated' + workspaceId: workspaceId + } +} + +resource sqlRoleAssignment 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2023-04-15' = { + name: dbContributorRoleAssignmentId + parent: cosmosAccount + properties: { + roleDefinitionId: dbContributorId + principalId: principalId + scope: cosmosAccount.id + } +} + +output cosmosDocumentEndpoint string = cosmosAccount.properties.documentEndpoint diff --git a/migrate/modules/keyVault.bicep b/migrate/modules/keyVault.bicep new file mode 100644 index 00000000..ce7c226d --- /dev/null +++ b/migrate/modules/keyVault.bicep @@ -0,0 +1,88 @@ +@description('KeyVault Name') +param keyVaultName string + +@description('Deployment Location') +param location string = resourceGroup().location + +@description('Managed Identity PrincipalId') +param identityPrincipalId string + +@description('Managed Identity ClientId') +param identityClientId string + +@description('AzureAD TenantId') +param tenantId string = subscription().tenantId + +@description('Log Analytics Worskpace ID') +param workspaceId string + +var keyVaultUser = '4633458b-17de-408a-b874-0445c86b69e6' +var keyVaultUserId = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', keyVaultUser) +var keyVaultUserRoleAssignmentId = guid(keyVaultUser, identityPrincipalId, keyVault.id) + +resource keyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' = { + name: keyVaultName + location: location + properties: { + enablePurgeProtection: true + enableRbacAuthorization: true + tenantId: tenantId + sku: { + name: 'standard' + family: 'A' + } + networkAcls: { + defaultAction: 'Allow' + bypass: 'AzureServices' + } + } +} + +resource identityId 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { + parent: keyVault + name: 'IDENTITY-ID' + properties: { + value: identityClientId + } +} + +resource diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + name: 'diagSettings' + scope: keyVault + properties: { + logs: [ + { + categoryGroup: 'allLogs' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + ] + workspaceId: workspaceId + } +} + +resource keyVaultUserAssignment 'Microsoft.Authorization/roleAssignments@2020-04-01-preview' = { + name: keyVaultUserRoleAssignmentId + scope: keyVault + properties: { + principalType: 'ServicePrincipal' + roleDefinitionId: keyVaultUserId + principalId: identityPrincipalId + } +} + +output keyVaultName string = keyVault.name +output keyVaultUri string = keyVault.properties.vaultUri diff --git a/migrate/modules/logAnalyticsWorkspace.bicep b/migrate/modules/logAnalyticsWorkspace.bicep new file mode 100644 index 00000000..d8296ef2 --- /dev/null +++ b/migrate/modules/logAnalyticsWorkspace.bicep @@ -0,0 +1,17 @@ +@description('Log Analytics Workspace Name') +param workspaceName string + +@description('Deployment Location') +param location string = resourceGroup().location + +resource workspace 'Microsoft.OperationalInsights/workspaces@2020-10-01' = { + name: workspaceName + location: location + properties: { + sku: { + name: 'PerGB2018' + } + } +} + +output workspaceId string = workspace.id diff --git a/migrate/overrides.jsonc b/migrate/overrides.jsonc new file mode 100644 index 00000000..ccaa4a35 --- /dev/null +++ b/migrate/overrides.jsonc @@ -0,0 +1,44 @@ +[ + { + // Web App Resource Details + "ResourceType": "Microsoft.Web/sites", + "ResourceName": "ipam-app-name", + "ResourceGroup": "ipam-rg-name", + "Subscription": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + { + // App Service Plan Resource Details + "ResourceType": "Microsoft.Web/serverfarms", + "ResourceName": "ipam-asp-name", + "ResourceGroup": "ipam-rg-name", + "Subscription": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + { + // Managed Identity Resource Details + "ResourceType": "Microsoft.ManagedIdentity/userAssignedIdentities", + "ResourceName": "ipam-identity-name", + "ResourceGroup": "ipam-rg-name", + "Subscription": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + { + // Log Analytics Workspace Resource Details + "ResourceType": "Microsoft.OperationalInsights/workspaces", + "ResourceName": "ipam-workspace-name", + "ResourceGroup": "ipam-rg-name", + "Subscription": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + { + // Cosmos DB Account Resource Details + "ResourceType": "Microsoft.DocumentDB/databaseAccounts", + "ResourceName": "ipam-cosmosdb-name", + "ResourceGroup": "ipam-rg-name", + "Subscription": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + }, + { + // Key Vault Resource Details + "ResourceType": "Microsoft.KeyVault/vaults", + "ResourceName": "ipam-keyvault-name", + "ResourceGroup": "ipam-rg-name", + "Subscription": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" + } +] diff --git a/tests/azureipam.tests.ps1 b/tests/azureipam.tests.ps1 index 5fc6fccd..6809fd5b 100644 --- a/tests/azureipam.tests.ps1 +++ b/tests/azureipam.tests.ps1 @@ -4,7 +4,10 @@ BeforeAll { Set-StrictMode -Version Latest [string]$baseUrl = "$env:IPAM_URL/api" - [System.Security.SecureString]$accessToken = ConvertTo-SecureString (Get-AzAccessToken -ResourceUrl api://$env:IPAM_ENGINE_APP_ID).Token -AsPlainText + + $token = (Get-AzAccessToken -ResourceUrl api://$env:IPAM_ENGINE_APP_ID).Token + [System.Security.SecureString]$accessToken = if ($token -is [System.Security.SecureString]) { $token } else { ConvertTo-SecureString $token -AsPlainText } + [hashtable]$headers = @{ "Content-Type" = "application/json" } @@ -129,7 +132,7 @@ BeforeAll { } # Parse JWT Access Token - Function Parse-JWTtoken { + Function Get-JWTPayload { [CmdletBinding()] Param( [Parameter(Mandatory=$true)] @@ -169,7 +172,7 @@ BeforeAll { # Convert from JSON to PSObject $tokenObj = $tokenJson | ConvertFrom-Json - + Write-Output $headerObj, $tokenObj } } @@ -199,7 +202,7 @@ Context 'Spaces' { New-ApiResource '/spaces' $spaceB $spaces, $spacesStatus = Get-ApiResource '/spaces' - + $spaces.Count | Should -Be 2 $spaces.Name -contains 'TestSpace01' | Should -Be $true $spaces.Name -contains 'TestSpace02' | Should -Be $true @@ -210,7 +213,7 @@ Context 'Spaces' { Remove-ApiResource '/spaces/TestSpace02' $spaces, $spacesStatus = Get-ApiResource '/spaces' - + $spaces.Count | Should -Be 1 $spaces.Name -contains 'TestSpace01' | Should -Be $true $spaces.Name -contains 'TestSpace02' | Should -Be $false @@ -234,7 +237,7 @@ Context 'Spaces' { Update-ApiResource '/spaces/TestSpace01' $update $spaces, $spacesStatus = Get-ApiResource '/spaces' - + $spaces.Count | Should -Be 1 $spaces[0].Name -eq 'TestSpaceA' | Should -Be $true $spaces[0].Desc -eq 'Test Space A' | Should -Be $true @@ -275,7 +278,7 @@ Context 'Blocks' { New-ApiResource '/spaces/TestSpaceA/blocks' $blockB $blocks, $blocksStatus = Get-ApiResource '/spaces/TestSpaceA/blocks' - + $blocks.Count | Should -Be 2 $blocks.Name -contains 'TestBlock01' | Should -Be $true $blocks.Name -contains 'TestBlock02' | Should -Be $true @@ -286,7 +289,7 @@ Context 'Blocks' { Remove-ApiResource '/spaces/TestSpaceA/blocks/TestBlock02' $blocks, $blocksStatus = Get-ApiResource '/spaces/TestSpaceA/blocks' - + $blocks.Count | Should -Be 1 $blocks.Name -contains 'TestBlock01' | Should -Be $true $blocks.Name -contains 'TestBlock02' | Should -Be $false @@ -310,7 +313,7 @@ Context 'Blocks' { Update-ApiResource '/spaces/TestSpaceA/blocks/TestBlock01' $update $blocks, $blocksStatus = Get-ApiResource '/spaces/TestSpaceA/blocks' - + $blocks.Count | Should -Be 1 $blocks[0].Name -eq 'TestBlockA' | Should -Be $true $blocks[0].Cidr -eq '10.1.0.0/16' | Should -Be $true @@ -331,7 +334,7 @@ Context 'Networks' { It 'Verify No Networks Exist in Block' { $networks, $networksStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/networks' - + $networks | Should -Be $null } @@ -399,7 +402,7 @@ Context 'External Networks' { It 'Verify No External Networks Exist in Block' { $externals, $externalsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals' - + $externals.Count | Should -Be 0 } @@ -414,7 +417,7 @@ Context 'External Networks' { New-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals' $script:externalA $externals, $externalsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals' - + $externals.Count | Should -Be 1 $externals[0].Name -eq "ExternalNetA" | Should -Be $true @@ -433,7 +436,7 @@ Context 'External Networks' { New-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals' $script:externalB $externals, $externalsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals' - + $externals.Count | Should -Be 2 $externals[0].Name -eq "ExternalNetA" | Should -Be $true @@ -449,7 +452,7 @@ Context 'External Networks' { It 'Get a Specific External Network' { $external, $externalStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetB' - + $external.Name -eq "ExternalNetB" | Should -Be $true $external.Desc -eq "External Network B" | Should -Be $true $external.Cidr -eq "10.1.2.0/24" | Should -Be $true @@ -478,7 +481,7 @@ Context 'External Networks' { Update-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetB' $update $externals, $externalsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals' - + $externals.Count | Should -Be 2 $externals[0].Name -eq "ExternalNetA" | Should -Be $true @@ -495,7 +498,7 @@ Context 'External Networks' { Remove-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetC' $externals, $externalsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals' - + $externals.Count | Should -Be 1 $externals[0].Name -eq "ExternalNetA" | Should -Be $true @@ -507,7 +510,7 @@ Context 'External Networks' { It 'Verify No External Subnets Exist in External Network' { $subnets, $subnetsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets' - + $subnets.Count | Should -Be 0 } @@ -522,7 +525,7 @@ Context 'External Networks' { New-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets' $script:subnetA $subnets, $subnetsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets' - + $subnets.Count | Should -Be 1 $subnets[0].Name -eq "SubnetA" | Should -Be $true @@ -541,7 +544,7 @@ Context 'External Networks' { New-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets' $script:subnetB $subnets, $subnetsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets' - + $subnets.Count | Should -Be 2 $subnets[0].Name -eq "SubnetA" | Should -Be $true @@ -557,7 +560,7 @@ Context 'External Networks' { It 'Get Specific External Subnet' { $subnet, $subnetStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetB' - + $subnet.Name -eq "SubnetB" | Should -Be $true $subnet.Desc -eq "Subnet B" | Should -Be $true $subnet.Cidr -eq "10.1.1.64/26" | Should -Be $true @@ -586,7 +589,7 @@ Context 'External Networks' { Update-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetB' $update $subnets, $subnetsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets' - + $subnets.Count | Should -Be 2 $subnets[0].Name -eq "SubnetA" | Should -Be $true @@ -603,7 +606,7 @@ Context 'External Networks' { Remove-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetC' $subnets, $subnetsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets' - + $subnets.Count | Should -Be 1 $subnets[0].Name -eq "SubnetA" | Should -Be $true @@ -615,7 +618,7 @@ Context 'External Networks' { It 'Verify No External Endpoints Exist in External Subnet' { $endpoints, $endpointsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' - + $endpoints.Count | Should -Be 0 } @@ -630,7 +633,7 @@ Context 'External Networks' { New-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' $script:endpointA $endpoints, $endpointsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' - + $endpoints.Count | Should -Be 1 $endpoints[0].Name -eq "EndpointA" | Should -Be $true @@ -649,7 +652,7 @@ Context 'External Networks' { New-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' $script:endpointB $endpoints, $endpointsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' - + $endpoints.Count | Should -Be 2 $endpoints[0].Name -eq "EndpointA" | Should -Be $true @@ -685,7 +688,7 @@ Context 'External Networks' { Set-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' $body $endpoints, $endpointsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' - + $endpoints.Count | Should -Be 4 $endpoints[0].Name -eq "EndpointA" | Should -Be $true @@ -715,7 +718,7 @@ Context 'External Networks' { Remove-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' $body $endpoints, $endpointsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' - + $endpoints.Count | Should -Be 2 $endpoints[0].Name -eq "EndpointA" | Should -Be $true @@ -731,7 +734,7 @@ Context 'External Networks' { It 'Get a Specific External Endpoint' { $endpoint, $endpointStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints/EndpointA' - + $endpoint.Name | Should -Be "EndpointA" $endpoint.Desc | Should -Be "Endpoint A" $endpoint.IP | Should -Be "10.1.1.4" @@ -760,7 +763,7 @@ Context 'External Networks' { Update-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints/EndpointB' $update $endpoints, $endpointsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' - + $endpoints.Count | Should -Be 2 $endpoints[0].Name -eq "EndpointA" | Should -Be $true @@ -777,7 +780,7 @@ Context 'External Networks' { Remove-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints/EndpointC' $endpoints, $endpointsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/externals/ExternalNetA/subnets/SubnetA/endpoints' - + $endpoints.Count | Should -Be 1 $endpoints[0].Name -eq "EndpointA" | Should -Be $true @@ -791,7 +794,7 @@ Context 'Reservations' { It 'Verify No Reservations Exist in Block' { $reservations, $reservationsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/reservations' - + $reservations | Should -Be $null } @@ -817,7 +820,7 @@ Context 'Reservations' { $script:reservationC, $reservationCStatus = New-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/reservations' $bodyC $reservations, $reservationsStatus = Get-ApiResource '/spaces/TestSpaceA/blocks/TestBlockA/reservations' - + $reservations.Count | Should -Be 3 $reservations[0].Space -eq "TestSpaceA" | Should -Be $true @@ -898,7 +901,7 @@ Context 'Reservations' { It 'Get a Specific Reservation' { $reservation, $reservationStatus = Get-ApiResource "/spaces/TestSpaceA/blocks/TestBlockA/reservations/$($script:reservationC.Id)" - + $reservation.Space -eq "TestSpaceA" | Should -Be $true $reservation.Block -eq "TestBlockA" | Should -Be $true $reservation.Desc -eq "Test Reservation C" | Should -Be $true @@ -938,7 +941,7 @@ Context 'Tools' { New-ApiResource '/spaces' $toolsSpace $spaces, $spacesStatus = Get-ApiResource '/spaces' - + $spaces.Count | Should -Be 2 $spaces.Name -eq 'TestSpaceA' | Should -Be $true $spaces.Name -eq 'ToolsSpace' | Should -Be $true @@ -954,7 +957,7 @@ Context 'Tools' { New-ApiResource '/spaces/ToolsSpace/blocks' $toolsBlock $blocks, $blocksStatus = Get-ApiResource '/spaces/ToolsSpace/blocks' - + $blocks.Count | Should -Be 1 $blocks.Name -eq 'ToolsBlock' | Should -Be $true diff --git a/tools/build.ps1 b/tools/build.ps1 index 80bd2436..88aaf774 100644 --- a/tools/build.ps1 +++ b/tools/build.ps1 @@ -1,7 +1,7 @@ ############################################################################################################### ## ## Azure IPAM Zip Deploy Archive Creation Script -## +## ############################################################################################################### # Set minimum version requirements @@ -11,7 +11,7 @@ param( [Parameter(ValueFromPipelineByPropertyName = $true, Mandatory = $true)] - [ValidateScript({ + [ValidateScript({ if (Test-Path -LiteralPath $_ -PathType Container) { return $true } @@ -32,15 +32,21 @@ param( throw 'File name contains invalid characters' })] [string] - $FileName = 'ipam.zip' + $FileName = 'ipam.zip', + + # Use this to use "npm install" instead of "npm ci" and direct pip to install "requirements.txt" instead of "requirements.lock.txt" + [Parameter(ValueFromPipelineByPropertyName = $true, + Mandatory = $false)] + [switch] + $ManifestOnly ) # Root Directory $ROOT_DIR = (Get-Item $($MyInvocation.MyCommand.Path)).Directory.Parent.FullName # Define minimum NodeJS and NPM versions required to build the Azure IPAM UI solution -$MIN_NODE_VERSION = [version]'18.0.0' -$MIN_NPM_VERSION = [version]'8.6.0' +$MIN_NODE_VERSION = [version]'22.12.0' +$MIN_NPM_VERSION = [version]'10.9.2' # Load Python version required to build the Azure IPAM UI solution $engineAppDir = Join-Path -Path $ROOT_DIR -ChildPath "engine" -AdditionalChildPath "app" @@ -58,17 +64,25 @@ $ErrorActionPreference = "Stop" $logPath = Join-Path -Path $ROOT_DIR -ChildPath "logs" New-Item -ItemType Directory -Path $logpath -Force | Out-Null -$buildLog = Join-Path -Path $logPath -ChildPath "build_$(get-date -format `"yyyyMMddhhmmsstt`").log" +$errorLog = Join-Path -Path $logPath -ChildPath "error_$(get-date -format `"yyyyMMddhhmmsstt`").log" +$transcriptLog = Join-Path -Path $logPath -ChildPath "build_$(get-date -format `"yyyyMMddhhmmsstt`").log" -Start-Transcript -Path $buildLog | Out-Null +Start-Transcript -Path $transcriptLog | Out-Null try { Write-Host + + if ($ManifestOnly) { + Write-Host "NOTE: " -ForegroundColor Magenta -NoNewline + Write-Host "ManifestOnly" -ForegroundColor Cyan -NoNewline + Write-Host " flag is set!" -ForegroundColor Magenta + } + Write-Host "INFO: Verifying NodeJS is present and has the correct version" -ForegroundColor Green # Check for NodeJS and NPM and fetch their current versions try { - $npmErr = $( + $npmErr = $( $npmDetails = npm version --json ) 2>&1 } catch { @@ -112,7 +126,7 @@ try { # Check for PIP and fetch the associated Python version try { - $pipErr = $( + $pipErr = $( $pipDetails = pip --version ) 2>&1 } catch { @@ -125,7 +139,15 @@ try { # Extract Python version and exit if it doesn't match required version if($null -eq $pipErr) { - $pythonVersion = [version]$([regex]::matches($pipDetails, '(?!=[(python ])[\d]+\.[\d]+(?=[)])').value) + try { + $pythonVersion = [version]$([regex]::matches($pipDetails, '(?!=[(python ])[\d]+\.[\d]+(?=[)])').value) + } catch { + Write-Host "ERROR: Cannot extract Python version!" -ForegroundColor red + Write-Host "ERROR: Python " -ForegroundColor red -NoNewline + Write-Host "v$PYTHON_VERSION" -ForegroundColor cyan -NoNewline + Write-Host " and PIP are required to build the Azure IPAM code package!" -ForegroundColor red + exit + } } else { Write-Host "ERROR: Python PIP not detected!" -ForegroundColor red Write-Host "ERROR: Python " -ForegroundColor red -NoNewline @@ -155,38 +177,54 @@ try { Write-Host "INFO: Running NPM Install..." -ForegroundColor Green # Install Azure IPAM UI Dependencies - $npmInstallErr = $( - $npmInstall = npm ci --no-progress --no-update-notifier --no-fund --loglevel error - ) 2>&1 - - # Switch back to original dir - Pop-Location + try { + # Capture all output for logging purposes + $npmOutput = if ($ManifestOnly) { + npm install --no-progress --no-update-notifier --no-fund --loglevel error 2>&1 + } else { + npm ci --no-progress --no-update-notifier --no-fund --loglevel error 2>&1 + } - # Exit if NPM Install fails - if($npmInstallErr) { + # Throw error if NPM Install fails + if ($LASTEXITCODE -ne 0) { + throw "NPM Install failed with exit code $LASTEXITCODE. Output: $($npmOutput -join "`n")" + } + } + catch { + # Switch back to original dir before throwing error + Pop-Location Write-Host "ERROR: NPM Install failed!" -ForegroundColor red - throw $npmInstallErr + throw $_ } + # Switch back to original dir + Pop-Location + # Switch to UI dir for build process Push-Location -Path $uiDir Write-Host "INFO: Running NPM Build..." -ForegroundColor Green # Build Azure IPAM UI - $npmBuildErr = $( - $npmBuild = npm run build --no-update-notifier - ) 2>&1 - - # Switch back to original dir - Pop-Location + try { + # Capture all output for logging purposes + $npmBuildOutput = npm run build --no-update-notifier 2>&1 - # Exit if NPM Build fails - if($npmBuildErr) { + # Throw error if NPM Build fails + if ($LASTEXITCODE -ne 0) { + throw "NPM Build failed with exit code $LASTEXITCODE. Output: $($npmBuildOutput -join "`n")" + } + } + catch { + # Switch back to original dir before throwing error + Pop-Location Write-Host "ERROR: NPM Build failed!" -ForegroundColor red - throw $npmBuildErr + throw $_ } + # Switch back to original dir + Pop-Location + # Create temporary directory New-Item -ItemType Directory -Path $tempFolder -Force | Out-Null @@ -202,19 +240,29 @@ try { $packageDir = New-Item -ItemType Directory -Path (Join-Path -Path $tempFolder -ChildPath "packages") # Fetch Azure IPAM Engine modules - $pipInstallErr = $( - $pipInstall = pip install -r requirements.lock.txt --target $packageDir.FullName --no-warn-script-location --progress-bar off - ) 2>&1 - - # Switch back to original dir - Pop-Location + try { + # Capture all output for logging purposes + $pipOutput = if ($ManifestOnly) { + pip install -r requirements.txt --target $packageDir.FullName --no-warn-script-location --no-user --progress-bar off 2>&1 + } else { + pip install -r requirements.lock.txt --target $packageDir.FullName --no-warn-script-location --no-user --progress-bar off 2>&1 + } - # Exit if PIP Install fails - if($pipInstallErr) { + # Throw error if PIP Install fails + if ($LASTEXITCODE -ne 0) { + throw "PIP Install failed with exit code $LASTEXITCODE. Output: $($pipOutput -join "`n")" + } + } + catch { + # Switch back to original dir before throwing error + Pop-Location Write-Host "ERROR: PIP Install failed!" -ForegroundColor red - throw $pipInstallErr + throw $_ } + # Switch back to original dir + Pop-Location + # Create the Azure IPAM ZIP Deploy archive if NPM Build and PIP install were successful if((-not $npmBuildErr) -and (-not $pipInstallErr)) { $FilePath = Join-Path -Path $Path -ChildPath $FileName @@ -251,7 +299,7 @@ try { Write-Host "ZIP Asset Path: $fullPath" -ForegroundColor Yellow } catch { - $_ | Out-File -FilePath $buildLog -Append + $_ | Out-File -FilePath $errorLog -Append Write-Host "ERROR: Unable to build Azure IPAM Zip assets due to an exception, see log for detailed information!" -ForegroundColor red Write-Host "Build Log: $buildLog" -ForegroundColor Red diff --git a/ui/.devcontainer/devcontainer.json b/ui/.devcontainer/devcontainer.json index 3ff80516..710f16eb 100644 --- a/ui/.devcontainer/devcontainer.json +++ b/ui/.devcontainer/devcontainer.json @@ -3,10 +3,10 @@ "build": { // "context": "..", "dockerfile": "Dockerfile", - // Update 'VARIANT' to pick a Node version: 18, 16, 14. + // Update 'VARIANT' to pick a Node version: 22, 20, 18, 16, 14. // Append -bullseye or -buster to pin to an OS version. // Use -bullseye variants on local arm64/Apple Silicon. - "args": { "VARIANT": "18-bullseye" }, + "args": { "VARIANT": "22-bullseye" }, }, // Remove container when terminated diff --git a/ui/Dockerfile.deb b/ui/Dockerfile.deb index 6d22c7c1..b71a3f2e 100644 --- a/ui/Dockerfile.deb +++ b/ui/Dockerfile.deb @@ -1,8 +1,12 @@ -ARG BASE_IMAGE=node:18-slim +ARG BASE_IMAGE=node:22-slim FROM $BASE_IMAGE +# Set Default Port ARG PORT=80 +# Set Debian Frontend to Non-Interactive +ARG DEBIAN_FRONTEND=noninteractive + # Set Environment Variable ENV PORT=${PORT} diff --git a/ui/Dockerfile.dev b/ui/Dockerfile.dev index 79a94a85..b5b9e29e 100644 --- a/ui/Dockerfile.dev +++ b/ui/Dockerfile.dev @@ -1,6 +1,7 @@ -ARG VARIANT=18-bullseye +ARG VARIANT=22-bullseye FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:${VARIANT} +# Set Default Port ARG PORT=3000 # Set Working Directory diff --git a/ui/Dockerfile.rhel b/ui/Dockerfile.rhel index 54a2078b..15b0025a 100644 --- a/ui/Dockerfile.rhel +++ b/ui/Dockerfile.rhel @@ -1,6 +1,7 @@ -ARG BASE_IMAGE=registry.access.redhat.com/ubi8/nodejs-18 +ARG BASE_IMAGE=registry.access.redhat.com/ubi8/nodejs-22 FROM $BASE_IMAGE +# Set Default Port ARG PORT=8080 # Set Environment Variable diff --git a/ui/eslint.config.js b/ui/eslint.config.js index 82b33a84..98ccad66 100644 --- a/ui/eslint.config.js +++ b/ui/eslint.config.js @@ -7,6 +7,45 @@ import jest from "eslint-plugin-jest"; export default [ js.configs.recommended, + { + files: ["*.js", "*.mjs", "*.cjs"], + languageOptions: { + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + globals: { + ...globals.node, + } + }, + rules: { + "no-unused-vars": "off", + "no-prototype-builtins": "off", + "no-constant-binary-expression": "off" + }, + }, + { + files: ["src/**/*.js"], + plugins: { + jest + }, + languageOptions: { + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + globals: { + ...globals.node, + ...globals.browser, + ...globals.jest + } + }, + rules: { + "no-unused-vars": "off", + "no-prototype-builtins": "off", + "no-constant-binary-expression": "off" + }, + }, { files: ["src/**/*.jsx"], plugins: { @@ -42,4 +81,61 @@ export default [ "no-constant-binary-expression": "off" }, }, + { + files: ["src/**/*.ts"], + plugins: { + jest + }, + languageOptions: { + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + }, + globals: { + ...globals.node, + ...globals.browser, + ...globals.jest + } + }, + rules: { + "no-unused-vars": "off", + "no-prototype-builtins": "off", + "no-constant-binary-expression": "off" + }, + }, + { + files: ["src/**/*.tsx"], + plugins: { + react, + "react-hooks": hooks, + jest + }, + settings: { + react: { + version: "detect", + } + }, + languageOptions: { + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: 'latest', + sourceType: 'module', + }, + globals: { + ...globals.node, + ...globals.browser, + ...globals.jest + } + }, + rules: { + "no-unused-vars": "off", + "no-prototype-builtins": "off", + "react/prop-types": "off", + "react/display-name": "off", + "react/no-unescaped-entities": "off", + "no-constant-binary-expression": "off" + }, + }, ]; diff --git a/ui/package-lock.json b/ui/package-lock.json index f2c2cec5..33819a29 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,52 +1,52 @@ { "name": "azure-ipam-ui", - "version": "3.4.0", + "version": "3.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "azure-ipam-ui", - "version": "3.4.0", + "version": "3.5.0", "dependencies": { - "@azure/msal-browser": "^3.27.0", - "@azure/msal-react": "^2.2.0", - "@emotion/react": "^11.13.5", - "@emotion/styled": "^11.13.5", + "@azure/msal-browser": "^4.22.1", + "@azure/msal-react": "^3.0.19", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", "@inovua/reactdatagrid-community": "^5.10.2", - "@mui/icons-material": "^6.1.8", - "@mui/lab": "^6.0.0-beta.16", - "@mui/material": "^6.1.8", - "@reduxjs/toolkit": "^2.3.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.0.1", - "@testing-library/user-event": "^14.5.2", - "axios": "^1.7.7", - "echarts": "^5.5.1", - "echarts-for-react": "^3.0.2", - "globals": "^15.12.0", + "@mui/icons-material": "^7.3.2", + "@mui/lab": "^7.0.0-beta.17", + "@mui/material": "^7.3.2", + "@reduxjs/toolkit": "^2.9.0", + "@testing-library/jest-dom": "^6.8.0", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", + "axios": "^1.11.0", + "echarts": "^6.0.0", + "echarts-for-react": "^3.0.4", + "globals": "^16.4.0", "lodash": "^4.17.21", "md5": "^2.3.0", "moment": "^2.30.1", - "notistack": "^3.0.1", + "notistack": "^3.0.2", "pluralize": "^8.0.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-draggable": "^4.4.6", - "react-redux": "^9.1.2", - "react-router-dom": "^6.28.0", - "spinners-react": "^1.0.10", - "web-vitals": "^4.2.4" + "react-draggable": "^4.5.0", + "react-redux": "^9.2.0", + "react-router": "^7.8.2", + "spinners-react": "^1.0.11", + "web-vitals": "^5.1.0" }, "devDependencies": { - "@eslint/js": "^9.15.0", - "@vitejs/plugin-react": "^4.3.3", - "eslint": "^9.15.0", - "eslint-plugin-jest": "^28.9.0", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.0.0", - "serve": "^14.2.4", - "vite": "^5.4.11", - "vite-plugin-eslint2": "^5.0.2" + "@eslint/js": "^9.35.0", + "@vitejs/plugin-react": "^5.0.2", + "eslint": "^9.35.0", + "eslint-plugin-jest": "^29.0.1", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "serve": "^14.2.5", + "vite": "^7.1.5", + "vite-plugin-eslint2": "^5.0.4" } }, "node_modules/@adobe/css-tools": { @@ -59,6 +59,7 @@ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -68,74 +69,80 @@ } }, "node_modules/@azure/msal-browser": { - "version": "3.27.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.27.0.tgz", - "integrity": "sha512-+b4ZKSD8+vslCtVRVetkegEhOFMLP3rxDWJY212ct+2r6jVg6OSQKc1Qz3kCoXo0FgwaXkb+76TMZfpHp8QtgA==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.22.1.tgz", + "integrity": "sha512-/I76rBJpt5ZVfFXk+GkKxD4w1DZEbVpNn0aQjvRgnDnTYo3L/f8Oeo3R1O9eL/ccg5j1537iRLr7UwVhwnHtyg==", + "license": "MIT", "dependencies": { - "@azure/msal-common": "14.16.0" + "@azure/msal-common": "15.12.0" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "14.16.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", - "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", + "version": "15.12.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.12.0.tgz", + "integrity": "sha512-4ucXbjVw8KJ5QBgnGJUeA07c8iznwlk5ioHIhI4ASXcXgcf2yRFhWzYOyWg/cI49LC9ekpFJeQtO3zjDTbl6TQ==", + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-2.2.0.tgz", - "integrity": "sha512-2V+9JXeXyyjYNF92y5u0tU4el9px/V1+vkRuN+DtoxyiMHCtYQpJoaFdGWArh43zhz5aqQqiGW/iajPDSu3QsQ==", + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/@azure/msal-react/-/msal-react-3.0.19.tgz", + "integrity": "sha512-309fo4+V0vUlZolMDv2w+JlZBH1Fr2/vpPtMbZNhGYjKrexEBWNx3uAPVCa4Vyf/egWxXYTXAcbRhd6+Wlp8Lg==", + "license": "MIT", "engines": { "node": ">=10" }, "peerDependencies": { - "@azure/msal-browser": "^3.27.0", - "react": "^16.8.0 || ^17 || ^18" + "@azure/msal-browser": "^4.21.0", + "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.2.tgz", - "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -154,17 +161,19 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/generator": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.2.tgz", - "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", - "dependencies": { - "@babel/parser": "^7.26.2", - "@babel/types": "^7.26.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -172,13 +181,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -187,27 +197,38 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -217,58 +238,64 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz", - "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -278,12 +305,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -293,12 +321,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -308,61 +337,54 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", - "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -387,9 +409,10 @@ } }, "node_modules/@emotion/cache": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.13.5.tgz", - "integrity": "sha512-Z3xbtJ+UcK76eWkagZ1onvn/wAVb1GOMuR15s30Fm2wrMgC7jzpnO2JZXr4eujTTqoQFUrZIw/rT0c6Zzjca1g==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", "dependencies": { "@emotion/memoize": "^0.9.0", "@emotion/sheet": "^1.4.0", @@ -417,15 +440,16 @@ "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" }, "node_modules/@emotion/react": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.13.5.tgz", - "integrity": "sha512-6zeCUxUH+EPF1s+YF/2hPVODeV/7V07YU5x+2tfuRL8MdW6rv5vb2+CBEGTGwBdux0OIERcOS+RzxeK80k2DsQ==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", - "@emotion/cache": "^11.13.5", + "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" @@ -454,18 +478,20 @@ "node_modules/@emotion/sheet": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", - "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" }, "node_modules/@emotion/styled": { - "version": "11.13.5", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.13.5.tgz", - "integrity": "sha512-gnOQ+nGLPvDXgIx119JqGalys64lhMdnNQA9TMxhDA4K0Hq5+++OE20Zs5GxiCV9r814xQ2K5WmtofSpHVW6BQ==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/is-prop-valid": "^1.3.0", "@emotion/serialize": "^1.3.3", - "@emotion/use-insertion-effect-with-fallbacks": "^1.1.0", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2" }, "peerDependencies": { @@ -484,9 +510,10 @@ "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.1.0.tgz", - "integrity": "sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", "peerDependencies": { "react": ">=16.8.0" } @@ -502,378 +529,436 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -909,12 +994,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", - "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -922,20 +1008,35 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", - "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -959,6 +1060,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -967,69 +1069,42 @@ } }, "node_modules/@eslint/js": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", - "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.35.0.tgz", + "integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", - "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", "dev": true, + "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.15.2", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@floating-ui/core": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", - "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", - "dependencies": { - "@floating-ui/utils": "^0.2.8" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.12", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", - "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.8" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", - "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", - "dependencies": { - "@floating-ui/dom": "^1.0.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", - "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1079,10 +1154,11 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -1113,16 +1189,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1133,74 +1206,38 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.62", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.62.tgz", - "integrity": "sha512-TzJLCNlrMkSU4bTCdTT+TVUiGx4sjZLhH673UV6YN+rNNP8wJpkWfRSvjDB5HcbH2T0lUamnz643ZnV+8IiMjw==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@floating-ui/react-dom": "^2.1.1", - "@mui/types": "^7.2.19", - "@mui/utils": "^6.1.8", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.1", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.8.tgz", - "integrity": "sha512-TGAvzwUg9hybDacwfIGFjI2bXYXrIqky+vMfaeay8rvT56/PNAlvIDUJ54kpT5KRc9AWAihOvtDI7/LJOThOmQ==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.2.tgz", + "integrity": "sha512-AOyfHjyDKVPGJJFtxOlept3EYEdLoar/RvssBTWVAvDJGIE676dLi2oT/Kx+FoVXFoA/JdV7DEMq/BVWV3KHRw==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.1.8.tgz", - "integrity": "sha512-6frsXcf1TcJKWevWwRup6V4L8lzI33cbHcAjT83YLgKw0vYRZKY0kjMI9fhrJZdRWXgFFgKKvEv3GjoxbqFF7A==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.2.tgz", + "integrity": "sha512-TZWazBjWXBjR6iGcNkbKklnwodcwj0SrChCNHc9BhD9rBgET22J1eFhHsEmvSvru9+opDy3umqAimQjokhfJlQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0" + "@babel/runtime": "^7.28.3" }, "engines": { "node": ">=14.0.0" @@ -1210,7 +1247,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.1.8", + "@mui/material": "^7.3.2", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1221,15 +1258,15 @@ } }, "node_modules/@mui/lab": { - "version": "6.0.0-beta.16", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.16.tgz", - "integrity": "sha512-YFeKREMMCiUhp4dGXd6Y/7N3BLepys9bM6xi4aF0WTZOvfl1ksDXPzuXPGiiiIuMgQFJeyN5iUnS1iPu3wH+kQ==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/base": "5.0.0-beta.62", - "@mui/system": "^6.1.8", - "@mui/types": "^7.2.19", - "@mui/utils": "^6.1.8", + "version": "7.0.0-beta.17", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-7.0.0-beta.17.tgz", + "integrity": "sha512-H8tSINm6Xgbi7o49MplAwks4tAEE6SpFNd9l7n4NURl0GSpOv0CZvgXKSJt4+6TmquDhE7pomHpHWJiVh/2aCg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.3", + "@mui/system": "^7.3.2", + "@mui/types": "^7.4.6", + "@mui/utils": "^7.3.2", "clsx": "^2.1.1", "prop-types": "^15.8.1" }, @@ -1243,8 +1280,8 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material": "^6.1.8", - "@mui/material-pigment-css": "^6.1.8", + "@mui/material": "^7.3.2", + "@mui/material-pigment-css": "^7.3.2", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1265,21 +1302,22 @@ } }, "node_modules/@mui/material": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.8.tgz", - "integrity": "sha512-QZdQFnXct+7NXIzHgT3qt+sQiO7HYGZU2vymP9Xl9tUMXEOA/S1mZMMb7+WGZrk5TzNlU/kP/85K0da5V1jXoQ==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.1.8", - "@mui/system": "^6.1.8", - "@mui/types": "^7.2.19", - "@mui/utils": "^6.1.8", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.2.tgz", + "integrity": "sha512-qXvbnawQhqUVfH1LMgMaiytP+ZpGoYhnGl7yYq2x57GYzcFL/iPzSZ3L30tlbwEjSVKNYcbiKO8tANR1tadjUg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.3", + "@mui/core-downloads-tracker": "^7.3.2", + "@mui/system": "^7.3.2", + "@mui/types": "^7.4.6", + "@mui/utils": "^7.3.2", "@popperjs/core": "^2.11.8", - "@types/react-transition-group": "^4.4.11", + "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1", - "react-is": "^18.3.1", + "react-is": "^19.1.1", "react-transition-group": "^4.4.5" }, "engines": { @@ -1292,7 +1330,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.1.8", + "@mui/material-pigment-css": "^7.3.2", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1313,12 +1351,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.8.tgz", - "integrity": "sha512-TuKl7msynCNCVvhX3c0ef1sF0Qb3VHcPs8XOGB/8bdOGBr/ynmIG1yTMjZeiFQXk8yN9fzK/FDEKMFxILNn3wg==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.2.tgz", + "integrity": "sha512-ha7mFoOyZGJr75xeiO9lugS3joRROjc8tG1u4P50dH0KR7bwhHznVMcYg7MouochUy0OxooJm/OOSpJ7gKcMvg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.1.8", + "@babel/runtime": "^7.28.3", + "@mui/utils": "^7.3.2", "prop-types": "^15.8.1" }, "engines": { @@ -1339,13 +1378,14 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.8.tgz", - "integrity": "sha512-ZvEoT0U2nPLSLI+B4by4cVjaZnPT2f20f4JUPkyHdwLv65ZzuoHiTlwyhqX1Ch63p8bcJzKTHQVGisEoMK6PGA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.2.tgz", + "integrity": "sha512-PkJzW+mTaek4e0nPYZ6qLnW5RGa0KN+eRTf5FA2nc7cFZTeM+qebmGibaTLrgQBy3UpcpemaqfzToBNkzuxqew==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.26.0", - "@emotion/cache": "^11.13.1", - "@emotion/serialize": "^1.3.2", + "@babel/runtime": "^7.28.3", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1372,15 +1412,16 @@ } }, "node_modules/@mui/system": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.8.tgz", - "integrity": "sha512-i1kLfQoWxzFpXTBQIuPoA3xKnAnP3en4I2T8xIolovSolGQX5k8vGjw1JaydQS40td++cFsgCdEU458HDNTGUA==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.1.8", - "@mui/styled-engine": "^6.1.8", - "@mui/types": "^7.2.19", - "@mui/utils": "^6.1.8", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.2.tgz", + "integrity": "sha512-9d8JEvZW+H6cVkaZ+FK56R53vkJe3HsTpcjMUtH8v1xK6Y1TjzHdZ7Jck02mGXJsE6MQGWVs3ogRHTQmS9Q/rA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.3", + "@mui/private-theming": "^7.3.2", + "@mui/styled-engine": "^7.3.2", + "@mui/types": "^7.4.6", + "@mui/utils": "^7.3.2", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1411,9 +1452,13 @@ } }, "node_modules/@mui/types": { - "version": "7.2.19", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz", - "integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==", + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.6.tgz", + "integrity": "sha512-NVBbIw+4CDMMppNamVxyTccNv0WxtDb7motWDlMeSC8Oy95saj1TIZMGynPpFLePt3yOD8TskzumeqORCgRGWw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.3" + }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1424,16 +1469,17 @@ } }, "node_modules/@mui/utils": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.8.tgz", - "integrity": "sha512-O2DWb1kz8hiANVcR7Z4gOB3SvPPsSQGUmStpyBDzde6dJIfBzgV9PbEQOBZd3EBsd1pB+Uv1z5LAJAbymmawrA==", - "dependencies": { - "@babel/runtime": "^7.26.0", - "@mui/types": "^7.2.19", - "@types/prop-types": "^15.7.13", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.2.tgz", + "integrity": "sha512-4DMWQGenOdLnM3y/SdFQFwKsCLM+mqxzvoWp9+x2XdEzXapkznauHLiXtSohHs/mc0+5/9UACt1GdugCX2te5g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.3", + "@mui/types": "^7.4.6", + "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.3.1" + "react-is": "^19.1.1" }, "engines": { "node": ">=14.0.0" @@ -1497,17 +1543,20 @@ } }, "node_modules/@reduxjs/toolkit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.3.0.tgz", - "integrity": "sha512-WC7Yd6cNGfHx8zf+iu+Q1UPTfEcXhQ+ATi7CV1hlrSAaQBdlPzg7Ww/wJHNQem7qG9rxmWoFCDCPubSvFObGzA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.9.0.tgz", + "integrity": "sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog==", + "license": "MIT", "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", "immer": "^10.0.3", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { - "react": "^16.9.0 || ^17.0.0 || ^18", + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "peerDependenciesMeta": { @@ -1519,19 +1568,19 @@ } } }, - "node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", - "engines": { - "node": ">=14.0.0" - } + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.34", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.34.tgz", + "integrity": "sha512-LyAREkZHP5pMom7c24meKmJCdhf2hEyvam2q0unr3or9ydwDL+DJ8chTF6Av/RFPb3rH8UFBdMzO5MxTZW97oA==", + "dev": true, + "license": "MIT" }, "node_modules/@rollup/pluginutils": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.3.tgz", - "integrity": "sha512-Pnsb6f32CD2W3uCaLZIzDmeFyQ2b8UWMFI7xtwUezpcGBDVDW6y9XgAWIlARiGAo6eNF5FK5aQTr0LFyNyqq5A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", @@ -1550,239 +1599,297 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.3.tgz", - "integrity": "sha512-EzxVSkIvCFxUd4Mgm4xR9YXrcp976qVaHnqom/Tgm+vU79k4vV4eYTjmRvGfeoW8m9LVcsAy/lGjcgVegKEhLQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.3.tgz", - "integrity": "sha512-LJc5pDf1wjlt9o/Giaw9Ofl+k/vLUaYsE2zeQGH85giX2F+wn/Cg8b3c5CDP3qmVmeO5NzwVUzQQxwZvC2eQKw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.3.tgz", - "integrity": "sha512-OuRysZ1Mt7wpWJ+aYKblVbJWtVn3Cy52h8nLuNSzTqSesYw1EuN6wKp5NW/4eSre3mp12gqFRXOKTcN3AI3LqA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.3.tgz", - "integrity": "sha512-xW//zjJMlJs2sOrCmXdB4d0uiilZsOdlGQIC/jjmMWT47lkLLoB1nsNhPUcnoqyi5YR6I4h+FjBpILxbEy8JRg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.3.tgz", - "integrity": "sha512-58E0tIcwZ+12nK1WiLzHOD8I0d0kdrY/+o7yFVPRHuVGY3twBwzwDdTIBGRxLmyjciMYl1B/U515GJy+yn46qw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.3.tgz", - "integrity": "sha512-78fohrpcVwTLxg1ZzBMlwEimoAJmY6B+5TsyAZ3Vok7YabRBUvjYTsRXPTjGEvv/mfgVBepbW28OlMEz4w8wGA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.3.tgz", - "integrity": "sha512-h2Ay79YFXyQi+QZKo3ISZDyKaVD7uUvukEHTOft7kh00WF9mxAaxZsNs3o/eukbeKuH35jBvQqrT61fzKfAB/Q==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.3.tgz", - "integrity": "sha512-Sv2GWmrJfRY57urktVLQ0VKZjNZGogVtASAgosDZ1aUB+ykPxSi3X1nWORL5Jk0sTIIwQiPH7iE3BMi9zGWfkg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.3.tgz", - "integrity": "sha512-FPoJBLsPW2bDNWjSrwNuTPUt30VnfM8GPGRoLCYKZpPx0xiIEdFip3dH6CqgoT0RnoGXptaNziM0WlKgBc+OWQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.3.tgz", - "integrity": "sha512-TKxiOvBorYq4sUpA0JT+Fkh+l+G9DScnG5Dqx7wiiqVMiRSkzTclP35pE6eQQYjP4Gc8yEkJGea6rz4qyWhp3g==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.3.tgz", - "integrity": "sha512-v2M/mPvVUKVOKITa0oCFksnQQ/TqGrT+yD0184/cWHIu0LoIuYHwox0Pm3ccXEz8cEQDLk6FPKd1CCm+PlsISw==", + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.3.tgz", - "integrity": "sha512-LdrI4Yocb1a/tFVkzmOE5WyYRgEBOyEhWYJe4gsDWDiwnjYKjNs7PS6SGlTDB7maOHF4kxevsuNBl2iOcj3b4A==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.3.tgz", - "integrity": "sha512-d4wVu6SXij/jyiwPvI6C4KxdGzuZOvJ6y9VfrcleHTwo68fl8vZC5ZYHsCVPUi4tndCfMlFniWgwonQ5CUpQcA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.3.tgz", - "integrity": "sha512-/6bn6pp1fsCGEY5n3yajmzZQAh+mW4QPItbiWxs69zskBzJuheb3tNynEjL+mKOsUSFK11X4LYF2BwwXnzWleA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.3.tgz", - "integrity": "sha512-nBXOfJds8OzUT1qUreT/en3eyOXd2EH5b0wr2bVB5999qHdGKkzGzIyKYaKj02lXk6wpN71ltLIaQpu58YFBoQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.3.tgz", - "integrity": "sha512-ogfbEVQgIZOz5WPWXF2HVb6En+kWzScuxJo/WdQTqEgeyGkaa2ui5sQav9Zkr7bnNCLK48uxmmK0TySm22eiuw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.3.tgz", - "integrity": "sha512-ecE36ZBMLINqiTtSNQ1vzWc5pXLQHlf/oqGp/bSbi7iedcjcNb6QbCBNG73Euyy2C+l/fn8qKWEwxr+0SSfs3w==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.3.tgz", - "integrity": "sha512-vliZLrDmYKyaUoMzEbMTg2JkerfBjn03KmAw9CykO0Zzkzoyd7o3iZNam/TpyWNjNT+Cz2iO3P9Smv2wgrR+Eg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -1803,16 +1910,16 @@ } }, "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.8.0.tgz", + "integrity": "sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==", + "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", - "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.21", + "picocolors": "^1.1.1", "redent": "^3.0.0" }, "engines": { @@ -1821,27 +1928,16 @@ "yarn": ">=1" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==" }, "node_modules/@testing-library/react": { - "version": "16.0.1", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", - "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.0.tgz", + "integrity": "sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" }, @@ -1850,10 +1946,10 @@ }, "peerDependencies": { "@testing-library/dom": "^10.0.0", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1865,9 +1961,10 @@ } }, "node_modules/@testing-library/user-event": { - "version": "14.5.2", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", - "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "license": "MIT", "engines": { "node": ">=12", "npm": ">=6" @@ -1924,16 +2021,18 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/lodash": { "version": "4.17.13", @@ -1962,14 +2061,16 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" }, "node_modules/@types/react": { "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -1985,17 +2086,19 @@ } }, "node_modules/@types/react-transition-group": { - "version": "4.4.11", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", - "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", - "dependencies": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { "@types/react": "*" } }, "node_modules/@types/use-sync-external-store": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz", - "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==" + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.15.0", @@ -2056,10 +2159,11 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -2136,22 +2240,24 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.3.tgz", - "integrity": "sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.2.tgz", + "integrity": "sha512-tmyFgixPZCx2+e6VO9TNITWcCQl8+Nl/E8YbAyPVv85QCc7/A3JrdfG2A8gIzvVhWuzMOVrFW1aReaNxrI6tbw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/core": "^7.25.2", - "@babel/plugin-transform-react-jsx-self": "^7.24.7", - "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@babel/core": "^7.28.3", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.34", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@zeit/schemas": { @@ -2160,24 +2266,12 @@ "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", "dev": true }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -2190,6 +2284,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -2199,6 +2294,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -2303,7 +2399,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/aria-query": { "version": "5.3.0", @@ -2314,13 +2411,14 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" }, "engines": { "node": ">= 0.4" @@ -2388,15 +2486,16 @@ } }, "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2422,19 +2521,19 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" }, "engines": { "node": ">= 0.4" @@ -2443,16 +2542,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -2464,12 +2575,13 @@ } }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", + "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, @@ -2528,10 +2640,11 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2550,9 +2663,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -2568,11 +2681,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.1" + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -2591,16 +2705,16 @@ } }, "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "set-function-length": "^1.2.2" }, "engines": { "node": ">= 0.4" @@ -2609,20 +2723,50 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=6" + "node": ">= 0.4" } }, - "node_modules/camelcase": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", - "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "dev": true, - "engines": { + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "engines": { "node": ">=14.16" }, "funding": { @@ -2630,9 +2774,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", "dev": true, "funding": [ { @@ -2647,7 +2791,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "4.1.2", @@ -2744,6 +2889,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2756,6 +2902,7 @@ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -2764,28 +2911,40 @@ } }, "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", "dev": true, + "license": "MIT", "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", + "bytes": "3.1.2", + "compressible": "~2.0.18", "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", "vary": "~1.1.2" }, "engines": { "node": ">= 0.8.0" } }, + "node_modules/compression/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -2794,7 +2953,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", @@ -2816,6 +2976,15 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -2831,6 +3000,15 @@ "node": ">=10" } }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2864,14 +3042,15 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2881,29 +3060,31 @@ } }, "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "is-data-view": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/inspect-js" } }, "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" }, @@ -2915,9 +3096,10 @@ } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -2983,6 +3165,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -3017,11 +3200,26 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3029,32 +3227,35 @@ "dev": true }, "node_modules/echarts": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.1.tgz", - "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz", + "integrity": "sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==", + "license": "Apache-2.0", "dependencies": { "tslib": "2.3.0", - "zrender": "5.6.0" + "zrender": "6.0.0" } }, "node_modules/echarts-for-react": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-3.0.2.tgz", - "integrity": "sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-3.0.4.tgz", + "integrity": "sha512-rc7SNdr0JoMTkMJspp9ejlZAoipv2mMwbI30ggIgxSZMELdX36C2aJREvJ9OSkyetC/RoO1s7VrefXUrUAMClg==", + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "size-sensor": "^1.0.1" }, "peerDependencies": { - "echarts": "^3.0.0 || ^4.0.0 || ^5.0.0", + "echarts": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", "react": "^15.0.0 || >=16.0.0" } }, "node_modules/electron-to-chromium": { - "version": "1.5.63", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz", - "integrity": "sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==", - "dev": true + "version": "1.5.182", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.182.tgz", + "integrity": "sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -3071,57 +3272,66 @@ } }, "node_modules/es-abstract": { - "version": "1.23.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.5.tgz", - "integrity": "sha512-vlmniQ0WNPwXqA0BnmwV3Ng7HxiGlh6r5U6JcTMNx8OilcAGqVJBHJcPjqOMaczU9fRuRK5Px2BdVyPRnKMMVQ==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", "dev": true, + "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", + "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -3131,13 +3341,10 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3146,42 +3353,43 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } }, "node_modules/es-iterator-helpers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.0.tgz", - "integrity": "sha512-tpxqxncxnpw3c93u8n3VOzACmRFoVmWJqbWXvX/JfKbkhBw1oslgPrUfeSt2psuqyEJFD6N/9lg5i7bsKpoq+Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", + "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", - "gopd": "^1.0.1", + "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.3", - "safe-array-concat": "^1.1.2" + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -3190,14 +3398,15 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3213,14 +3422,15 @@ } }, "node_modules/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==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" }, "engines": { "node": ">= 0.4" @@ -3230,41 +3440,44 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" } }, "node_modules/escalade": { @@ -3272,6 +3485,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3288,31 +3502,33 @@ } }, "node_modules/eslint": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", - "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "version": "9.35.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.35.0.tgz", + "integrity": "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.15.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.35.0", + "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.5", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3347,19 +3563,20 @@ } }, "node_modules/eslint-plugin-jest": { - "version": "28.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.9.0.tgz", - "integrity": "sha512-rLu1s1Wf96TgUUxSw6loVIkNtUjq1Re7A9QdCCHSohnvXEBAjuL420h0T/fMmkQlNsQP2GhQzEUpYHPfxBkvYQ==", + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.0.1.tgz", + "integrity": "sha512-EE44T0OSMCeXhDrrdsbKAhprobKkPtJTbQz5yEktysNpHeDZTAL1SfDTNKmcFfJkY6yrQLtTKZALrD3j/Gpmiw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/utils": "^8.0.0" }, "engines": { - "node": "^16.10.0 || ^18.12.0 || >=20.0.0" + "node": "^20.12.0 || ^22.0.0 || >=24.0.0" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "@typescript-eslint/eslint-plugin": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", "jest": "*" }, "peerDependenciesMeta": { @@ -3372,28 +3589,29 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.37.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", - "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, + "license": "MIT", "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.2", + "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.1.0", + "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.8", + "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", - "object.values": "^1.2.0", + "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.11", + "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "engines": { @@ -3404,10 +3622,11 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0.tgz", - "integrity": "sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3433,10 +3652,11 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -3449,10 +3669,11 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3461,14 +3682,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3494,6 +3716,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -3514,7 +3737,8 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/esutils": { "version": "2.0.3", @@ -3590,7 +3814,8 @@ "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -3607,6 +3832,24 @@ "reusify": "^1.0.4" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3691,21 +3934,31 @@ } }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -3735,15 +3988,18 @@ } }, "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -3766,21 +4022,27 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3789,6 +4051,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -3802,14 +4077,15 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -3831,9 +4107,10 @@ } }, "node_modules/globals": { - "version": "15.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.12.0.tgz", - "integrity": "sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -3866,22 +4143,26 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3907,10 +4188,14 @@ } }, "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -3919,10 +4204,10 @@ } }, "node_modules/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, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3934,7 +4219,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -3983,6 +4267,7 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -4035,27 +4320,30 @@ "dev": true }, "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "hasown": "^2.0.2", + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4070,12 +4358,17 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4085,25 +4378,30 @@ } }, "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/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==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4122,6 +4420,7 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4144,11 +4443,14 @@ } }, "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", "dev": true, + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" }, "engines": { @@ -4159,12 +4461,14 @@ } }, "node_modules/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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4198,12 +4502,16 @@ } }, "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4219,12 +4527,16 @@ } }, "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4250,6 +4562,7 @@ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4279,12 +4592,14 @@ } }, "node_modules/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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4306,13 +4621,16 @@ } }, "node_modules/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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4326,6 +4644,7 @@ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4334,12 +4653,13 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "call-bound": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -4361,12 +4681,14 @@ } }, "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -4376,12 +4698,15 @@ } }, "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4391,12 +4716,13 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -4410,6 +4736,7 @@ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4418,25 +4745,30 @@ } }, "node_modules/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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-weakset": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4" + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -4461,7 +4793,8 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", @@ -4470,16 +4803,18 @@ "dev": true }, "node_modules/iterator.prototype": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.3.tgz", - "integrity": "sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -4495,6 +4830,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -4528,7 +4864,8 @@ "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==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -4541,6 +4878,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -4667,6 +5005,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -4680,6 +5019,15 @@ "lz-string": "bin/bin.js" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -4801,9 +5149,9 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -4811,6 +5159,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -4825,24 +5174,27 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "dev": true + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" }, "node_modules/notistack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.1.tgz", - "integrity": "sha512-ntVZXXgSQH5WYfyU+3HfcXuKaapzAJ8fBLQ/G618rn3yvSzEbnOB8ZSOwhX+dAORy/lw+GC2N061JA0+gYWTVA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.2.tgz", + "integrity": "sha512-0R+/arLYbK5Hh7mEfR2adt0tyXJcCC9KkA2hc56FeWik2QN6Bm/S4uW+BjzDARsJth5u06nTjelSw/VSnB1YEA==", + "license": "MIT", "dependencies": { "clsx": "^1.1.0", "goober": "^2.0.33" @@ -4856,8 +5208,8 @@ "url": "https://opencollective.com/notistack" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/notistack/node_modules/clsx": { @@ -4889,10 +5241,11 @@ } }, "node_modules/object-inspect": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", - "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -4910,14 +5263,17 @@ } }, "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", "object-keys": "^1.1.1" }, "engines": { @@ -4928,14 +5284,16 @@ } }, "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" + "es-object-atoms": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -4960,12 +5318,14 @@ } }, "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, @@ -4977,10 +5337,11 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5017,6 +5378,24 @@ "node": ">= 0.8.0" } }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -5124,10 +5503,11 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -5144,18 +5524,19 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -5171,8 +5552,9 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5307,6 +5689,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -5318,6 +5701,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5327,11 +5711,12 @@ } }, "node_modules/react-draggable": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz", - "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.5.0.tgz", + "integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==", + "license": "MIT", "dependencies": { - "clsx": "^1.1.1", + "clsx": "^2.1.1", "prop-types": "^15.8.1" }, "peerDependencies": { @@ -5339,30 +5724,24 @@ "react-dom": ">= 16.3.0" } }, - "node_modules/react-draggable/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.1.tgz", + "integrity": "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==", + "license": "MIT" }, "node_modules/react-redux": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.1.2.tgz", - "integrity": "sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", + "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", + "license": "MIT", "dependencies": { - "@types/use-sync-external-store": "^0.0.3", - "use-sync-external-store": "^1.0.0" + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" }, "peerDependencies": { - "@types/react": "^18.2.25", - "react": "^18.0", + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", "redux": "^5.0.0" }, "peerDependenciesMeta": { @@ -5375,48 +5754,42 @@ } }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-router": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", - "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.8.2.tgz", + "integrity": "sha512-7M2fR1JbIZ/jFWqelpvSZx+7vd7UlBTfdZqf6OSdF9g6+sfdqJDAWcak6ervbHph200ePlu+7G8LdoiC3ReyAQ==", + "license": "MIT", "dependencies": { - "@remix-run/router": "1.21.0" + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", - "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", - "dependencies": { - "@remix-run/router": "1.21.0", - "react-router": "6.28.0" - }, - "engines": { - "node": ">=14.0.0" + "react": ">=18", + "react-dom": ">=18" }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -5454,18 +5827,20 @@ } }, "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", + "es-abstract": "^1.23.9", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -5474,20 +5849,18 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, "node_modules/regexp.prototype.flags": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", - "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "set-function-name": "^2.0.2" }, "engines": { @@ -5573,12 +5946,13 @@ } }, "node_modules/rollup": { - "version": "4.27.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.27.3.tgz", - "integrity": "sha512-SLsCOnlmGt9VoZ9Ek8yBK8tAdmPHeppkw+Xa7yDlCEhDTvwYei03JlWo1fdc7YTfLZ4tD8riJCUyAgTbszk1fQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -5588,24 +5962,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.27.3", - "@rollup/rollup-android-arm64": "4.27.3", - "@rollup/rollup-darwin-arm64": "4.27.3", - "@rollup/rollup-darwin-x64": "4.27.3", - "@rollup/rollup-freebsd-arm64": "4.27.3", - "@rollup/rollup-freebsd-x64": "4.27.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.27.3", - "@rollup/rollup-linux-arm-musleabihf": "4.27.3", - "@rollup/rollup-linux-arm64-gnu": "4.27.3", - "@rollup/rollup-linux-arm64-musl": "4.27.3", - "@rollup/rollup-linux-powerpc64le-gnu": "4.27.3", - "@rollup/rollup-linux-riscv64-gnu": "4.27.3", - "@rollup/rollup-linux-s390x-gnu": "4.27.3", - "@rollup/rollup-linux-x64-gnu": "4.27.3", - "@rollup/rollup-linux-x64-musl": "4.27.3", - "@rollup/rollup-win32-arm64-msvc": "4.27.3", - "@rollup/rollup-win32-ia32-msvc": "4.27.3", - "@rollup/rollup-win32-x64-msvc": "4.27.3", + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" } }, @@ -5633,14 +6009,16 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, "engines": { @@ -5651,20 +6029,53 @@ } }, "node_modules/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 + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "is-regex": "^1.1.4" + "is-regex": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -5677,6 +6088,7 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -5691,10 +6103,11 @@ } }, "node_modules/serve": { - "version": "14.2.4", - "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.4.tgz", - "integrity": "sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==", + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz", + "integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==", "dev": true, + "license": "MIT", "dependencies": { "@zeit/schemas": "2.36.0", "ajv": "8.12.0", @@ -5703,7 +6116,7 @@ "chalk": "5.0.1", "chalk-template": "0.4.0", "clipboardy": "3.0.0", - "compression": "1.7.4", + "compression": "1.8.1", "is-port-reachable": "4.0.0", "serve-handler": "6.1.6", "update-check": "1.5.4" @@ -5785,6 +6198,12 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -5817,6 +6236,21 @@ "node": ">= 0.4" } }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/shallowequal": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-0.2.2.tgz", @@ -5847,15 +6281,73 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -5888,19 +6380,35 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/spinners-react": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/spinners-react/-/spinners-react-1.0.10.tgz", - "integrity": "sha512-VZy4IhnIVTw8tcsuWtUGz1VfRK4EDnnlIVd88ezkfiDQyYlmo+QbIAjJD1BRghTBr5RJBULuJ4MIPsJapTp1xw==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/spinners-react/-/spinners-react-1.0.11.tgz", + "integrity": "sha512-CpQZixAI3dW+nLuJSrOTz4bDroSwLoIO5ljFt8wc1qy39yhK6MUubKnPD3z9/xIwNWFqy3mMRlW1mik/Fgqjow==", + "license": "MIT", "peerDependencies": { - "@types/react": "^16.x || ^17.x || ^18.x", - "@types/react-dom": "^16.x || ^17.x || ^18.x", - "react": "^16.x || ^17.x || ^18.x", - "react-dom": "^16.x || ^17.x || ^18.x" + "@types/react": "^16.x || ^17.x || ^18.x || ^19.x", + "@types/react-dom": "^16.x || ^17.x || ^18.x || ^19.x", + "react": "^16.x || ^17.x || ^18.x || ^19.x", + "react-dom": "^16.x || ^17.x || ^18.x || ^19.x" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/string-width": { @@ -5921,23 +6429,25 @@ } }, "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", + "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -5957,15 +6467,19 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5975,15 +6489,20 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6057,6 +6576,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -6091,6 +6611,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6118,7 +6655,8 @@ "node_modules/tslib": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", - "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -6145,30 +6683,32 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.3", "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -6178,17 +6718,19 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" }, "engines": { "node": ">= 0.4" @@ -6198,17 +6740,18 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-proto": "^1.0.3", "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" }, "engines": { "node": ">= 0.4" @@ -6232,24 +6775,28 @@ } }, "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", + "call-bound": "^1.0.3", "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -6265,9 +6812,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -6296,11 +6844,12 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz", - "integrity": "sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/vary": { @@ -6308,25 +6857,30 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } }, "node_modules/vite": { - "version": "5.4.11", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.11.tgz", - "integrity": "sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==", + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", + "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -6335,19 +6889,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -6368,17 +6928,24 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, "node_modules/vite-plugin-eslint2": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/vite-plugin-eslint2/-/vite-plugin-eslint2-5.0.2.tgz", - "integrity": "sha512-2szmNvCsve7AfdPBPQ5JVMx5kOZXXt+S6derNfoNu7+EKa8qnDiMGOkQeSd/I62zIiS1wfvQW+QsGODbCFTGeQ==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/vite-plugin-eslint2/-/vite-plugin-eslint2-5.0.4.tgz", + "integrity": "sha512-3Yc7K2R/RrONB9JtwEh2Y40YP3tQi/3UiNHrwcYDsDBKDKnEu7B8PwmXLm7piDFRbxcnTPvgrV2LZnBpKP8JUw==", "dev": true, + "license": "MIT", "dependencies": { - "@rollup/pluginutils": "^5.1.3", - "debug": "^4.3.7" + "@rollup/pluginutils": "^5.2.0", + "debug": "^4.4.1" }, "engines": { "node": ">=18" @@ -6387,10 +6954,10 @@ "url": "https://github.com/sponsors/modyqyw" }, "peerDependencies": { - "@types/eslint": "^7.0.0 || ^8.0.0 || ^9.0.0-0", - "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0-0", + "@types/eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", "rollup": "^2.0.0 || ^3.0.0 || ^4.0.0", - "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "peerDependenciesMeta": { "@types/eslint": { @@ -6402,9 +6969,10 @@ } }, "node_modules/web-vitals": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.2.4.tgz", - "integrity": "sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.1.0.tgz", + "integrity": "sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==", + "license": "Apache-2.0" }, "node_modules/which": { "version": "2.0.2", @@ -6422,39 +6990,45 @@ } }, "node_modules/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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", "dev": true, + "license": "MIT", "dependencies": { - "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" + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-builtin-type": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", - "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", "dev": true, + "license": "MIT", "dependencies": { + "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", + "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", + "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", - "which-typed-array": "^1.1.15" + "which-typed-array": "^1.1.16" }, "engines": { "node": ">= 0.4" @@ -6468,6 +7042,7 @@ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, + "license": "MIT", "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -6482,15 +7057,18 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "dev": true, + "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, "engines": { @@ -6557,14 +7135,22 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, "engines": { - "node": ">= 6" + "node": ">= 14.6" } }, "node_modules/yocto-queue": { @@ -6580,9 +7166,10 @@ } }, "node_modules/zrender": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", - "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz", + "integrity": "sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==", + "license": "BSD-3-Clause", "dependencies": { "tslib": "2.3.0" } diff --git a/ui/package.json b/ui/package.json index 36b998a9..e9c71a57 100644 --- a/ui/package.json +++ b/ui/package.json @@ -4,34 +4,34 @@ "type": "module", "private": true, "dependencies": { - "@azure/msal-browser": "^3.27.0", - "@azure/msal-react": "^2.2.0", - "@emotion/react": "^11.13.5", - "@emotion/styled": "^11.13.5", + "@azure/msal-browser": "^4.22.1", + "@azure/msal-react": "^3.0.19", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", "@inovua/reactdatagrid-community": "^5.10.2", - "@mui/icons-material": "^6.1.8", - "@mui/lab": "^6.0.0-beta.16", - "@mui/material": "^6.1.8", - "@reduxjs/toolkit": "^2.3.0", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.0.1", - "@testing-library/user-event": "^14.5.2", - "axios": "^1.7.7", - "echarts": "^5.5.1", - "echarts-for-react": "^3.0.2", - "globals": "^15.12.0", + "@mui/icons-material": "^7.3.2", + "@mui/lab": "^7.0.0-beta.17", + "@mui/material": "^7.3.2", + "@reduxjs/toolkit": "^2.9.0", + "@testing-library/jest-dom": "^6.8.0", + "@testing-library/react": "^16.3.0", + "@testing-library/user-event": "^14.6.1", + "axios": "^1.11.0", + "echarts": "^6.0.0", + "echarts-for-react": "^3.0.4", + "globals": "^16.4.0", "lodash": "^4.17.21", "md5": "^2.3.0", "moment": "^2.30.1", - "notistack": "^3.0.1", + "notistack": "^3.0.2", "pluralize": "^8.0.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-draggable": "^4.4.6", - "react-redux": "^9.1.2", - "react-router-dom": "^6.28.0", - "spinners-react": "^1.0.10", - "web-vitals": "^4.2.4" + "react-draggable": "^4.5.0", + "react-redux": "^9.2.0", + "react-router": "^7.8.2", + "spinners-react": "^1.0.11", + "web-vitals": "^5.1.0" }, "scripts": { "start": "vite", @@ -50,14 +50,14 @@ ] }, "devDependencies": { - "@eslint/js": "^9.15.0", - "@vitejs/plugin-react": "^4.3.3", - "eslint": "^9.15.0", - "eslint-plugin-jest": "^28.9.0", - "eslint-plugin-react": "^7.37.2", - "eslint-plugin-react-hooks": "^5.0.0", - "serve": "^14.2.4", - "vite": "^5.4.11", - "vite-plugin-eslint2": "^5.0.2" + "@eslint/js": "^9.35.0", + "@vitejs/plugin-react": "^5.0.2", + "eslint": "^9.35.0", + "eslint-plugin-jest": "^29.0.1", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "serve": "^14.2.5", + "vite": "^7.1.5", + "vite-plugin-eslint2": "^5.0.4" } } diff --git a/ui/src/App.jsx b/ui/src/App.jsx index f549ac48..b0366777 100644 --- a/ui/src/App.jsx +++ b/ui/src/App.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { BrowserRouter as Router} from "react-router-dom"; +import { BrowserRouter as Router} from "react-router"; import { useSelector } from 'react-redux'; import { diff --git a/ui/src/features/DiscoverTable/Table.jsx b/ui/src/features/DiscoverTable/Table.jsx index 916eb282..0aa8c86f 100644 --- a/ui/src/features/DiscoverTable/Table.jsx +++ b/ui/src/features/DiscoverTable/Table.jsx @@ -1,6 +1,6 @@ import * as React from "react"; import { useSelector, useDispatch } from 'react-redux'; -import { useLocation } from "react-router-dom"; +import { useLocation } from "react-router"; import { cloneDeep, pickBy, orderBy, isEmpty, merge } from 'lodash'; diff --git a/ui/src/features/configure/associations/associations.jsx b/ui/src/features/configure/associations/associations.jsx index 14b9c116..20dc1136 100644 --- a/ui/src/features/configure/associations/associations.jsx +++ b/ui/src/features/configure/associations/associations.jsx @@ -1,6 +1,6 @@ import * as React from "react"; import { useSelector, useDispatch } from "react-redux"; -import { useLocation } from "react-router-dom"; +import { useLocation } from "react-router"; import { styled } from "@mui/material/styles"; import { useTheme } from "@mui/material/styles"; diff --git a/ui/src/features/configure/basics/block/block.jsx b/ui/src/features/configure/basics/block/block.jsx index 9bf58206..0afd07d1 100644 --- a/ui/src/features/configure/basics/block/block.jsx +++ b/ui/src/features/configure/basics/block/block.jsx @@ -2,7 +2,7 @@ import * as React from "react"; import { useSelector } from "react-redux"; import { styled } from "@mui/material/styles"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { isEmpty} from "lodash"; diff --git a/ui/src/features/configure/externals/externals.jsx b/ui/src/features/configure/externals/externals.jsx index 7ad3b237..35e9733a 100644 --- a/ui/src/features/configure/externals/externals.jsx +++ b/ui/src/features/configure/externals/externals.jsx @@ -1,6 +1,6 @@ import * as React from "react"; import { useSelector, useDispatch } from "react-redux"; -import { useLocation } from "react-router-dom"; +import { useLocation } from "react-router"; import { styled } from "@mui/material/styles"; import { isEqual, sortBy, pick } from "lodash"; diff --git a/ui/src/features/configure/reservations/reservations.jsx b/ui/src/features/configure/reservations/reservations.jsx index 20088d39..d9021ec6 100644 --- a/ui/src/features/configure/reservations/reservations.jsx +++ b/ui/src/features/configure/reservations/reservations.jsx @@ -1,6 +1,6 @@ import * as React from "react"; import { useSelector, useDispatch } from "react-redux"; -import { useLocation } from "react-router-dom"; +import { useLocation } from "react-router"; import { styled } from "@mui/material/styles"; import { useTheme } from '@mui/material/styles'; diff --git a/ui/src/features/configure/reservations/utils/newReservation.jsx b/ui/src/features/configure/reservations/utils/newReservation.jsx index d3a1d83c..d15adf76 100644 --- a/ui/src/features/configure/reservations/utils/newReservation.jsx +++ b/ui/src/features/configure/reservations/utils/newReservation.jsx @@ -1,6 +1,6 @@ import * as React from "react"; import { useDispatch } from "react-redux"; -import { useLocation } from "react-router-dom"; +import { useLocation } from "react-router"; import { useSnackbar } from "notistack"; diff --git a/ui/src/features/drawer/drawer.jsx b/ui/src/features/drawer/drawer.jsx index f280d2eb..226ef66c 100644 --- a/ui/src/features/drawer/drawer.jsx +++ b/ui/src/features/drawer/drawer.jsx @@ -2,7 +2,7 @@ import * as React from "react"; import { useSelector, useDispatch } from 'react-redux'; import { useMsal } from "@azure/msal-react"; -import { InteractionRequiredAuthError } from "@azure/msal-browser"; +import { InteractionStatus, InteractionRequiredAuthError, BrowserAuthError } from "@azure/msal-browser"; import { useSnackbar } from "notistack"; @@ -12,9 +12,10 @@ import { SvgIcon } from "@mui/material"; import { orderBy } from 'lodash'; import { plural, singular } from 'pluralize'; -import { Routes, Route, Link, Navigate, useNavigate } from "react-router-dom"; +import { Routes, Route, Link, Navigate, useNavigate } from "react-router"; import { callMsGraph, callMsGraphPhoto } from "../../msal/graph"; +import { msalInstance } from "../../index"; import { AppBar, @@ -172,7 +173,7 @@ const Search = styled("div")(({ theme }) => ({ // })); export default function NavDrawer() { - const { instance, accounts } = useMsal(); + const { instance, accounts, inProgress } = useMsal(); const { enqueueSnackbar, closeSnackbar } = useSnackbar(); const [menuAnchorEl, setMenuAnchorEl] = React.useState(null); @@ -421,17 +422,17 @@ export default function NavDrawer() { return word.charAt(0).toUpperCase() + word.slice(1); }) .join(' '); - + return titleCase; } function GetInstanceType(target) { var instanceType = typeof target; - + if(instanceType === 'object') { instanceType = Array.isArray(target) ? 'array': 'object'; } - + return instanceType; } @@ -543,7 +544,7 @@ export default function NavDrawer() { {navItem.map((item, itemIndex) => { - return item.hasOwnProperty('children') + return item.hasOwnProperty('children') ? ((item.admin && isAdmin) || !item.admin) && { try { - const response = await instance.acquireTokenSilent(request); - navigator.clipboard.writeText(response.accessToken); - handleMenuClose(); - enqueueSnackbar('Token copied to clipboard!', { variant: 'success' }); - } catch (e) { - if (e instanceof InteractionRequiredAuthError) { - instance.acquireTokenRedirect(request); - } else { - console.log("ERROR"); - console.log("------------------"); - console.log(e); - console.log("------------------"); - enqueueSnackbar("Error fetching access token", { variant: "error" }); + const accounts = msalInstance.getAllAccounts(); + + if (accounts.length === 0) { + throw new Error("No user accounts found. Please login first."); } + + const tokenRequest = { + ...apiRequest, + account: accounts[0] + }; + + let token; + try { + const response = await msalInstance.acquireTokenSilent(tokenRequest); + token = response.accessToken; + } catch (e) { + if (e instanceof InteractionRequiredAuthError || + (e instanceof BrowserAuthError && e.errorCode === "monitor_window_timeout")) { + + await msalInstance.acquireTokenRedirect(tokenRequest); + return; // Exit since redirect will happen + } else { + throw e; + } + } + + if (token) { + navigator.clipboard.writeText(token); + handleMenuClose(); + enqueueSnackbar('Token copied to clipboard!', { variant: 'success' }); + } + } catch (e) { + console.log("ERROR REQUESTING TOKEN"); + console.log("------------------"); + console.log(e); + console.log("------------------"); + enqueueSnackbar("Error fetching access token", { variant: "error" }); } })(); } @@ -927,7 +952,7 @@ export default function NavDrawer() { anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} variant="dot" > */} - { graphData ? + { graphData ? graphPhoto ? : : diff --git a/ui/src/features/ipam/ipamAPI.jsx b/ui/src/features/ipam/ipamAPI.jsx index 84498d86..d2c42bdc 100644 --- a/ui/src/features/ipam/ipamAPI.jsx +++ b/ui/src/features/ipam/ipamAPI.jsx @@ -1,45 +1,35 @@ import axios from 'axios'; - import { InteractionRequiredAuthError, BrowserAuthError } from "@azure/msal-browser"; -import { - apiRequest -} from '../../msal/authConfig'; - import { msalInstance } from '../../index'; +import { apiRequest } from '../../msal/authConfig'; import { getEngineURL } from '../../global/globals'; const ENGINE_URL = getEngineURL(); async function generateToken() { - // const activeAccount = msalInstance.getActiveAccount(); const accounts = msalInstance.getAllAccounts(); - // if (!activeAccount && accounts.length === 0) { - // } + if (accounts.length === 0) { + throw new Error("No user accounts found. Please login first."); + } - const request = { - scopes: apiRequest.scopes, + const tokenRequest = { + ...apiRequest, account: accounts[0] }; - await msalInstance.handleRedirectPromise(); - try { - const response = await msalInstance.acquireTokenSilent(request); - + const response = await msalInstance.acquireTokenSilent(tokenRequest); return response.accessToken; } catch (e) { - if (e instanceof InteractionRequiredAuthError || e instanceof BrowserAuthError) { - const response = await msalInstance.acquireTokenRedirect(request); - - return response.accessToken; + if (e instanceof InteractionRequiredAuthError || + (e instanceof BrowserAuthError && e.errorCode === "monitor_window_timeout")) { + + await msalInstance.acquireTokenRedirect(tokenRequest); + return null; } else { - console.log("ERROR FETCHING API TOKEN"); - console.log("------------------"); - console.log(e); - console.log("------------------"); - throw(e); + throw e; } } } @@ -51,7 +41,7 @@ api.interceptors.request.use( const token = await generateToken(); config.headers['Authorization'] = `Bearer ${token}`; - + return config; }, error => { diff --git a/ui/src/features/login/login.jsx b/ui/src/features/login/login.jsx index e8abecb6..a6c4edd8 100644 --- a/ui/src/features/login/login.jsx +++ b/ui/src/features/login/login.jsx @@ -7,18 +7,45 @@ import { loginRequest } from "../../msal/authConfig"; const Login = () => { const { instance, inProgress } = useMsal(); const isAuthenticated = useIsAuthenticated(); + const loginAttempted = React.useRef(false); React.useEffect(() => { - if (!isAuthenticated && inProgress === InteractionStatus.None) { - instance.loginRedirect(loginRequest).catch((e) => { - console.log("LOGIN ERROR:"); - console.log("--------------"); - console.error(e); - console.log("--------------"); - }); - } + const handleAuthentication = async () => { + // Only attempt login if: + // 1. User is not authenticated + // 2. No interaction is currently in progress (this is the key check per MSAL docs) + // 3. We haven't already attempted login + if ( + !isAuthenticated && + inProgress === InteractionStatus.None && + !loginAttempted.current + ) { + loginAttempted.current = true; + + try { + await instance.loginRedirect(loginRequest); + } catch (error) { + console.log("LOGIN ERROR:"); + console.log("--------------"); + console.error(error); + console.log("--------------"); + + // Reset the attempt flag on any error to allow retry + loginAttempted.current = false; + } + } + }; + + handleAuthentication(); }, [isAuthenticated, inProgress, instance]); + // Reset login attempt flag when authentication state changes + React.useEffect(() => { + if (isAuthenticated) { + loginAttempted.current = false; + } + }, [isAuthenticated]); + return(null) }; diff --git a/ui/src/features/tabs/adminTabs.jsx b/ui/src/features/tabs/adminTabs.jsx index bf9a16c2..0ecf808b 100644 --- a/ui/src/features/tabs/adminTabs.jsx +++ b/ui/src/features/tabs/adminTabs.jsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Link, useLocation } from "react-router-dom"; +import { Link, useLocation } from "react-router"; import PropTypes from 'prop-types'; import Tabs from '@mui/material/Tabs'; diff --git a/ui/src/features/tabs/analyzeTabs.jsx b/ui/src/features/tabs/analyzeTabs.jsx index 8113d978..32f6ec32 100644 --- a/ui/src/features/tabs/analyzeTabs.jsx +++ b/ui/src/features/tabs/analyzeTabs.jsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Link, useLocation } from "react-router-dom"; +import { Link, useLocation } from "react-router"; import PropTypes from 'prop-types'; import Tabs from '@mui/material/Tabs'; diff --git a/ui/src/features/tabs/configTabs.jsx b/ui/src/features/tabs/configTabs.jsx index 9e4604e9..6e8a3d4f 100644 --- a/ui/src/features/tabs/configTabs.jsx +++ b/ui/src/features/tabs/configTabs.jsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Link, useLocation } from "react-router-dom"; +import { Link, useLocation } from "react-router"; import PropTypes from 'prop-types'; import Tabs from '@mui/material/Tabs'; diff --git a/ui/src/features/tabs/discoverTabs.jsx b/ui/src/features/tabs/discoverTabs.jsx index 74a113fb..8d033831 100644 --- a/ui/src/features/tabs/discoverTabs.jsx +++ b/ui/src/features/tabs/discoverTabs.jsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Link, useLocation } from "react-router-dom"; +import { Link, useLocation } from "react-router"; import PropTypes from 'prop-types'; import Tabs from '@mui/material/Tabs'; diff --git a/ui/src/features/tabs/toolsTabs.jsx b/ui/src/features/tabs/toolsTabs.jsx index f666c4c4..50d812b9 100644 --- a/ui/src/features/tabs/toolsTabs.jsx +++ b/ui/src/features/tabs/toolsTabs.jsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Link, useLocation } from "react-router-dom"; +import { Link, useLocation } from "react-router"; import PropTypes from 'prop-types'; import Tabs from '@mui/material/Tabs'; diff --git a/ui/src/features/tools/generator/generator.jsx b/ui/src/features/tools/generator/generator.jsx index 4fd53598..eb6f178a 100644 --- a/ui/src/features/tools/generator/generator.jsx +++ b/ui/src/features/tools/generator/generator.jsx @@ -1,7 +1,7 @@ import * as React from "react"; import { useSelector } from "react-redux"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router"; import { useSnackbar } from "notistack"; @@ -264,7 +264,7 @@ const Generator = () => { return prefixMask; }); - let availableMasks = cidrMasks.filter((opt) => opt.value >= Math.max(...maskList) && opt.value <= 29); + let availableMasks = cidrMasks.filter((opt) => opt.value >= Math.min(...maskList) && opt.value <= 29); setMaskOptions(availableMasks); setSelectedMask(availableMasks[0]); diff --git a/ui/src/features/tools/planner/planner.jsx b/ui/src/features/tools/planner/planner.jsx index b2b37fc9..3c0d108c 100644 --- a/ui/src/features/tools/planner/planner.jsx +++ b/ui/src/features/tools/planner/planner.jsx @@ -20,7 +20,7 @@ import { CircularProgress } from "@mui/material"; -import Grid from "@mui/material/Grid2"; +import Grid from "@mui/material/Grid"; import { FilterList as FilterListIcon, diff --git a/ui/src/msal/authConfig.jsx b/ui/src/msal/authConfig.jsx index c9dcd1cd..c2a4efde 100644 --- a/ui/src/msal/authConfig.jsx +++ b/ui/src/msal/authConfig.jsx @@ -1,3 +1,5 @@ +// import { LogLevel } from '@azure/msal-browser'; + import { ENGINE_APP_ID, UI_APP_ID, @@ -16,9 +18,27 @@ export const msalConfig = { redirectUri: window.location.origin, }, cache: { - cacheLocation: "sessionStorage", // This configures where your cache will be stored + cacheLocation: "localStorage", // This configures where your cache will be stored storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge }, + system: { + allowRedirectInIframe: false, + preventCorsPreflight: true, + iframeHashTimeout: 10000, // Increase iframe timeout to 10 seconds + loadFrameTimeout: 10000, // Increase frame loading timeout + windowHashTimeout: 60000, // Increase overall timeout for redirect flows + /** + * Below you can configure MSAL.js logs. For more information, visit: + * https://docs.microsoft.com/azure/active-directory/develop/msal-logging-js + */ + // loggerOptions: { + // loggerCallback(logLevel, message) { + // console.log(message); + // }, + // logLevel: LogLevel.Trace, + // piiLoggingEnabled: true // Set to true to enable PII logging. This is not recommended in production. + // } + } }; export const loginRequest = { diff --git a/ui/src/msal/graph.jsx b/ui/src/msal/graph.jsx index 200f9290..b77da228 100644 --- a/ui/src/msal/graph.jsx +++ b/ui/src/msal/graph.jsx @@ -1,40 +1,37 @@ import axios from 'axios'; - import { InteractionRequiredAuthError, BrowserAuthError } from "@azure/msal-browser"; import { msalInstance } from '../index'; import { graphConfig } from "./authConfig"; async function generateToken() { - // const activeAccount = msalInstance.getActiveAccount(); const accounts = msalInstance.getAllAccounts(); - // if (!activeAccount && accounts.length === 0) { - // } + if (accounts.length === 0) { + throw new Error("No user accounts found. Please login or re-authenticate first."); + } const request = { scopes: ["User.Read", "Directory.Read.All"], - account: accounts[0], forceRefresh: true, }; - await msalInstance.handleRedirectPromise(); + const tokenRequest = { + ...request, + account: accounts[0] + }; try { - const response = await msalInstance.acquireTokenSilent(request); - + const response = await msalInstance.acquireTokenSilent(tokenRequest); return response.accessToken; } catch (e) { - if (e instanceof InteractionRequiredAuthError || e instanceof BrowserAuthError) { - const response = await msalInstance.acquireTokenRedirect(request); - - return response.accessToken; + if (e instanceof InteractionRequiredAuthError || + (e instanceof BrowserAuthError && e.errorCode === "monitor_window_timeout")) { + + await msalInstance.acquireTokenRedirect(tokenRequest); + return null; } else { - console.log("ERROR FETCHING GRAPH TOKEN"); - console.log("------------------"); - console.log(e); - console.log("------------------"); - throw(e); + throw e; } } } @@ -46,7 +43,7 @@ graph.interceptors.request.use( const token = await generateToken(); config.headers['Authorization'] = `Bearer ${token}`; - + return config; }, error => { @@ -59,9 +56,23 @@ export function callMsGraph() { return graph .get(url) .then(response => response.data) - .catch(error => { + .catch(async error => { console.log("ERROR CALLING MSGRAPH"); console.log(error); + + // If we get a 401, the token might be invalid - try to get a fresh token + if (error.response?.status === 401) { + console.log("401 error - attempting to refresh Graph token"); + try { + // Force a fresh token acquisition for Graph API + await generateToken(); + // The generateToken function will trigger a redirect if needed + } catch (tokenError) { + console.log("Token refresh failed:", tokenError); + throw tokenError; + } + } + throw error; }); } diff --git a/ui/vite.config.js b/ui/vite.config.js index 17046bd9..cef7cf8a 100644 --- a/ui/vite.config.js +++ b/ui/vite.config.js @@ -1,53 +1,53 @@ -import { defineConfig, splitVendorChunkPlugin } from "vite"; +import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; -import eslint from 'vite-plugin-eslint2'; +import eslint from "vite-plugin-eslint2"; export default () => { return defineConfig({ + // resolve: { + // dedupe: ['react', 'react-dom'] + // }, plugins: [ react(), - eslint( - { - // cache: false, - lintOnStart: true, - lintInWorker: true, - include: ['src/**/*.js', 'src/**/*.jsx', 'src/**/*.ts', 'src/**/*.tsx'], - exclude: [] - } - ), - splitVendorChunkPlugin(), + eslint({ + // cache: false, + lintOnStart: true, + lintInWorker: true, + include: ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"], + exclude: [], + }), { - name: 'build-ui-html', - apply: 'build', + name: "build-ui-html", + apply: "build", transformIndexHtml: (html) => { return { html, tags: [ { - tag: 'script', + tag: "script", attrs: { - type: 'text/javascript', - src: '/env.js', + type: "text/javascript", + src: "/env.js", }, - injectTo: 'head', + injectTo: "head", }, ], }; }, - } + }, ], server: { hmr: { protocol: "ws", - path: "/ws" - } + path: "/ws", + }, }, define: { IPAM_VERSION: JSON.stringify(process.env.npm_package_version), }, build: { - chunkSizeWarningLimit: 5120 + chunkSizeWarningLimit: 5120, }, - logLevel: 'warn' - }) -} + logLevel: "warn", + }); +};