diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a42156bfc..7a32def93 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -35,8 +35,8 @@ updates: # npm dependencies - grouped - package-ecosystem: "npm" directories: - - "/src/app/frontend" - - "/src/app/frontend-server" + - "/src/App" + - "/src/App/server" schedule: interval: "monthly" target-branch: "dependabotchanges" diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml index a60e8c405..f76a66844 100644 --- a/.github/workflows/azure-dev.yml +++ b/.github/workflows/azure-dev.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout Code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set timestamp and env name shell: bash diff --git a/.github/workflows/broken-links-checker.yml b/.github/workflows/broken-links-checker.yml index 3b19db1df..bc9fc498b 100644 --- a/.github/workflows/broken-links-checker.yml +++ b/.github/workflows/broken-links-checker.yml @@ -25,7 +25,7 @@ jobs: - name: Get changed markdown files (PR only) id: changed-markdown-files if: github.event_name == 'pull_request' - uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v46 + uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v46 with: files: | **/*.md @@ -35,7 +35,7 @@ jobs: - name: Check Broken Links in Changed Markdown Files id: lychee-check-pr if: github.event_name == 'pull_request' && steps.changed-markdown-files.outputs.any_changed == 'true' - uses: lycheeverse/lychee-action@v2.7.0 + uses: lycheeverse/lychee-action@v2.8.0 with: args: > --verbose --no-progress --exclude ^https?:// @@ -48,7 +48,7 @@ jobs: - name: Check Broken Links in All Markdown Files in Entire Repo (Manual Trigger) id: lychee-check-manual if: github.event_name == 'workflow_dispatch' - uses: lycheeverse/lychee-action@v2.7.0 + uses: lycheeverse/lychee-action@v2.8.0 with: args: > --verbose --no-progress --exclude ^https?:// diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 1ca61df0a..d8cde5c7b 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -15,11 +15,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: ref: ${{ github.sha }} - - uses: codfish/semantic-release-action@v3 + - uses: codfish/semantic-release-action@v5 id: semantic with: tag-format: 'v${version}' diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 56c978126..372faa494 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -8,8 +8,7 @@ on: - demo paths: - 'src/backend/**' - - 'src/app/frontend/**' - - 'src/app/frontend-server/**' + - 'src/App/**' - '.github/workflows/docker-build.yml' pull_request: types: @@ -23,8 +22,7 @@ on: - demo paths: - 'src/backend/**' - - 'src/app/frontend/**' - - 'src/app/frontend-server/**' + - 'src/App/**' - '.github/workflows/docker-build.yml' workflow_dispatch: @@ -44,10 +42,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to Azure (OIDC) if: ${{ (github.event_name == 'push' && (github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo')) || (github.event_name == 'workflow_dispatch' && (github.ref_name == 'dependabotchanges'||github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo')) }} @@ -86,17 +84,17 @@ jobs: fi - name: Build and Push Docker Image for Frontend Server - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: - context: ./src/app - file: ./src/app/WebApp.Dockerfile + context: ./src/App + file: ./src/App/WebApp.Dockerfile push: ${{ github.ref_name == 'main' || github.ref_name == 'dev' || github.ref_name == 'demo' || github.ref_name == 'dependabotchanges' }} tags: | ${{ secrets.ACR_LOGIN_SERVER || 'acrlogin.azurecr.io' }}/content-gen-app:${{ steps.determine_tag.outputs.tagname }} ${{ secrets.ACR_LOGIN_SERVER || 'acrlogin.azurecr.io' }}/content-gen-app:${{ steps.determine_tag.outputs.tagname }}_${{ steps.date.outputs.date }}_${{ github.run_number }} - name: Build and Push Docker Image for Backend Server - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: ./src/backend file: ./src/backend/ApiApp.Dockerfile diff --git a/.github/workflows/job-docker-build.yml b/.github/workflows/job-docker-build.yml index 48266fab7..b154c79fc 100644 --- a/.github/workflows/job-docker-build.yml +++ b/.github/workflows/job-docker-build.yml @@ -46,7 +46,7 @@ jobs: echo "Generated unique Docker tag: $UNIQUE_TAG" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to Azure (OIDC) uses: azure/login@v2 @@ -65,8 +65,8 @@ jobs: env: DOCKER_BUILD_SUMMARY: false with: - context: ./src/app - file: ./src/app/WebApp.Dockerfile + context: ./src/App + file: ./src/App/WebApp.Dockerfile push: true tags: | ${{ secrets.ACR_TEST_LOGIN_SERVER }}/content-gen-app:${{ steps.generate_docker_tag.outputs.AZURE_ENV_IMAGE_TAG }} @@ -74,7 +74,7 @@ jobs: - name: Build and Push Docker Image for Backend Server id: build_push_backend - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 env: DOCKER_BUILD_SUMMARY: false with: diff --git a/.github/workflows/pr-title-checker.yml b/.github/workflows/pr-title-checker.yml index debfc53f4..9a3090fc8 100644 --- a/.github/workflows/pr-title-checker.yml +++ b/.github/workflows/pr-title-checker.yml @@ -17,6 +17,6 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event_name != 'merge_group' }} steps: - - uses: amannn/action-semantic-pull-request@v5 + - uses: amannn/action-semantic-pull-request@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/scheduled-Dependabot-PRs-Auto-Merge.yml b/.github/workflows/scheduled-Dependabot-PRs-Auto-Merge.yml index 0653e3d70..e29507533 100644 --- a/.github/workflows/scheduled-Dependabot-PRs-Auto-Merge.yml +++ b/.github/workflows/scheduled-Dependabot-PRs-Auto-Merge.yml @@ -36,7 +36,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install GitHub CLI run: | diff --git a/.github/workflows/stale-bot.yml b/.github/workflows/stale-bot.yml index 99bbca6d8..739bca437 100644 --- a/.github/workflows/stale-bot.yml +++ b/.github/workflows/stale-bot.yml @@ -67,7 +67,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload CSV Report of Inactive Branches - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: merged-branches-report path: merged_branches_report.csv diff --git a/.github/workflows/test-automation-v2.yml b/.github/workflows/test-automation-v2.yml index 0aabfd40f..76c75ad25 100644 --- a/.github/workflows/test-automation-v2.yml +++ b/.github/workflows/test-automation-v2.yml @@ -136,7 +136,7 @@ jobs: - name: Upload test report id: upload_report - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 if: always() with: name: test-report diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 82573ca99..d518eda3c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,7 @@ on: permissions: contents: read actions: read + pull-requests: write concurrency: group: test-${{ github.ref }} @@ -69,7 +70,19 @@ jobs: - name: Run Backend Tests with Coverage if: env.skip_backend_tests == 'false' run: | - pytest --cov=. --cov-report=term-missing --cov-report=xml ./src/tests + pytest --cov=. --cov-report=term-missing --cov-report=xml --junitxml=pytest.xml ./src/tests + + - name: Pytest Coverage Comment + if: | + always() && + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.fork == false && + env.skip_backend_tests == 'false' + uses: MishaKav/pytest-coverage-comment@26f986d2599c288bb62f623d29c2da98609e9cd4 # v1.6.0 + with: + pytest-xml-coverage-path: coverage.xml + junitxml-path: pytest.xml + report-only-changed-files: true - name: Skip Backend Tests if: env.skip_backend_tests == 'true' diff --git a/.gitignore b/.gitignore index 3696c9d2f..310b95883 100644 --- a/.gitignore +++ b/.gitignore @@ -36,15 +36,15 @@ eggs/ *.swo # Node -/src/app/frontend/node_modules/ -/src/app/frontend-server/node_modules/ -/src/app/frontend-server/static/ -/src/app/frontend-server/*.zip +/src/App/node_modules/ +/src/App/server/node_modules/ +/src/App/server/static/ +/src/App/server/*.zip node_modules/ # Build output -/src/app/static/ -/src/app/frontend/dist/ +/src/App/static/ +/src/App/dist/ # Logs *.log diff --git a/azure.yaml b/azure.yaml index e0fda8a98..23f3ced27 100644 --- a/azure.yaml +++ b/azure.yaml @@ -8,7 +8,6 @@ metadata: requiredVersions: azd: '>= 1.18.0 != 1.23.9' - bicep: '>= 0.33.0' parameters: solutionPrefix: diff --git a/azure_custom.yaml b/azure_custom.yaml index 266e53c60..8ba99fac6 100644 --- a/azure_custom.yaml +++ b/azure_custom.yaml @@ -11,7 +11,7 @@ infra: services: frontend: - project: ./src/app/frontend-server + project: ./src/App/server language: js host: appservice dist: ./dist diff --git a/docs/AZD_DEPLOYMENT.md b/docs/AZD_DEPLOYMENT.md index 7dac3486f..6582e4d48 100644 --- a/docs/AZD_DEPLOYMENT.md +++ b/docs/AZD_DEPLOYMENT.md @@ -239,7 +239,7 @@ Error: az acr build failed **Solution**: Check the Dockerfile and ensure all required files are present: ```bash # Manual build for debugging -cd src/app +cd src/App docker build -f WebApp.Dockerfile -t content-gen-app:test . ``` @@ -251,7 +251,7 @@ Error: az webapp deploy failed **Solution**: Ensure the frontend builds successfully: ```bash -cd src/app/frontend +cd src/App npm install npm run build ``` diff --git a/docs/TECHNICAL_GUIDE.md b/docs/TECHNICAL_GUIDE.md index 69a28172b..fcedf6f38 100644 --- a/docs/TECHNICAL_GUIDE.md +++ b/docs/TECHNICAL_GUIDE.md @@ -142,7 +142,7 @@ pip install -r requirements.txt python app.py # Frontend -cd src/app/frontend +cd src/App npm install npm run dev ``` diff --git a/infra/main.bicep b/infra/main.bicep index de5c3be3c..c07f548d9 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -285,7 +285,7 @@ var existingTags = existingResourceGroup.tags ?? {} // ============== // #disable-next-line no-deployments-resources -resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { +resource avmTelemetry 'Microsoft.Resources/deployments@2025-04-01' = if (enableTelemetry) { name: '46d3xbcp.ptn.sa-contentgeneration.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, solutionLocation), 0, 4)}' properties: { mode: 'Incremental' @@ -507,7 +507,7 @@ var dnsZoneIndex = { } @batchSize(5) -module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.8.0' = [ +module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.8.1' = [ for (zone, i) in privateDnsZones: if (enablePrivateNetworking) { name: take('avm.res.network.private-dns-zone.${replace(zone, '.', '-')}', 64) params: { @@ -525,7 +525,7 @@ module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.8.0' = [ ] // ========== AI Foundry: AI Services ========== // -module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.14.1' = if (!useExistingAiFoundryAiProject) { +module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.14.2' = if (!useExistingAiFoundryAiProject) { name: take('avm.res.cognitive-services.account.${aiFoundryAiServicesResourceName}', 64) params: { name: aiFoundryAiServicesResourceName @@ -589,7 +589,7 @@ module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.14.1' } // Create private endpoint for AI Services AFTER the account is fully provisioned -module aiServicesPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.11.1' = if (!useExistingAiFoundryAiProject && enablePrivateNetworking) { +module aiServicesPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.12.0' = if (!useExistingAiFoundryAiProject && enablePrivateNetworking) { name: take('pep-ai-services-${aiFoundryAiServicesResourceName}', 64) params: { name: 'pep-${aiFoundryAiServicesResourceName}' @@ -714,7 +714,7 @@ module aiSearch 'br/public:avm/res/search/search-service:0.12.0' = { } // ========== AI Search Connection to AI Services ========== // -resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-09-01' = if (!useExistingAiFoundryAiProject) { +resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-12-01' = if (!useExistingAiFoundryAiProject) { name: '${aiFoundryAiServicesResourceName}/${aiFoundryAiProjectResourceName}/${aiSearchConnectionName}' properties: { category: 'CognitiveSearch' @@ -735,7 +735,7 @@ var productImagesContainer = 'product-images' var generatedImagesContainer = 'generated-images' var dataContainer = 'data' -module storageAccount 'br/public:avm/res/storage/storage-account:0.31.1' = { +module storageAccount 'br/public:avm/res/storage/storage-account:0.32.0' = { name: take('avm.res.storage.storage-account.${storageAccountName}', 64) params: { name: storageAccountName @@ -803,7 +803,7 @@ var cosmosDBDatabaseName = 'content_generation_db' var cosmosDBConversationsContainer = 'conversations' var cosmosDBProductsContainer = 'products' -module cosmosDB 'br/public:avm/res/document-db/database-account:0.18.0' = { +module cosmosDB 'br/public:avm/res/document-db/database-account:0.19.0' = { name: take('avm.res.document-db.database-account.${cosmosDBResourceName}', 64) params: { name: 'cosmos-${solutionSuffix}' diff --git a/infra/main.json b/infra/main.json index b88c84b66..2b5f02619 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.42.1.51946", - "templateHash": "15695041727004400845" + "templateHash": "12441959736083561218" }, "name": "Intelligent Content Generation Accelerator", "description": "Solution Accelerator for multimodal marketing content generation using Microsoft Agent Framework.\n" @@ -399,7 +399,7 @@ "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", + "apiVersion": "2025-04-01", "name": "[format('46d3xbcp.ptn.sa-contentgeneration.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, variables('solutionLocation')), 0, 4))]", "properties": { "mode": "Incremental", @@ -430,7 +430,7 @@ "aiSearchFoundryConnection": { "condition": "[not(variables('useExistingAiFoundryAiProject'))]", "type": "Microsoft.CognitiveServices/accounts/projects/connections", - "apiVersion": "2025-09-01", + "apiVersion": "2025-12-01", "name": "[format('{0}/{1}/{2}', variables('aiFoundryAiServicesResourceName'), variables('aiFoundryAiProjectResourceName'), variables('aiSearchConnectionName'))]", "properties": { "category": "CognitiveSearch", @@ -4880,7 +4880,7 @@ "_generator": { "name": "bicep", "version": "0.42.1.51946", - "templateHash": "3628160320006039137" + "templateHash": "5942055518457081505" } }, "parameters": { @@ -5088,8 +5088,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.5.1644", - "templateHash": "11959948740766233645" + "version": "0.41.2.15936", + "templateHash": "7311263652424030280" }, "name": "Network Security Groups", "description": "This module deploys a Network security Group (NSG)." @@ -5348,7 +5348,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if only logs are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -5385,7 +5385,7 @@ "metadata": { "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.0" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -5460,7 +5460,7 @@ "metadata": { "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } } @@ -5527,7 +5527,7 @@ "type": "object", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/networkSecurityGroups@2024-07-01#properties/tags" + "source": "Microsoft.Network/networkSecurityGroups@2025-05-01#properties/tags" }, "description": "Optional. Tags of the NSG resource." }, @@ -5563,7 +5563,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.5.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-networksecuritygroup.{0}.{1}', replace('0.5.3', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -5581,7 +5581,7 @@ }, "networkSecurityGroup": { "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "2023-11-01", + "apiVersion": "2025-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -5619,7 +5619,7 @@ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", "apiVersion": "2020-05-01", - "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", @@ -5636,7 +5636,7 @@ }, "type": "Microsoft.Insights/diagnosticSettings", "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { "copy": [ @@ -5668,7 +5668,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/networkSecurityGroups', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -5711,7 +5711,7 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('networkSecurityGroup', '2023-11-01', 'full').location]" + "value": "[reference('networkSecurityGroup', '2025-05-01', 'full').location]" } } } @@ -5760,8 +5760,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "15393147213974991892" + "version": "0.41.2.15936", + "templateHash": "11272001757446231867" }, "name": "Virtual Networks", "description": "This module deploys a Virtual Network (vNet)." @@ -6427,6 +6427,17 @@ "metadata": { "description": "Optional. Indicates if VM protection is enabled for all the subnets in the virtual network." } + }, + "enablePrivateEndpointVNetPolicies": { + "type": "string", + "defaultValue": "Disabled", + "allowedValues": [ + "Basic", + "Disabled" + ], + "metadata": { + "description": "Optional. Enables high scale private endpoints for the virtual network. This is necessary if the virtual network requires more than 1000 private endpoints or is peered to virtual networks with a total of more than 4000 private endpoints." + } } }, "variables": { @@ -6452,7 +6463,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.7.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-virtualnetwork.{0}.{1}', replace('0.8.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -6482,14 +6493,15 @@ "enableDdosProtection": "[not(empty(parameters('ddosProtectionPlanResourceId')))]", "encryption": "[if(equals(parameters('vnetEncryption'), true()), createObject('enabled', parameters('vnetEncryption'), 'enforcement', parameters('vnetEncryptionEnforcement')), null())]", "flowTimeoutInMinutes": "[if(not(equals(parameters('flowTimeoutInMinutes'), 0)), parameters('flowTimeoutInMinutes'), null())]", - "enableVmProtection": "[parameters('enableVmProtection')]" + "enableVmProtection": "[parameters('enableVmProtection')]", + "privateEndpointVNetPolicies": "[parameters('enablePrivateEndpointVNetPolicies')]" } }, "virtualNetwork_lock": { "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", "apiVersion": "2020-05-01", - "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", @@ -6506,7 +6518,7 @@ }, "type": "Microsoft.Insights/diagnosticSettings", "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { "copy": [ @@ -6547,7 +6559,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -6640,8 +6652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "11087937906182478872" + "version": "0.41.2.15936", + "templateHash": "13992200806189615656" }, "name": "Virtual Network Subnets", "description": "This module deploys a Virtual Network Subnet." @@ -6955,7 +6967,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/virtualNetworks/{0}/subnets/{1}', parameters('virtualNetworkName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetworkName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -7066,8 +7078,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "4173704498931603856" + "version": "0.41.2.15936", + "templateHash": "6939030350004475953" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering." @@ -7223,8 +7235,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "4173704498931603856" + "version": "0.41.2.15936", + "templateHash": "6939030350004475953" }, "name": "Virtual Network Peerings", "description": "This module deploys a Virtual Network Peering." @@ -18321,8 +18333,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "17921343070314002065" + "version": "0.41.2.15936", + "templateHash": "18337341331267624582" }, "name": "Private DNS Zones", "description": "This module deploys a Private DNS zone." @@ -19075,7 +19087,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.8.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privatednszone.{0}.{1}', replace('0.8.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -19102,7 +19114,7 @@ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", "apiVersion": "2020-05-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", @@ -19119,7 +19131,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -19140,7 +19152,7 @@ "count": "[length(coalesce(parameters('a'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-ARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -19177,8 +19189,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "12608084563401365743" + "version": "0.41.2.15936", + "templateHash": "7372385900111002873" }, "name": "Private DNS Zone A record", "description": "This module deploys a Private DNS Zone A record." @@ -19341,7 +19353,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.nw-privdnszonea.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.nw-privdnszonea.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -19380,7 +19392,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/A/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/A', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -19431,7 +19443,7 @@ "count": "[length(coalesce(parameters('aaaa'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-AAAARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -19468,8 +19480,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "4881696097088567452" + "version": "0.41.2.15936", + "templateHash": "14405855828972373002" }, "name": "Private DNS Zone AAAA record", "description": "This module deploys a Private DNS Zone AAAA record." @@ -19632,7 +19644,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.nw-privdnszoneaaaa.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.nw-privdnszoneaaaa.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -19671,7 +19683,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/AAAA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/AAAA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -19722,7 +19734,7 @@ "count": "[length(coalesce(parameters('cname'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-CNAMERecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -19759,8 +19771,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "13307906270868460967" + "version": "0.41.2.15936", + "templateHash": "13818627461335065928" }, "name": "Private DNS Zone CNAME record", "description": "This module deploys a Private DNS Zone CNAME record." @@ -19923,7 +19935,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.nw-privdnszonecname.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.nw-privdnszonecname.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -19962,7 +19974,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/CNAME/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/CNAME', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -20013,7 +20025,7 @@ "count": "[length(coalesce(parameters('mx'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-MXRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -20050,8 +20062,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "7946896598573056688" + "version": "0.41.2.15936", + "templateHash": "2977624679479439942" }, "name": "Private DNS Zone MX record", "description": "This module deploys a Private DNS Zone MX record." @@ -20214,7 +20226,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.nw-privdnszonemx.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.nw-privdnszonemx.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -20253,7 +20265,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/MX/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/MX', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -20304,7 +20316,7 @@ "count": "[length(coalesce(parameters('ptr'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-PTRRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -20341,8 +20353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "7627375510490151870" + "version": "0.41.2.15936", + "templateHash": "15286275176817336979" }, "name": "Private DNS Zone PTR record", "description": "This module deploys a Private DNS Zone PTR record." @@ -20505,7 +20517,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.nw-privdnszoneptr.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.nw-privdnszoneptr.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -20544,7 +20556,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/PTR/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/PTR', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -20595,7 +20607,7 @@ "count": "[length(coalesce(parameters('soa'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-SOARecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -20632,8 +20644,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "16709883266329935583" + "version": "0.41.2.15936", + "templateHash": "4837447718856535826" }, "name": "Private DNS Zone SOA record", "description": "This module deploys a Private DNS Zone SOA record." @@ -20796,7 +20808,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.nw-privdnszonesoa.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.nw-privdnszonesoa.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -20835,7 +20847,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SOA/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SOA', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -20886,7 +20898,7 @@ "count": "[length(coalesce(parameters('srv'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-SRVRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -20923,8 +20935,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "8123422724272920495" + "version": "0.41.2.15936", + "templateHash": "15028912535488490265" }, "name": "Private DNS Zone SRV record", "description": "This module deploys a Private DNS Zone SRV record." @@ -21087,7 +21099,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.nw-privdnszonesrv.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.nw-privdnszonesrv.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -21126,7 +21138,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/SRV/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/SRV', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -21177,7 +21189,7 @@ "count": "[length(coalesce(parameters('txt'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-TXTRecord-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -21214,8 +21226,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "17170531000135004092" + "version": "0.41.2.15936", + "templateHash": "7641583415110009849" }, "name": "Private DNS Zone TXT record", "description": "This module deploys a Private DNS Zone TXT record." @@ -21378,7 +21390,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.nw-privdnszonetxt.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.nw-privdnszonetxt.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -21417,7 +21429,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateDnsZones/{0}/TXT/{1}', parameters('privateDnsZoneName'), parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateDnsZones/TXT', parameters('privateDnsZoneName'), parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -21468,7 +21480,7 @@ "count": "[length(coalesce(parameters('virtualNetworkLinks'), createArray()))]" }, "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", + "apiVersion": "2025-04-01", "name": "[format('{0}-PrivateDnsZone-VNetLink-{1}', uniqueString(deployment().name, parameters('location')), copyIndex())]", "properties": { "expressionEvaluationOptions": { @@ -21496,6 +21508,9 @@ }, "resolutionPolicy": { "value": "[tryGet(coalesce(parameters('virtualNetworkLinks'), createArray())[copyIndex()], 'resolutionPolicy')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" } }, "template": { @@ -21505,13 +21520,20 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.37.4.10188", - "templateHash": "517173107480898390" + "version": "0.41.2.15936", + "templateHash": "7392770862892927923" }, "name": "Private DNS Zone Virtual Network Link", "description": "This module deploys a Private DNS Zone Virtual Network Link." }, "parameters": { + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } + }, "privateDnsZoneName": { "type": "string", "metadata": { @@ -21564,6 +21586,26 @@ } }, "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.nw-privdnszonevnetlink.{0}.{1}', replace('0.1.0', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, "privateDnsZone": { "existing": true, "type": "Microsoft.Network/privateDnsZones", @@ -21755,8 +21797,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "6544538318162038728" + "version": "0.41.2.15936", + "templateHash": "8642151282041103672" }, "name": "Cognitive Services", "description": "This module deploys a Cognitive Service." @@ -21853,8 +21895,9 @@ }, "version": { "type": "string", + "nullable": true, "metadata": { - "description": "Required. The version of Cognitive Services account deployment model." + "description": "Conditional. The version of Cognitive Services account deployment model. Required if the model does not have a default version." } } }, @@ -23118,7 +23161,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.14.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.cognitiveservices-account.{0}.{1}', replace('0.14.2', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -23209,7 +23252,7 @@ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", "apiVersion": "2020-05-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", @@ -23239,7 +23282,7 @@ }, "type": "Microsoft.Insights/diagnosticSettings", "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { "copy": [ @@ -23280,7 +23323,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.CognitiveServices/accounts/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.CognitiveServices/accounts', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.CognitiveServices/accounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -23982,8 +24025,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.39.26.7824", - "templateHash": "356315690886888607" + "version": "0.41.2.15936", + "templateHash": "13968722110082077308" } }, "definitions": { @@ -24265,8 +24308,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.5.1644", - "templateHash": "16604612898799598358" + "version": "0.41.2.15936", + "templateHash": "18436885663402767850" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -24330,7 +24373,7 @@ "metadata": { "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -24429,7 +24472,7 @@ "metadata": { "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } } @@ -24468,12 +24511,22 @@ "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/ipConfigurations" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/ipConfigurations" }, "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." }, "nullable": true }, + "ipVersionType": { + "type": "string", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/ipVersionType" + }, + "description": "Optional. Specifies the IP version type for the private IPs of the private endpoint. If not defined, this defaults to IPv4." + }, + "defaultValue": "IPv4" + }, "privateDnsZoneGroup": { "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, @@ -24509,7 +24562,7 @@ "type": "object", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/tags" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/tags" }, "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." }, @@ -24519,7 +24572,7 @@ "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/customDnsConfigs" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/customDnsConfigs" }, "description": "Optional. Custom DNS configurations." }, @@ -24529,7 +24582,7 @@ "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/manualPrivateLinkServiceConnections" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/manualPrivateLinkServiceConnections" }, "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." }, @@ -24539,7 +24592,7 @@ "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/privateLinkServiceConnections" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/privateLinkServiceConnections" }, "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." }, @@ -24579,7 +24632,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.12.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -24597,7 +24650,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -24618,14 +24671,15 @@ "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", "subnet": { "id": "[parameters('subnetResourceId')]" - } + }, + "ipVersionType": "[parameters('ipVersionType')]" } }, "privateEndpoint_lock": { "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", "apiVersion": "2020-05-01", - "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", @@ -24642,7 +24696,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -24685,8 +24739,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.5.1644", - "templateHash": "24141742673128945" + "version": "0.41.2.15936", + "templateHash": "9935179114830442414" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -24745,12 +24799,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "copy": [ @@ -24825,13 +24879,13 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2024-10-01', 'full').location]" + "value": "[reference('privateEndpoint', '2025-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/customDnsConfigs", + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/customDnsConfigs", "output": true }, "description": "The custom DNS configurations of the private endpoint." @@ -24903,7 +24957,7 @@ "_generator": { "name": "bicep", "version": "0.42.1.51946", - "templateHash": "831092851343254936" + "templateHash": "16893794525082731825" } }, "parameters": { @@ -24955,7 +25009,7 @@ "resources": [ { "type": "Microsoft.CognitiveServices/accounts/projects", - "apiVersion": "2025-10-01-preview", + "apiVersion": "2025-12-01", "name": "[format('{0}/{1}', parameters('aiServicesName'), parameters('name'))]", "tags": "[parameters('tags')]", "location": "[parameters('location')]", @@ -24988,21 +25042,21 @@ "metadata": { "description": "Required. API endpoint for the AI project." }, - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('name')), '2025-10-01-preview').endpoints['AI Foundry API']]" + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('name')), '2025-12-01').endpoints['AI Foundry API']]" }, "aoaiEndpoint": { "type": "string", "metadata": { "description": "Contains AI Endpoint." }, - "value": "[if(not(empty(variables('existingOpenAIEndpoint'))), variables('existingOpenAIEndpoint'), reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')), '2025-10-01-preview').endpoints['OpenAI Language Model Instance API'])]" + "value": "[if(not(empty(variables('existingOpenAIEndpoint'))), variables('existingOpenAIEndpoint'), reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')), '2025-12-01').endpoints['OpenAI Language Model Instance API'])]" }, "systemAssignedMIPrincipalId": { "type": "string", "metadata": { "description": "Required. Principal ID of the AI project system-assigned managed identity." }, - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('name')), '2025-10-01-preview', 'full').identity.principalId]" + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('name')), '2025-12-01', 'full').identity.principalId]" } } } @@ -25041,7 +25095,7 @@ "_generator": { "name": "bicep", "version": "0.42.1.51946", - "templateHash": "1725382017005764747" + "templateHash": "12916889556462137224" } }, "parameters": { @@ -25116,14 +25170,14 @@ "metadata": { "description": "The endpoint of the existing AI Services account." }, - "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')), '2025-10-01-preview').endpoint]" + "value": "[reference(resourceId('Microsoft.CognitiveServices/accounts', parameters('aiServicesName')), '2025-12-01').endpoint]" }, "aiProjectPrincipalId": { "type": "string", "metadata": { "description": "The principal ID of the existing AI Project (if provided)." }, - "value": "[if(not(empty(parameters('aiProjectName'))), coalesce(tryGet(tryGet(if(not(empty(parameters('aiProjectName'))), reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('aiProjectName')), '2025-10-01-preview', 'full'), null()), 'identity'), 'principalId'), ''), '')]" + "value": "[if(not(empty(parameters('aiProjectName'))), coalesce(tryGet(tryGet(if(not(empty(parameters('aiProjectName'))), reference(resourceId('Microsoft.CognitiveServices/accounts/projects', parameters('aiServicesName'), parameters('aiProjectName')), '2025-12-01', 'full'), null()), 'identity'), 'principalId'), ''), '')]" } } } @@ -25165,7 +25219,7 @@ "_generator": { "name": "bicep", "version": "0.42.1.51946", - "templateHash": "8900687660670713557" + "templateHash": "13520241640147571543" } }, "parameters": { @@ -25192,7 +25246,7 @@ "batchSize": 1 }, "type": "Microsoft.CognitiveServices/accounts/deployments", - "apiVersion": "2024-10-01", + "apiVersion": "2025-12-01", "name": "[format('{0}/{1}', parameters('aiServicesName'), parameters('deployments')[copyIndex()].name)]", "properties": { "model": { @@ -27413,8 +27467,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "9428266357530474061" + "version": "0.41.2.15936", + "templateHash": "1254456195180100771" }, "name": "Storage Accounts", "description": "This module deploys a Storage Account." @@ -29544,7 +29598,8 @@ "type": "string", "defaultValue": "TLS1_2", "allowedValues": [ - "TLS1_2" + "TLS1_2", + "TLS1_3" ], "metadata": { "description": "Optional. Set the minimum TLS version on request to storage. The TLS versions 1.0 and 1.1 are deprecated and not supported anymore." @@ -29773,7 +29828,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.31.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.storage-storageaccount.{0}.{1}', replace('0.32.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -30569,6 +30624,9 @@ }, "rules": { "value": "[variables('formattedManagementPolicies')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" } }, "template": { @@ -30577,8 +30635,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "10458708345324133836" + "version": "0.41.2.15936", + "templateHash": "4000605059554016072" }, "name": "Storage Account Management Policies", "description": "This module deploys a Storage Account Management Policy." @@ -30599,9 +30657,36 @@ }, "description": "Required. The Storage Account ManagementPolicies Rules." } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-mgmtpolicy.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, { "type": "Microsoft.Storage/storageAccounts/managementPolicies", "apiVersion": "2025-06-01", @@ -30680,6 +30765,9 @@ }, "sshAuthorizedKeys": { "value": "[tryGet(coalesce(parameters('localUsers'), createArray())[copyIndex()], 'sshAuthorizedKeys')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" } }, "template": { @@ -30689,8 +30777,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "18432545368646748336" + "version": "0.41.2.15936", + "templateHash": "1801226901235196767" }, "name": "Storage Account Local Users", "description": "This module deploys a Storage Account Local User, which is used for SFTP authentication." @@ -30802,9 +30890,36 @@ "metadata": { "description": "Optional. The local user SSH authorized keys for SFTP." } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, "resources": { + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-localuser.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, "storageAccount": { "existing": true, "type": "Microsoft.Storage/storageAccounts", @@ -30927,8 +31042,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "10857560398903249320" + "version": "0.41.2.15936", + "templateHash": "468225492069709453" }, "name": "Storage Account blob Services", "description": "This module deploys a Storage Account Blob Service." @@ -31597,8 +31712,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "11027676755461897101" + "version": "0.41.2.15936", + "templateHash": "273904034769611992" }, "name": "Storage Account Blob Containers", "description": "This module deploys a Storage Account Blob Container." @@ -31835,7 +31950,8 @@ "Storage Blob Data Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '2a2b9908-6ea1-4ae2-8e65-a410df84e7d1')]", "Storage Blob Delegator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'db58b8e5-c6ad-4a2a-8342-4190687cbf4a')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + }, + "enableReferencedModulesTelemetry": false }, "resources": { "storageAccount::blobServices": { @@ -31848,7 +31964,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.storage-blobcontainer.{0}.{1}', replace('0.3.3', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.storage-blobcontainer.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -31931,6 +32047,9 @@ }, "allowProtectedAppendWritesAll": { "value": "[tryGet(parameters('immutabilityPolicy'), 'allowProtectedAppendWritesAll')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" } }, "template": { @@ -31939,8 +32058,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7095313291458772891" + "version": "0.41.2.15936", + "templateHash": "15304742179563677019" }, "name": "Storage Account Blob Container Immutability Policies", "description": "This module deploys a Storage Account Blob Container Immutability Policy." @@ -31979,9 +32098,36 @@ "metadata": { "description": "Optional. This property can only be changed for unlocked time-based retention policies. When enabled, new blocks can be written to both \"Append and Block Blobs\" while maintaining immutability protection and compliance. Only new blocks can be added and any existing blocks cannot be modified or deleted. This property cannot be changed with ExtendImmutabilityPolicy API. The \"allowProtectedAppendWrites\" and \"allowProtectedAppendWritesAll\" properties are mutually exclusive. Defaults to false." } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, "resources": [ + { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-containerimmutpolicy.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, { "type": "Microsoft.Storage/storageAccounts/blobServices/containers/immutabilityPolicies", "apiVersion": "2025-01-01", @@ -32119,8 +32265,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "17257177273818228540" + "version": "0.41.2.15936", + "templateHash": "17583198711200998285" }, "name": "Storage Account File Share Services", "description": "This module deploys a Storage Account File Share Service." @@ -32653,8 +32799,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "3816110293032807861" + "version": "0.41.2.15936", + "templateHash": "10353179772982843397" }, "name": "Storage Account File Shares", "description": "This module deploys a Storage Account File Share." @@ -32870,7 +33016,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-03-01", - "name": "[format('46d3xbcp.res.storage-fileshare.{0}.{1}', replace('0.1.3', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "name": "[format('46d3xbcp.res.storage-fileshare.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -33138,8 +33284,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "4565703002375249410" + "version": "0.41.2.15936", + "templateHash": "9644461291744477521" }, "name": "Storage Account Queue Services", "description": "This module deploys a Storage Account Queue Service." @@ -33478,7 +33624,8 @@ } }, "variables": { - "name": "default" + "name": "default", + "enableReferencedModulesTelemetry": false }, "resources": { "storageAccount": { @@ -33561,6 +33708,9 @@ }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('queues'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" } }, "template": { @@ -33570,8 +33720,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "7074748362797990420" + "version": "0.41.2.15936", + "templateHash": "12812824360066955039" }, "name": "Storage Account Queues", "description": "This module deploys a Storage Account Queue." @@ -33686,6 +33836,13 @@ "metadata": { "description": "Optional. Array of role assignments to create." } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, "variables": { @@ -33719,6 +33876,26 @@ "apiVersion": "2025-06-01", "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-queue.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, "storageAccount": { "existing": true, "type": "Microsoft.Storage/storageAccounts", @@ -33843,8 +34020,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "6375163985707233003" + "version": "0.41.2.15936", + "templateHash": "10320403358700650147" }, "name": "Storage Account Table Services", "description": "This module deploys a Storage Account Table Service." @@ -34173,7 +34350,8 @@ } }, "variables": { - "name": "default" + "name": "default", + "enableReferencedModulesTelemetry": false }, "resources": { "storageAccount": { @@ -34253,6 +34431,9 @@ }, "roleAssignments": { "value": "[tryGet(coalesce(parameters('tables'), createArray())[copyIndex()], 'roleAssignments')]" + }, + "enableTelemetry": { + "value": "[variables('enableReferencedModulesTelemetry')]" } }, "template": { @@ -34262,8 +34443,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "5944147787326354061" + "version": "0.41.2.15936", + "templateHash": "11362260974696477885" }, "name": "Storage Account Table", "description": "This module deploys a Storage Account Table." @@ -34368,6 +34549,13 @@ "metadata": { "description": "Required. Name of the table." } + }, + "enableTelemetry": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable/Disable usage telemetry for module." + } } }, "variables": { @@ -34399,6 +34587,26 @@ "apiVersion": "2025-06-01", "name": "[format('{0}/{1}', parameters('storageAccountName'), 'default')]" }, + "avmTelemetry": { + "condition": "[parameters('enableTelemetry')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2024-03-01", + "name": "[format('46d3xbcp.res.storage-table.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name), 0, 4))]", + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [], + "outputs": { + "telemetry": { + "type": "String", + "value": "For more information, see https://aka.ms/avm/TelemetryInfo" + } + } + } + } + }, "storageAccount": { "existing": true, "type": "Microsoft.Storage/storageAccounts", @@ -34516,8 +34724,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "16028661694229002815" + "version": "0.41.2.15936", + "templateHash": "13227497656004178962" } }, "definitions": { @@ -34670,8 +34878,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "10883422529455341146" + "version": "0.41.2.15936", + "templateHash": "1894366578172550759" }, "name": "Storage Account Object Replication Policy", "description": "This module deploys a Storage Account Object Replication Policy for both the source account and destination account." @@ -34824,8 +35032,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "28895254110549746" + "version": "0.41.2.15936", + "templateHash": "14995722372031126283" }, "name": "Storage Account Object Replication Policy", "description": "This module deploys a Storage Account Object Replication Policy for a provided storage account." @@ -35041,8 +35249,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.40.2.10011", - "templateHash": "28895254110549746" + "version": "0.41.2.15936", + "templateHash": "14995722372031126283" }, "name": "Storage Account Object Replication Policy", "description": "This module deploys a Storage Account Object Replication Policy for a provided storage account." @@ -35457,8 +35665,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "11889744396543212232" + "version": "0.40.2.10011", + "templateHash": "1772014800591596213" }, "name": "Azure Cosmos DB account", "description": "This module deploys an Azure Cosmos DB account. The API used for the account is determined by the child resources that are deployed." @@ -36148,7 +36356,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -36190,7 +36398,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -36231,7 +36439,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -36541,6 +36749,29 @@ } } }, + "customerManagedKeyAndVaultOnlyType": { + "type": "object", + "properties": { + "keyVaultResourceId": { + "type": "string", + "metadata": { + "description": "Required. The resource ID of a key vault to reference a customer managed key for encryption from." + } + }, + "keyName": { + "type": "string", + "metadata": { + "description": "Required. The name of the customer managed key to use for encryption." + } + } + }, + "metadata": { + "description": "An AVM-aligned type for a customer-managed key. To be used if only the key vault & key may be specified.", + "__bicep_imported_from!": { + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" + } + } + }, "diagnosticSettingFullType": { "type": "object", "properties": { @@ -36659,7 +36890,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -36743,7 +36974,7 @@ "metadata": { "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -36771,7 +37002,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -36947,7 +37178,7 @@ "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can NOT be assumed (i.e., for services that have more than one subresource, like Storage Account with Blob (blob, table, queue, file, ...).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -37022,7 +37253,7 @@ "metadata": { "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } } @@ -37467,6 +37698,13 @@ "metadata": { "description": "Optional. The default identity for accessing key vault used in features like customer managed keys. Use `FirstPartyIdentity` to use the tenant-level CosmosDB enterprise application. The default identity needs to be explicitly set by the users." } + }, + "customerManagedKey": { + "$ref": "#/definitions/customerManagedKeyAndVaultOnlyType", + "nullable": true, + "metadata": { + "description": "Optional. The customer managed key definition. If specified, the parameter `defaultIdentity` must be configured as well." + } } }, "variables": { @@ -37479,7 +37717,7 @@ ], "enableReferencedModulesTelemetry": false, "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", - "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", + "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(variables('formattedUserAssignedIdentities'))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(variables('formattedUserAssignedIdentities'))), 'UserAssigned', null())), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInControlPlaneRoleNames": { "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", "Cosmos DB Account Reader Role": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'fbdf93bf-df7d-467e-a4d2-9458aa1360c8')]", @@ -37491,14 +37729,24 @@ "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]", "Role Based Access Control Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]", "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]" - } + }, + "isHSMManagedCMK": "[equals(tryGet(split(coalesce(tryGet(parameters('customerManagedKey'), 'keyVaultResourceId'), ''), '/'), 7), 'managedHSMs')]" }, "resources": { + "cMKKeyVault::cMKKey": { + "condition": "[and(and(not(empty(parameters('customerManagedKey'))), not(variables('isHSMManagedCMK'))), and(not(empty(parameters('customerManagedKey'))), not(variables('isHSMManagedCMK'))))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2024-11-01", + "subscriptionId": "[split(parameters('customerManagedKey').keyVaultResourceId, '/')[2]]", + "resourceGroup": "[split(parameters('customerManagedKey').keyVaultResourceId, '/')[4]]", + "name": "[format('{0}/{1}', last(split(parameters('customerManagedKey').keyVaultResourceId, '/')), parameters('customerManagedKey').keyName)]" + }, "avmTelemetry": { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2024-07-01", - "name": "[format('46d3xbcp.res.documentdb-databaseaccount.{0}.{1}', replace('0.18.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.documentdb-databaseaccount.{0}.{1}', replace('0.19.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -37514,6 +37762,15 @@ } } }, + "cMKKeyVault": { + "condition": "[and(not(empty(parameters('customerManagedKey'))), not(variables('isHSMManagedCMK')))]", + "existing": true, + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2024-11-01", + "subscriptionId": "[split(parameters('customerManagedKey').keyVaultResourceId, '/')[2]]", + "resourceGroup": "[split(parameters('customerManagedKey').keyVaultResourceId, '/')[4]]", + "name": "[last(split(parameters('customerManagedKey').keyVaultResourceId, '/'))]" + }, "databaseAccount": { "type": "Microsoft.DocumentDB/databaseAccounts", "apiVersion": "2025-04-15", @@ -37522,13 +37779,16 @@ "tags": "[parameters('tags')]", "identity": "[variables('identity')]", "kind": "[if(not(empty(parameters('mongodbDatabases'))), 'MongoDB', 'GlobalDocumentDB')]", - "properties": "[shallowMerge(createArray(createObject('enableBurstCapacity', if(not(contains(coalesce(parameters('capabilitiesToAdd'), createArray()), 'EnableServerless')), parameters('enableBurstCapacity'), false()), 'analyticalStorageConfiguration', parameters('analyticalStorageConfiguration'), 'defaultIdentity', if(and(not(empty(parameters('defaultIdentity'))), not(equals(tryGet(parameters('defaultIdentity'), 'name'), 'UserAssignedIdentity'))), parameters('defaultIdentity').name, format('UserAssignedIdentity={0}', tryGet(parameters('defaultIdentity'), 'resourceId'))), 'enablePartitionMerge', parameters('enablePartitionMerge'), 'enablePerRegionPerPartitionAutoscale', parameters('enablePerRegionPerPartitionAutoscale'), 'databaseAccountOfferType', parameters('databaseAccountOfferType'), 'backupPolicy', shallowMerge(createArray(createObject('type', parameters('backupPolicyType')), if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject()), if(equals(parameters('backupPolicyType'), 'Periodic'), createObject('periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))), createObject()))), 'capabilities', map(coalesce(parameters('capabilitiesToAdd'), createArray()), lambda('capability', createObject('name', lambdaVariables('capability'))))), if(not(empty(parameters('cors'))), createObject('cors', parameters('cors')), createObject()), if(contains(coalesce(parameters('capabilitiesToAdd'), createArray()), 'EnableCassandra'), createObject('connectorOffer', if(parameters('enableCassandraConnector'), 'Small', null()), 'enableCassandraConnector', parameters('enableCassandraConnector')), createObject()), createObject('minimalTlsVersion', parameters('minimumTlsVersion'), 'capacity', createObject('totalThroughputLimit', parameters('totalThroughputLimit')), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Disabled')), if(or(or(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), not(empty(parameters('tables')))), not(empty(parameters('cassandraKeyspaces')))), createObject('consistencyPolicy', shallowMerge(createArray(createObject('defaultConsistencyLevel', parameters('defaultConsistencyLevel')), if(equals(parameters('defaultConsistencyLevel'), 'BoundedStaleness'), createObject('maxStalenessPrefix', parameters('maxStalenessPrefix'), 'maxIntervalInSeconds', parameters('maxIntervalInSeconds')), createObject()))), 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'locations', if(not(empty(parameters('failoverLocations'))), map(parameters('failoverLocations'), lambda('failoverLocation', createObject('failoverPriority', lambdaVariables('failoverLocation').failoverPriority, 'locationName', lambdaVariables('failoverLocation').locationName, 'isZoneRedundant', coalesce(tryGet(lambdaVariables('failoverLocation'), 'isZoneRedundant'), true())))), createArray(createObject('failoverPriority', 0, 'locationName', parameters('location'), 'isZoneRedundant', parameters('zoneRedundant')))), 'ipRules', map(coalesce(tryGet(parameters('networkRestrictions'), 'ipRules'), createArray()), lambda('ipRule', createObject('ipAddressOrRange', lambdaVariables('ipRule')))), 'virtualNetworkRules', map(coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray()), lambda('rule', createObject('id', lambdaVariables('rule').subnetResourceId, 'ignoreMissingVNetServiceEndpoint', false()))), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'None'), 'networkAclBypassResourceIds', tryGet(parameters('networkRestrictions'), 'networkAclBypassResourceIds'), 'isVirtualNetworkFilterEnabled', or(not(empty(tryGet(parameters('networkRestrictions'), 'ipRules'))), not(empty(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules')))), 'enableFreeTier', parameters('enableFreeTier'), 'enableAutomaticFailover', parameters('enableAutomaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(or(or(not(empty(parameters('mongodbDatabases'))), not(empty(parameters('gremlinDatabases')))), not(empty(parameters('cassandraKeyspaces')))), createObject('disableLocalAuth', false(), 'disableKeyBasedMetadataWriteAccess', false()), createObject('disableLocalAuth', parameters('disableLocalAuthentication'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess'))), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject())))]" + "properties": "[shallowMerge(createArray(createObject('enableBurstCapacity', if(not(contains(coalesce(parameters('capabilitiesToAdd'), createArray()), 'EnableServerless')), parameters('enableBurstCapacity'), false()), 'databaseAccountOfferType', parameters('databaseAccountOfferType'), 'analyticalStorageConfiguration', parameters('analyticalStorageConfiguration'), 'defaultIdentity', if(and(not(empty(parameters('defaultIdentity'))), not(equals(tryGet(parameters('defaultIdentity'), 'name'), 'UserAssignedIdentity'))), parameters('defaultIdentity').name, format('UserAssignedIdentity={0}', tryGet(parameters('defaultIdentity'), 'resourceId'))), 'keyVaultKeyUri', if(not(empty(parameters('customerManagedKey'))), if(not(variables('isHSMManagedCMK')), format('{0}', reference('cMKKeyVault::cMKKey').keyUri), format('https://{0}.managedhsm.azure.net/keys/{1}', last(split(parameters('customerManagedKey').keyVaultResourceId, '/')), parameters('customerManagedKey').keyName)), null()), 'enablePartitionMerge', parameters('enablePartitionMerge'), 'enablePerRegionPerPartitionAutoscale', parameters('enablePerRegionPerPartitionAutoscale'), 'backupPolicy', shallowMerge(createArray(createObject('type', parameters('backupPolicyType')), if(equals(parameters('backupPolicyType'), 'Continuous'), createObject('continuousModeProperties', createObject('tier', parameters('backupPolicyContinuousTier'))), createObject()), if(equals(parameters('backupPolicyType'), 'Periodic'), createObject('periodicModeProperties', createObject('backupIntervalInMinutes', parameters('backupIntervalInMinutes'), 'backupRetentionIntervalInHours', parameters('backupRetentionIntervalInHours'), 'backupStorageRedundancy', parameters('backupStorageRedundancy'))), createObject()))), 'capabilities', map(coalesce(parameters('capabilitiesToAdd'), createArray()), lambda('capability', createObject('name', lambdaVariables('capability'))))), if(not(empty(parameters('cors'))), createObject('cors', parameters('cors')), createObject()), if(contains(coalesce(parameters('capabilitiesToAdd'), createArray()), 'EnableCassandra'), createObject('connectorOffer', if(parameters('enableCassandraConnector'), 'Small', null()), 'enableCassandraConnector', parameters('enableCassandraConnector')), createObject()), createObject('minimalTlsVersion', parameters('minimumTlsVersion'), 'capacity', createObject('totalThroughputLimit', parameters('totalThroughputLimit')), 'publicNetworkAccess', coalesce(tryGet(parameters('networkRestrictions'), 'publicNetworkAccess'), 'Disabled'), 'locations', if(not(empty(parameters('failoverLocations'))), map(parameters('failoverLocations'), lambda('failoverLocation', createObject('failoverPriority', lambdaVariables('failoverLocation').failoverPriority, 'locationName', lambdaVariables('failoverLocation').locationName, 'isZoneRedundant', coalesce(tryGet(lambdaVariables('failoverLocation'), 'isZoneRedundant'), true())))), createArray(createObject('failoverPriority', 0, 'locationName', parameters('location'), 'isZoneRedundant', parameters('zoneRedundant'))))), if(or(or(or(or(not(empty(parameters('sqlDatabases'))), not(empty(parameters('mongodbDatabases')))), not(empty(parameters('gremlinDatabases')))), not(empty(parameters('tables')))), not(empty(parameters('cassandraKeyspaces')))), createObject('consistencyPolicy', shallowMerge(createArray(createObject('defaultConsistencyLevel', parameters('defaultConsistencyLevel')), if(equals(parameters('defaultConsistencyLevel'), 'BoundedStaleness'), createObject('maxStalenessPrefix', parameters('maxStalenessPrefix'), 'maxIntervalInSeconds', parameters('maxIntervalInSeconds')), createObject()))), 'enableMultipleWriteLocations', parameters('enableMultipleWriteLocations'), 'ipRules', map(coalesce(tryGet(parameters('networkRestrictions'), 'ipRules'), createArray()), lambda('ipRule', createObject('ipAddressOrRange', lambdaVariables('ipRule')))), 'virtualNetworkRules', map(coalesce(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules'), createArray()), lambda('rule', createObject('id', lambdaVariables('rule').subnetResourceId, 'ignoreMissingVNetServiceEndpoint', false()))), 'networkAclBypass', coalesce(tryGet(parameters('networkRestrictions'), 'networkAclBypass'), 'None'), 'networkAclBypassResourceIds', tryGet(parameters('networkRestrictions'), 'networkAclBypassResourceIds'), 'isVirtualNetworkFilterEnabled', or(not(empty(tryGet(parameters('networkRestrictions'), 'ipRules'))), not(empty(tryGet(parameters('networkRestrictions'), 'virtualNetworkRules')))), 'enableFreeTier', parameters('enableFreeTier'), 'enableAutomaticFailover', parameters('enableAutomaticFailover'), 'enableAnalyticalStorage', parameters('enableAnalyticalStorage')), createObject()), if(or(or(not(empty(parameters('mongodbDatabases'))), not(empty(parameters('gremlinDatabases')))), not(empty(parameters('cassandraKeyspaces')))), createObject('disableLocalAuth', false(), 'disableKeyBasedMetadataWriteAccess', false()), createObject('disableLocalAuth', parameters('disableLocalAuthentication'), 'disableKeyBasedMetadataWriteAccess', parameters('disableKeyBasedMetadataWriteAccess'))), if(not(empty(parameters('mongodbDatabases'))), createObject('apiProperties', createObject('serverVersion', parameters('serverVersion'))), createObject())))]", + "dependsOn": [ + "cMKKeyVault::cMKKey" + ] }, "databaseAccount_lock": { "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", "apiVersion": "2020-05-01", - "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", @@ -37545,7 +37805,7 @@ }, "type": "Microsoft.Insights/diagnosticSettings", "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(parameters('diagnosticSettings'), createArray())[copyIndex()], 'name'), format('{0}-diagnosticSettings', parameters('name')))]", "properties": { "copy": [ @@ -37586,7 +37846,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.DocumentDB/databaseAccounts/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.DocumentDB/databaseAccounts', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -37638,8 +37898,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "1549250134356326406" + "version": "0.40.2.10011", + "templateHash": "3972888645334640168" }, "name": "DocumentDB Database Account SQL Databases", "description": "This module deploys a SQL Database in a CosmosDB Account." @@ -37894,8 +38154,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "1005439058963058082" + "version": "0.40.2.10011", + "templateHash": "4781880351108045502" }, "name": "DocumentDB Database Account SQL Database Containers", "description": "This module deploys a SQL Database Container in a CosmosDB Account." @@ -38162,8 +38422,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "8600771348637416058" + "version": "0.40.2.10011", + "templateHash": "8514367433037227852" }, "name": "DocumentDB Database Account SQL Role Definitions.", "description": "This module deploys a SQL Role Definision in a CosmosDB Account." @@ -38339,8 +38599,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "17007224102611744259" + "version": "0.40.2.10011", + "templateHash": "11817543900771838380" }, "name": "DocumentDB Database Account SQL Role Assignments.", "description": "This module deploys a SQL Role Assignment in a CosmosDB Account." @@ -38538,8 +38798,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "17007224102611744259" + "version": "0.40.2.10011", + "templateHash": "11817543900771838380" }, "name": "DocumentDB Database Account SQL Role Assignments.", "description": "This module deploys a SQL Role Assignment in a CosmosDB Account." @@ -38705,8 +38965,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "17859939500809924517" + "version": "0.40.2.10011", + "templateHash": "10787709019875067397" }, "name": "DocumentDB Database Account Cassandra Role Definitions.", "description": "This module deploys a Cassandra Role Definition in a CosmosDB Account." @@ -38862,8 +39122,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "552115240340341941" + "version": "0.40.2.10011", + "templateHash": "14764024820910071147" }, "name": "DocumentDB Database Account Cassandra Role Assignments.", "description": "This module deploys a Cassandra Role Assignment in a CosmosDB Account." @@ -39016,8 +39276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "552115240340341941" + "version": "0.40.2.10011", + "templateHash": "14764024820910071147" }, "name": "DocumentDB Database Account Cassandra Role Assignments.", "description": "This module deploys a Cassandra Role Assignment in a CosmosDB Account." @@ -39145,8 +39405,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "7289795303297936310" + "version": "0.40.2.10011", + "templateHash": "13897098552792121791" }, "name": "DocumentDB Database Account MongoDB Databases", "description": "This module deploys a MongoDB Database within a CosmosDB Account." @@ -39305,8 +39565,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "4317369978166598876" + "version": "0.40.2.10011", + "templateHash": "16151461445994734468" }, "name": "DocumentDB Database Account MongoDB Database Collections", "description": "This module deploys a MongoDB Database Collection." @@ -39470,8 +39730,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "14708982296215631776" + "version": "0.40.2.10011", + "templateHash": "11959636451300474346" }, "name": "DocumentDB Database Account Gremlin Databases", "description": "This module deploys a Gremlin Database within a CosmosDB Account." @@ -39630,8 +39890,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "15097132107382000570" + "version": "0.40.2.10011", + "templateHash": "10487122333182352122" }, "name": "DocumentDB Database Accounts Gremlin Databases Graphs", "description": "This module deploys a DocumentDB Database Accounts Gremlin Database Graph." @@ -39811,8 +40071,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "11768488776074268398" + "version": "0.40.2.10011", + "templateHash": "1787500858429182824" }, "name": "Azure Cosmos DB account tables", "description": "This module deploys a table within an Azure Cosmos DB Account." @@ -39950,8 +40210,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "63327155428300562" + "version": "0.40.2.10011", + "templateHash": "15257396763463366586" }, "name": "DocumentDB Database Account Cassandra Keyspaces", "description": "This module deploys a Cassandra Keyspace within a CosmosDB Account." @@ -40194,8 +40454,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "785607874724829202" + "version": "0.40.2.10011", + "templateHash": "15998065591386988132" }, "name": "DocumentDB Database Account Cassandra Keyspaces Tables", "description": "This module deploys a Cassandra Table within a Cassandra Keyspace in a CosmosDB Account." @@ -40371,8 +40631,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.33.27573", - "templateHash": "14021794949328228224" + "version": "0.40.2.10011", + "templateHash": "6617803098467821091" }, "name": "DocumentDB Database Account Cassandra Keyspaces Views", "description": "This module deploys a Cassandra View (Materialized View) within a Cassandra Keyspace in a CosmosDB Account." @@ -42103,7 +42363,7 @@ "_generator": { "name": "bicep", "version": "0.42.1.51946", - "templateHash": "1960900918045956971" + "templateHash": "18300393946144087310" } }, "definitions": { @@ -42189,12 +42449,19 @@ "metadata": { "description": "Optional. Specify the type of lock." } + }, + "notes": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Specify the notes of the lock." + } } }, "metadata": { "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -42220,7 +42487,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -42262,7 +42529,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -42303,7 +42570,7 @@ }, "metadata": { "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -42378,7 +42645,7 @@ "metadata": { "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -42500,7 +42767,7 @@ "metadata": { "description": "An AVM-aligned type for a diagnostic setting. To be used if both logs & metrics are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -42528,7 +42795,7 @@ "metadata": { "description": "An AVM-aligned type for a managed identity configuration. To be used if both a system-assigned & user-assigned identities are supported by the resource provider.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -42656,6 +42923,9 @@ "type": "object", "nullable": true, "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Network/privateEndpoints@2024-07-01#properties/tags" + }, "description": "Optional. Tags to be applied on all resources/Resource Groups in this deployment." } }, @@ -42670,7 +42940,7 @@ "metadata": { "description": "An AVM-aligned type for a private endpoint. To be used if the private endpoint's default service / groupId can be assumed (i.e., for services that only have one Private Endpoint type like 'vault' for key vault).", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.5.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } } @@ -43132,7 +43402,7 @@ "_generator": { "name": "bicep", "version": "0.42.1.51946", - "templateHash": "12846584330925464650" + "templateHash": "1934732677721164061" }, "name": "Site App Settings", "description": "This module deploys a Site App Setting." @@ -43230,7 +43500,7 @@ "condition": "[not(empty(parameters('storageAccountResourceId')))]", "existing": true, "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2025-06-01", + "apiVersion": "2025-08-01", "subscriptionId": "[split(parameters('storageAccountResourceId'), '/')[2]]", "resourceGroup": "[split(parameters('storageAccountResourceId'), '/')[4]]", "name": "[last(split(parameters('storageAccountResourceId'), '/'))]" @@ -43245,7 +43515,7 @@ "type": "Microsoft.Web/sites/config", "apiVersion": "2025-03-01", "name": "[format('{0}/{1}', parameters('appName'), parameters('name'))]", - "properties": "[union(parameters('properties'), parameters('currentAppSettings'), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(parameters('storageAccountResourceId'), '/')), listKeys('storageAccount', '2025-06-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), createObject('AzureWebJobsStorage__accountName', last(split(parameters('storageAccountResourceId'), '/')), 'AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob, 'AzureWebJobsStorage__queueServiceUri', reference('storageAccount').primaryEndpoints.queue, 'AzureWebJobsStorage__tableServiceUri', reference('storageAccount').primaryEndpoints.table), createObject())), if(not(empty(parameters('applicationInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('applicationInsights').ConnectionString), createObject()), variables('loggingProperties'))]", + "properties": "[union(parameters('properties'), parameters('currentAppSettings'), if(and(not(empty(parameters('storageAccountResourceId'))), not(parameters('storageAccountUseIdentityAuthentication'))), createObject('AzureWebJobsStorage', format('DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1};EndpointSuffix={2}', last(split(parameters('storageAccountResourceId'), '/')), listKeys('storageAccount', '2025-08-01').keys[0].value, environment().suffixes.storage)), if(and(not(empty(parameters('storageAccountResourceId'))), parameters('storageAccountUseIdentityAuthentication')), createObject('AzureWebJobsStorage__accountName', last(split(parameters('storageAccountResourceId'), '/')), 'AzureWebJobsStorage__blobServiceUri', reference('storageAccount').primaryEndpoints.blob, 'AzureWebJobsStorage__queueServiceUri', reference('storageAccount').primaryEndpoints.queue, 'AzureWebJobsStorage__tableServiceUri', reference('storageAccount').primaryEndpoints.table), createObject())), if(not(empty(parameters('applicationInsightResourceId'))), createObject('APPLICATIONINSIGHTS_CONNECTION_STRING', reference('applicationInsights').ConnectionString), createObject()), variables('loggingProperties'))]", "dependsOn": [ "applicationInsights", "storageAccount" @@ -43343,8 +43613,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.5.1644", - "templateHash": "16604612898799598358" + "version": "0.41.2.15936", + "templateHash": "18436885663402767850" }, "name": "Private Endpoints", "description": "This module deploys a Private Endpoint." @@ -43408,7 +43678,7 @@ "metadata": { "description": "An AVM-aligned type for a lock.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } }, @@ -43507,7 +43777,7 @@ "metadata": { "description": "An AVM-aligned type for a role assignment.", "__bicep_imported_from!": { - "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.6.1" + "sourceTemplate": "br:mcr.microsoft.com/bicep/avm/utl/types/avm-common-types:0.7.0" } } } @@ -43546,12 +43816,22 @@ "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/ipConfigurations" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/ipConfigurations" }, "description": "Optional. A list of IP configurations of the private endpoint. This will be used to map to the First Party Service endpoints." }, "nullable": true }, + "ipVersionType": { + "type": "string", + "metadata": { + "__bicep_resource_derived_type!": { + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/ipVersionType" + }, + "description": "Optional. Specifies the IP version type for the private IPs of the private endpoint. If not defined, this defaults to IPv4." + }, + "defaultValue": "IPv4" + }, "privateDnsZoneGroup": { "$ref": "#/definitions/privateDnsZoneGroupType", "nullable": true, @@ -43587,7 +43867,7 @@ "type": "object", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/tags" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/tags" }, "description": "Optional. Tags to be applied on all resources/resource groups in this deployment." }, @@ -43597,7 +43877,7 @@ "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/customDnsConfigs" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/customDnsConfigs" }, "description": "Optional. Custom DNS configurations." }, @@ -43607,7 +43887,7 @@ "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/manualPrivateLinkServiceConnections" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/manualPrivateLinkServiceConnections" }, "description": "Conditional. A grouping of information about the connection to the remote resource. Used when the network admin does not have access to approve connections to the remote resource. Required if `privateLinkServiceConnections` is empty." }, @@ -43617,7 +43897,7 @@ "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/privateLinkServiceConnections" + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/privateLinkServiceConnections" }, "description": "Conditional. A grouping of information about the connection to the remote resource. Required if `manualPrivateLinkServiceConnections` is empty." }, @@ -43657,7 +43937,7 @@ "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2025-04-01", - "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.11.1', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", + "name": "[format('46d3xbcp.res.network-privateendpoint.{0}.{1}', replace('0.12.0', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", "template": { @@ -43675,7 +43955,7 @@ }, "privateEndpoint": { "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "[parameters('name')]", "location": "[parameters('location')]", "tags": "[parameters('tags')]", @@ -43696,14 +43976,15 @@ "privateLinkServiceConnections": "[coalesce(parameters('privateLinkServiceConnections'), createArray())]", "subnet": { "id": "[parameters('subnetResourceId')]" - } + }, + "ipVersionType": "[parameters('ipVersionType')]" } }, "privateEndpoint_lock": { "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]", "type": "Microsoft.Authorization/locks", "apiVersion": "2020-05-01", - "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]", "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]", "properties": { "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]", @@ -43720,7 +44001,7 @@ }, "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Network/privateEndpoints/{0}', parameters('name'))]", + "scope": "[resourceId('Microsoft.Network/privateEndpoints', parameters('name'))]", "name": "[coalesce(tryGet(coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()], 'name'), guid(resourceId('Microsoft.Network/privateEndpoints', parameters('name')), coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].principalId, coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId))]", "properties": { "roleDefinitionId": "[coalesce(variables('formattedRoleAssignments'), createArray())[copyIndex()].roleDefinitionId]", @@ -43763,8 +44044,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.38.5.1644", - "templateHash": "24141742673128945" + "version": "0.41.2.15936", + "templateHash": "9935179114830442414" }, "name": "Private Endpoint Private DNS Zone Groups", "description": "This module deploys a Private Endpoint Private DNS Zone Group." @@ -43823,12 +44104,12 @@ "privateEndpoint": { "existing": true, "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "[parameters('privateEndpointName')]" }, "privateDnsZoneGroup": { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2024-10-01", + "apiVersion": "2025-05-01", "name": "[format('{0}/{1}', parameters('privateEndpointName'), parameters('name'))]", "properties": { "copy": [ @@ -43903,13 +44184,13 @@ "metadata": { "description": "The location the resource was deployed into." }, - "value": "[reference('privateEndpoint', '2024-10-01', 'full').location]" + "value": "[reference('privateEndpoint', '2025-05-01', 'full').location]" }, "customDnsConfigs": { "type": "array", "metadata": { "__bicep_resource_derived_type!": { - "source": "Microsoft.Network/privateEndpoints@2024-01-01#properties/properties/properties/customDnsConfigs", + "source": "Microsoft.Network/privateEndpoints@2025-05-01#properties/properties/properties/customDnsConfigs", "output": true }, "description": "The custom DNS configurations of the private endpoint." @@ -44169,7 +44450,7 @@ "_generator": { "name": "bicep", "version": "0.42.1.51946", - "templateHash": "10568375158361462358" + "templateHash": "12074733710352169882" } }, "parameters": { @@ -44253,7 +44534,7 @@ { "condition": "[parameters('enableTelemetry')]", "type": "Microsoft.Resources/deployments", - "apiVersion": "2024-03-01", + "apiVersion": "2025-04-01", "name": "[format('46d3xbcp.res.containerinstance.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]", "properties": { "mode": "Incremental", diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep index 411f4b0a3..323fd435b 100644 --- a/infra/main_custom.bicep +++ b/infra/main_custom.bicep @@ -294,7 +294,7 @@ var existingTags = existingResourceGroup.tags ?? {} // ============== // #disable-next-line no-deployments-resources -resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { +resource avmTelemetry 'Microsoft.Resources/deployments@2025-04-01' = if (enableTelemetry) { name: '46d3xbcp.ptn.sa-contentgeneration.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, solutionLocation), 0, 4)}' properties: { mode: 'Incremental' @@ -540,7 +540,7 @@ var dnsZoneIndex = { } @batchSize(5) -module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.8.0' = [ +module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.8.1' = [ for (zone, i) in privateDnsZones: if (enablePrivateNetworking) { name: take('avm.res.network.private-dns-zone.${replace(zone, '.', '-')}', 64) params: { @@ -558,7 +558,7 @@ module avmPrivateDnsZones 'br/public:avm/res/network/private-dns-zone:0.8.0' = [ ] // ========== AI Foundry: AI Services ========== // -module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.14.1' = if (!useExistingAiFoundryAiProject) { +module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.14.2' = if (!useExistingAiFoundryAiProject) { name: take('avm.res.cognitive-services.account.${aiFoundryAiServicesResourceName}', 64) params: { name: aiFoundryAiServicesResourceName @@ -622,7 +622,7 @@ module aiFoundryAiServices 'br/public:avm/res/cognitive-services/account:0.14.1' } // Create private endpoint for AI Services AFTER the account is fully provisioned -module aiServicesPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.11.1' = if (!useExistingAiFoundryAiProject && enablePrivateNetworking) { +module aiServicesPrivateEndpoint 'br/public:avm/res/network/private-endpoint:0.12.0' = if (!useExistingAiFoundryAiProject && enablePrivateNetworking) { name: take('pep-ai-services-${aiFoundryAiServicesResourceName}', 64) params: { name: 'pep-${aiFoundryAiServicesResourceName}' @@ -747,7 +747,7 @@ module aiSearch 'br/public:avm/res/search/search-service:0.12.0' = { } // ========== AI Search Connection to AI Services ========== // -resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-09-01' = if (!useExistingAiFoundryAiProject) { +resource aiSearchFoundryConnection 'Microsoft.CognitiveServices/accounts/projects/connections@2025-12-01' = if (!useExistingAiFoundryAiProject) { name: '${aiFoundryAiServicesResourceName}/${aiFoundryAiProjectResourceName}/${aiSearchConnectionName}' properties: { category: 'CognitiveSearch' @@ -768,7 +768,7 @@ var productImagesContainer = 'product-images' var generatedImagesContainer = 'generated-images' var dataContainer = 'data' -module storageAccount 'br/public:avm/res/storage/storage-account:0.31.1' = { +module storageAccount 'br/public:avm/res/storage/storage-account:0.32.0' = { name: take('avm.res.storage.storage-account.${storageAccountName}', 64) params: { name: storageAccountName @@ -836,7 +836,7 @@ var cosmosDBDatabaseName = 'content_generation_db' var cosmosDBConversationsContainer = 'conversations' var cosmosDBProductsContainer = 'products' -module cosmosDB 'br/public:avm/res/document-db/database-account:0.18.0' = { +module cosmosDB 'br/public:avm/res/document-db/database-account:0.19.0' = { name: take('avm.res.document-db.database-account.${cosmosDBResourceName}', 64) params: { name: 'cosmos-${solutionSuffix}' @@ -1016,7 +1016,7 @@ var userAssignedIdentityResourceIdForACI = '/subscriptions/${subscription().subs var shouldDeployACI = !empty(imageTag) && imageTag != 'none' #disable-next-line no-deployments-resources -resource aciTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry && shouldDeployACI) { +resource aciTelemetry 'Microsoft.Resources/deployments@2025-04-01' = if (enableTelemetry && shouldDeployACI) { name: '46d3xbcp.res.containerinstance.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, solutionLocation), 0, 4)}' properties: { mode: 'Incremental' diff --git a/infra/modules/ai-project.bicep b/infra/modules/ai-project.bicep index ee6eeeec5..4c425e99e 100644 --- a/infra/modules/ai-project.bicep +++ b/infra/modules/ai-project.bicep @@ -22,11 +22,11 @@ var existingOpenAIEndpoint = useExistingAiFoundryAiProject : '' // Reference to cognitive service in current resource group for new projects -resource cogServiceReference 'Microsoft.CognitiveServices/accounts@2025-10-01-preview' existing = { +resource cogServiceReference 'Microsoft.CognitiveServices/accounts@2025-12-01' existing = { name: aiServicesName } -resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-10-01-preview' = { +resource aiProject 'Microsoft.CognitiveServices/accounts/projects@2025-12-01' = { parent: cogServiceReference name: name tags: tags diff --git a/infra/modules/container-instance.bicep b/infra/modules/container-instance.bicep index 9136d7ac2..9a6767a76 100644 --- a/infra/modules/container-instance.bicep +++ b/infra/modules/container-instance.bicep @@ -41,7 +41,7 @@ var isPrivateNetworking = !empty(subnetResourceId) // ============== // #disable-next-line no-deployments-resources -resource avmTelemetry 'Microsoft.Resources/deployments@2024-03-01' = if (enableTelemetry) { +resource avmTelemetry 'Microsoft.Resources/deployments@2025-04-01' = if (enableTelemetry) { name: '46d3xbcp.res.containerinstance.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}' properties: { mode: 'Incremental' diff --git a/infra/modules/deploy_ai_model.bicep b/infra/modules/deploy_ai_model.bicep index 8ac6237c1..96595e093 100644 --- a/infra/modules/deploy_ai_model.bicep +++ b/infra/modules/deploy_ai_model.bicep @@ -5,14 +5,14 @@ param aiServicesName string param deployments array = [] // Reference AI Services account (module is scoped to the correct resource group) -resource aiServices 'Microsoft.CognitiveServices/accounts@2025-04-01-preview' existing = { +resource aiServices 'Microsoft.CognitiveServices/accounts@2025-12-01' existing = { name: aiServicesName } // Deploy models to AI Services account // Using batchSize(1) to avoid concurrent deployment issues @batchSize(1) -resource modelDeployments 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = [ +resource modelDeployments 'Microsoft.CognitiveServices/accounts/deployments@2025-12-01' = [ for (deployment, index) in deployments: { parent: aiServices name: deployment.name diff --git a/infra/modules/deploy_foundry_role_assignment.bicep b/infra/modules/deploy_foundry_role_assignment.bicep index 041e60d5f..efe58e733 100644 --- a/infra/modules/deploy_foundry_role_assignment.bicep +++ b/infra/modules/deploy_foundry_role_assignment.bicep @@ -36,12 +36,12 @@ resource cognitiveServicesOpenAiUserRole 'Microsoft.Authorization/roleDefinition // ========== Existing Resources ========== // // Reference the existing AI Services account -resource existingAiServices 'Microsoft.CognitiveServices/accounts@2025-10-01-preview' existing = { +resource existingAiServices 'Microsoft.CognitiveServices/accounts@2025-12-01' existing = { name: aiServicesName } // Reference the existing AI Project (if provided) -resource existingAiProject 'Microsoft.CognitiveServices/accounts/projects@2025-10-01-preview' existing = if (!empty(aiProjectName)) { +resource existingAiProject 'Microsoft.CognitiveServices/accounts/projects@2025-12-01' existing = if (!empty(aiProjectName)) { name: aiProjectName parent: existingAiServices } diff --git a/infra/modules/virtualNetwork.bicep b/infra/modules/virtualNetwork.bicep index 2d79b1288..008056bc9 100644 --- a/infra/modules/virtualNetwork.bicep +++ b/infra/modules/virtualNetwork.bicep @@ -212,7 +212,7 @@ param resourceSuffix string // Create NSGs for subnets using AVM Network Security Group module @batchSize(1) -module nsgs 'br/public:avm/res/network/network-security-group:0.5.2' = [ +module nsgs 'br/public:avm/res/network/network-security-group:0.5.3' = [ for (subnet, i) in vnetSubnets: if (!empty(subnet.?networkSecurityGroup)) { name: take('avm.res.network.network-security-group.${subnet.?networkSecurityGroup.name}.${resourceSuffix}', 64) params: { @@ -226,7 +226,7 @@ module nsgs 'br/public:avm/res/network/network-security-group:0.5.2' = [ ] // Create VNet and subnets using AVM Virtual Network module -module virtualNetwork 'br/public:avm/res/network/virtual-network:0.7.2' = { +module virtualNetwork 'br/public:avm/res/network/virtual-network:0.8.0' = { name: take('avm.res.network.virtual-network.${vnetName}', 64) params: { name: vnetName diff --git a/infra/modules/web-sites.bicep b/infra/modules/web-sites.bicep index 17a63ea77..42c7961a6 100644 --- a/infra/modules/web-sites.bicep +++ b/infra/modules/web-sites.bicep @@ -36,7 +36,7 @@ param clientAffinityEnabled bool = true @description('Optional. The resource ID of the app service environment to use for this resource.') param appServiceEnvironmentResourceId string? -import { managedIdentityAllType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +import { managedIdentityAllType } from 'br/public:avm/utl/types/avm-common-types:0.7.0' @description('Optional. The managed identity definition for this resource.') param managedIdentities managedIdentityAllType? @@ -80,14 +80,14 @@ param configs appSettingsConfigType[]? @description('Optional. The Function App configuration object.') param functionAppConfig resourceInput<'Microsoft.Web/sites@2025-03-01'>.properties.functionAppConfig? -import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +import { privateEndpointSingleServiceType } from 'br/public:avm/utl/types/avm-common-types:0.7.0' @description('Optional. Configuration details for private endpoints.') param privateEndpoints privateEndpointSingleServiceType[]? @description('Optional. Tags of the resource.') param tags object? -import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.5.1' +import { diagnosticSettingFullType } from 'br/public:avm/utl/types/avm-common-types:0.7.0' @description('Optional. The diagnostic settings of the service.') param diagnosticSettings diagnosticSettingFullType[]? @@ -266,7 +266,7 @@ resource app_diagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-0 } ] -module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.11.1' = [ +module app_privateEndpoints 'br/public:avm/res/network/private-endpoint:0.12.0' = [ for (privateEndpoint, index) in (privateEndpoints ?? []): { name: '${uniqueString(deployment().name, location)}-app-PrivateEndpoint-${index}' scope: resourceGroup( diff --git a/infra/modules/web-sites.config.bicep b/infra/modules/web-sites.config.bicep index 91d935d6d..8e459319f 100644 --- a/infra/modules/web-sites.config.bicep +++ b/infra/modules/web-sites.config.bicep @@ -96,7 +96,7 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing scope: resourceGroup(split(applicationInsightResourceId!, '/')[2], split(applicationInsightResourceId!, '/')[4]) } -resource storageAccount 'Microsoft.Storage/storageAccounts@2025-06-01' existing = if (!empty(storageAccountResourceId)) { +resource storageAccount 'Microsoft.Storage/storageAccounts@2025-08-01' existing = if (!empty(storageAccountResourceId)) { name: last(split(storageAccountResourceId!, '/')) scope: resourceGroup(split(storageAccountResourceId!, '/')[2], split(storageAccountResourceId!, '/')[4]) } diff --git a/infra/scripts/package_frontend.ps1 b/infra/scripts/package_frontend.ps1 index f711497f6..35888e7a9 100644 --- a/infra/scripts/package_frontend.ps1 +++ b/infra/scripts/package_frontend.ps1 @@ -1,16 +1,16 @@ # Package frontend for App Service deployment # This script is called by AZD during prepackage hook -# Working directory is ./src/app/frontend-server (project directory) +# Working directory is ./src/App/server (project directory) $ErrorActionPreference = 'Stop' Write-Host "Building React frontend..." -ForegroundColor Cyan # Build React frontend (one level up) -Push-Location ../frontend +Push-Location .. npm ci --loglevel=error if ($LASTEXITCODE -ne 0) { throw "npm ci failed with exit code $LASTEXITCODE" } -npm run build -- --outDir ../frontend-server/static +npm run build -- --outDir ./server/static if ($LASTEXITCODE -ne 0) { throw "npm run build failed with exit code $LASTEXITCODE" } Pop-Location diff --git a/infra/scripts/package_frontend.sh b/infra/scripts/package_frontend.sh index 5906d1495..7c25612e2 100644 --- a/infra/scripts/package_frontend.sh +++ b/infra/scripts/package_frontend.sh @@ -3,15 +3,15 @@ set -euo pipefail # Package frontend for App Service deployment # This script is called by AZD during prepackage hook -# Working directory is ./src/app/frontend-server (project directory) +# Working directory is ./src/App/server (project directory) echo "Building React frontend..." # Build React frontend (one level up) -cd ../frontend +cd .. npm ci --loglevel=error -npm run build -- --outDir ../frontend-server/static -cd ../frontend-server +npm run build -- --outDir ./server/static +cd ./server echo "Packaging frontend server..." diff --git a/infra/vscode_web/endpoint-requirements.txt b/infra/vscode_web/endpoint-requirements.txt index 18d6803e8..9b375ab95 100644 --- a/infra/vscode_web/endpoint-requirements.txt +++ b/infra/vscode_web/endpoint-requirements.txt @@ -1,3 +1,3 @@ -azure-ai-projects==1.0.0b12 -azure-identity==1.20.0 -ansible-core~=2.17.0 \ No newline at end of file +azure-ai-projects==2.0.0b4 +azure-identity==1.25.2 +ansible-core~=2.20.3 \ No newline at end of file diff --git a/scripts/deploy.ps1 b/scripts/deploy.ps1 index cc411a2f3..7203524a6 100644 --- a/scripts/deploy.ps1 +++ b/scripts/deploy.ps1 @@ -131,14 +131,14 @@ if ($continue -eq "y" -or $continue -eq "Y") { Write-Host "Step 3: Building and deploying frontend..." -ForegroundColor Green Write-Host "==========================================" -ForegroundColor Green - Set-Location "$ProjectDir\src\frontend" + Set-Location "$ProjectDir\src\App" npm install npm run build # Copy built files to server directory - Copy-Item -Path "$ProjectDir\src\static\*" -Destination "$ProjectDir\src\frontend-server\static\" -Recurse -Force + Copy-Item -Path "$ProjectDir\src\static\*" -Destination "$ProjectDir\src\App\server\static\" -Recurse -Force - Set-Location "$ProjectDir\src\frontend-server" + Set-Location "$ProjectDir\src\App\server" # Create deployment package if (Test-Path "frontend-deploy.zip") { diff --git a/scripts/deploy.sh b/scripts/deploy.sh index efd2b51b9..34fcc24af 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -129,14 +129,14 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then echo "Step 3: Building and deploying frontend..." echo "==========================================" - cd "$PROJECT_DIR/src/frontend" + cd "$PROJECT_DIR/src/App" npm install npm run build # Copy built files to server directory - cp -r "$PROJECT_DIR/src/static/"* "$PROJECT_DIR/src/frontend-server/static/" + cp -r "$PROJECT_DIR/src/static/"* "$PROJECT_DIR/src/App/server/static/" - cd "$PROJECT_DIR/src/frontend-server" + cd "$PROJECT_DIR/src/App/server" # Create deployment package rm -f frontend-deploy.zip diff --git a/scripts/local_dev.ps1 b/scripts/local_dev.ps1 index 58d2e85e2..58080ba02 100644 --- a/scripts/local_dev.ps1 +++ b/scripts/local_dev.ps1 @@ -31,7 +31,7 @@ $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $ProjectRoot = Split-Path -Parent $ScriptDir $SrcDir = Join-Path $ProjectRoot "src" $BackendDir = Join-Path $SrcDir "backend" -$FrontendDir = Join-Path $SrcDir "app\frontend" +$FrontendDir = Join-Path $SrcDir "App" # Default ports $BackendPort = if ($env:BACKEND_PORT) { $env:BACKEND_PORT } else { "5000" } diff --git a/scripts/local_dev.sh b/scripts/local_dev.sh index 8286e0c03..089028eee 100644 --- a/scripts/local_dev.sh +++ b/scripts/local_dev.sh @@ -34,7 +34,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" SRC_DIR="$PROJECT_ROOT/src" BACKEND_DIR="$SRC_DIR/backend" -FRONTEND_DIR="$SRC_DIR/app/frontend" +FRONTEND_DIR="$SRC_DIR/App" # Default ports BACKEND_PORT=${BACKEND_PORT:-5000} diff --git a/src/app/.dockerignore b/src/App/.dockerignore similarity index 96% rename from src/app/.dockerignore rename to src/App/.dockerignore index 01b21e59f..42ae7679c 100644 --- a/src/app/.dockerignore +++ b/src/App/.dockerignore @@ -9,7 +9,7 @@ # Build outputs (will be built in container) static/ **/dist/ -frontend-server/static/ +server/static/ # Development files *.log diff --git a/src/app/WebApp.Dockerfile b/src/App/WebApp.Dockerfile similarity index 84% rename from src/app/WebApp.Dockerfile rename to src/App/WebApp.Dockerfile index fca82196e..ead1365fd 100644 --- a/src/app/WebApp.Dockerfile +++ b/src/App/WebApp.Dockerfile @@ -1,7 +1,7 @@ # ============================================ # Frontend Dockerfile # Multi-stage build for Content Generation Frontend -# Combines: frontend (React/Vite) + frontend-server (Node.js proxy) +# Combines: React/Vite frontend + server (Node.js proxy) # ============================================ # ============================================ @@ -11,14 +11,14 @@ FROM node:20-alpine AS frontend-build WORKDIR /app -# Copy frontend package files -COPY frontend/package*.json ./ +# Copy package files +COPY package*.json ./ # Install dependencies RUN npm ci # Copy frontend source code -COPY frontend/ ./ +COPY . ./ # Build the frontend (outputs to ../static, but we're in /app so it goes to /static) # Override outDir to keep it in the container context @@ -31,14 +31,14 @@ FROM node:20-alpine AS production WORKDIR /app -# Copy frontend-server package files -COPY frontend-server/package*.json ./ +# Copy server package files +COPY server/package*.json ./ # Install only production dependencies RUN npm ci --only=production # Copy the server code -COPY frontend-server/server.js ./ +COPY server/server.js ./ # Copy built frontend assets from stage 1 COPY --from=frontend-build /app/dist ./static diff --git a/src/app/frontend/index.html b/src/App/index.html similarity index 100% rename from src/app/frontend/index.html rename to src/App/index.html diff --git a/src/app/frontend/microsoft.svg b/src/App/microsoft.svg similarity index 100% rename from src/app/frontend/microsoft.svg rename to src/App/microsoft.svg diff --git a/src/app/frontend/package-lock.json b/src/App/package-lock.json similarity index 68% rename from src/app/frontend/package-lock.json rename to src/App/package-lock.json index 59a539864..fb292f69c 100644 --- a/src/app/frontend/package-lock.json +++ b/src/App/package-lock.json @@ -8,36 +8,38 @@ "name": "content-generation-frontend", "version": "1.0.0", "dependencies": { - "@fluentui/react-components": "^9.54.0", - "@fluentui/react-icons": "^2.0.245", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-markdown": "^9.0.1", - "uuid": "^10.0.0" + "@fluentui/react-components": "^9.73.1", + "@fluentui/react-icons": "^2.0.320", + "@reduxjs/toolkit": "^2.2.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-markdown": "^10.1.0", + "react-redux": "^9.1.0", + "uuid": "^14.0.0" }, "devDependencies": { - "@types/node": "^24.10.1", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@types/uuid": "^10.0.0", - "@typescript-eslint/eslint-plugin": "^7.15.0", - "@typescript-eslint/parser": "^7.15.0", - "@vitejs/plugin-react": "^4.3.1", - "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.7", + "@types/node": "^25.3.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@types/uuid": "^11.0.0", + "@typescript-eslint/eslint-plugin": "^8.56.1", + "@typescript-eslint/parser": "^8.56.1", + "@vitejs/plugin-react": "^5.1.4", + "eslint": "^10.0.2", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", "typescript": "^5.5.2", - "vite": "^7.3.1" + "vite": "^7.3.2" } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -46,9 +48,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", "dev": true, "license": "MIT", "engines": { @@ -56,22 +58,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -98,14 +100,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -115,13 +117,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -152,29 +154,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -224,27 +226,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -286,42 +288,42 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", "debug": "^4.3.1" }, "engines": { @@ -329,9 +331,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { @@ -800,9 +802,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "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==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { @@ -828,71 +830,78 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", "dev": true, - "license": "ISC", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": "*" + "node": "^20.19.0 || ^22.13.0 || >=24" } }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" } }, "node_modules/@floating-ui/core": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", - "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.10" + "@floating-ui/utils": "^0.2.11" } }, "node_modules/@floating-ui/devtools": { @@ -905,20 +914,20 @@ } }, "node_modules/@floating-ui/dom": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", - "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", "license": "MIT", "peer": true, "dependencies": { - "@floating-ui/core": "^1.7.3", - "@floating-ui/utils": "^0.2.10" + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" } }, "node_modules/@floating-ui/utils": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", - "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", "license": "MIT" }, "node_modules/@fluentui/keyboard-keys": { @@ -931,30 +940,30 @@ } }, "node_modules/@fluentui/priority-overflow": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@fluentui/priority-overflow/-/priority-overflow-9.2.1.tgz", - "integrity": "sha512-WH5dv54aEqWo/kKQuADAwjv66W6OUMFllQMjpdkrktQp7pu4JXtmF60iYcp9+iuIX9iCeW01j8gNTU08MQlfIQ==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@fluentui/priority-overflow/-/priority-overflow-9.3.0.tgz", + "integrity": "sha512-yaBC0R4e+4ZlCWDulB5S+xBrlnLwfzdg68GaarCqQO8OHjLg7Ah05xTj7PsAYcoHeEg/9vYeBwGXBpRO8+Tjqw==", "license": "MIT", "dependencies": { "@swc/helpers": "^0.5.1" } }, "node_modules/@fluentui/react-accordion": { - "version": "9.8.14", - "resolved": "https://registry.npmjs.org/@fluentui/react-accordion/-/react-accordion-9.8.14.tgz", - "integrity": "sha512-jTcfYDRUotRhUEjE1LeG1Qm10515CQUKxHWQhppBYhq7yAZcS5jOms5tMZHtHs0EQsWv3nMgUYYqoOqAsU0jDQ==", + "version": "9.11.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-accordion/-/react-accordion-9.11.0.tgz", + "integrity": "sha512-mEy73hbJM53tMj3MWqm3ajbBxj48uubnJjumVKI8Z/eXHS8L3GzUy5rf/gUH26xSR2Tl+edpFhYB8PFbJDIKKw==", "license": "MIT", "dependencies": { - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-motion": "^9.11.4", - "@fluentui/react-motion-components-preview": "^0.14.1", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -966,18 +975,18 @@ } }, "node_modules/@fluentui/react-alert": { - "version": "9.0.0-beta.129", - "resolved": "https://registry.npmjs.org/@fluentui/react-alert/-/react-alert-9.0.0-beta.129.tgz", - "integrity": "sha512-afS5Mvf9EH5je3ZOnF96GNaXL5nA/eI69AhO4nsbsvc1RaO/CkEt9+6iVyGy2zeqbQgpsP9UkNwEYyToQ1CrzA==", + "version": "9.0.0-beta.139", + "resolved": "https://registry.npmjs.org/@fluentui/react-alert/-/react-alert-9.0.0-beta.139.tgz", + "integrity": "sha512-R9r4dwwpWpgFmB8wVeWqipjUh/e6lyacnerX39HtVdgcG/PE+kpdHjKGiy8MAD+BGYCzrUxKNhXTQDlpXasJ1Q==", "license": "MIT", "dependencies": { - "@fluentui/react-avatar": "^9.9.12", - "@fluentui/react-button": "^9.6.12", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-button": "^9.9.1", "@fluentui/react-icons": "^2.0.239", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -989,16 +998,16 @@ } }, "node_modules/@fluentui/react-aria": { - "version": "9.17.6", - "resolved": "https://registry.npmjs.org/@fluentui/react-aria/-/react-aria-9.17.6.tgz", - "integrity": "sha512-O421keKMgf9BkHH15kTnKGFuCFKN3ukydJLEfSQJmOfdAHyJMzAul8/zMvkd4vmMr84+PtZUD1+Tylk4NvpN4g==", + "version": "9.17.11", + "resolved": "https://registry.npmjs.org/@fluentui/react-aria/-/react-aria-9.17.11.tgz", + "integrity": "sha512-K9nz+Wn5JliCpG6bIYYPXvKmpOql+w9uyzmYNYkYQ6QHgoCpph7XUFx1HCtsJm2PPNi8WO8g0ZV9jojdGKl1Tg==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-utilities": "^9.26.3", "@swc/helpers": "^0.5.1" }, "peerDependencies": { @@ -1009,21 +1018,21 @@ } }, "node_modules/@fluentui/react-avatar": { - "version": "9.9.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-avatar/-/react-avatar-9.9.12.tgz", - "integrity": "sha512-dlJ5mOKCDChMAECFhpcPHoQicA28ATWQXLtz26hAuVJH2/gC/6mZ0j7drIVl9YECqT/ZbZ3/hpVeZu/S/FVrOA==", + "version": "9.11.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-avatar/-/react-avatar-9.11.1.tgz", + "integrity": "sha512-y1T67rVQQ/D4FAod8F4crXo9funaptscRIiW81LAsbN82fFVexMPQ9GmXooQQvn6ILvjJtf9IyvSJ195qDsyag==", "license": "MIT", "dependencies": { - "@fluentui/react-badge": "^9.4.11", - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-badge": "^9.5.2", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-popover": "^9.12.12", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-tooltip": "^9.8.11", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-tooltip": "^9.10.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1035,16 +1044,16 @@ } }, "node_modules/@fluentui/react-badge": { - "version": "9.4.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-badge/-/react-badge-9.4.11.tgz", - "integrity": "sha512-u2gTg+QeD5uaieAwE89n8MLg2MyZN/kGMx3hJewFKtq3SzvU4xcgcna2Gp4UgpaA3pnGZsJjjjDIHwsv4EyO9Q==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-badge/-/react-badge-9.5.2.tgz", + "integrity": "sha512-+UPAK9dCD6Gx+LWr6vqKMIbYOPf7oXX+GXRtCJ5fekCTHD0VgIWuIMuEtxVrHpJQdb2VNaZadY8/dMomk2JaXw==", "license": "MIT", "dependencies": { "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1056,20 +1065,20 @@ } }, "node_modules/@fluentui/react-breadcrumb": { - "version": "9.3.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-breadcrumb/-/react-breadcrumb-9.3.12.tgz", - "integrity": "sha512-cT5xmYQbAYH7HslJu6O5WvSYzsBvaQ54Q6yIPgV5kCo5n3M6OSrJ0Ga6Zbfqid/GnY4G60FfjOvbfHNNhmx2Sw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-breadcrumb/-/react-breadcrumb-9.4.1.tgz", + "integrity": "sha512-XgUB1yv04GdcL/6kUo6kh+BaN4df1A/Ds/fL1QxNrm5E26Vmvvlc0LN0WV/qb5qhKx0NwhtIXgOZHjfzyt7iCA==", "license": "MIT", "dependencies": { - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-button": "^9.6.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-button": "^9.9.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-link": "^9.7.0", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-link": "^9.8.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1081,19 +1090,19 @@ } }, "node_modules/@fluentui/react-button": { - "version": "9.6.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-button/-/react-button-9.6.12.tgz", - "integrity": "sha512-seI9L9O0fCHzlfKD/via1qqzaLFeiFKQeR1/97nXL06reC3DqLSCeiZP3UTxFljFE1CYZQRJfk1wH/D6j0ZCTA==", + "version": "9.9.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-button/-/react-button-9.9.1.tgz", + "integrity": "sha512-WNzpseiVbqEKKevTkAnyHNoK/8ktYPE6rvf31gGvSDnBBclqfrn4PSYG2ppi+Z7abmClnaNFxpp1OHuOoVQ8Bg==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", + "@fluentui/react-aria": "^9.17.11", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1105,18 +1114,18 @@ } }, "node_modules/@fluentui/react-card": { - "version": "9.5.6", - "resolved": "https://registry.npmjs.org/@fluentui/react-card/-/react-card-9.5.6.tgz", - "integrity": "sha512-hCY6VWrKqq+y0yqUkqgkpTN5TVJSU5ZlKtZU+Sed+TlnKlojkS6cYRvsnWdAKwyFLJF9ZYTn+uos9Vi0wQyjtg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-card/-/react-card-9.6.1.tgz", + "integrity": "sha512-KBijjAxi0mBDSgnA1OCglqAVWc+Q0L7A2wCokszX/53oqfJPSvWxWFma7esz9b5MF/kdRrAR0vmy7MiosepNLQ==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-text": "^9.6.11", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-text": "^9.6.16", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1128,21 +1137,21 @@ } }, "node_modules/@fluentui/react-carousel": { - "version": "9.8.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-carousel/-/react-carousel-9.8.12.tgz", - "integrity": "sha512-Gjn6cd67FodcjfU2MQTBI2xjijzgy54TdQA8vxObZ27I6y9OHeDR07PWTqaCkX8mcBR8ilTxVD5bQ+zuqfb66g==", + "version": "9.9.7", + "resolved": "https://registry.npmjs.org/@fluentui/react-carousel/-/react-carousel-9.9.7.tgz", + "integrity": "sha512-lummYk+tASL/rM/SXWruoqhUAyJjTiOMgiCz55ncE3q2pSZe/EbsV5WfRw5B3y7pHX8xLusN831TBgUthj/sUw==", "license": "MIT", "dependencies": { - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-button": "^9.6.12", - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-tooltip": "^9.8.11", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-tooltip": "^9.10.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1", "embla-carousel": "^8.5.1", @@ -1157,19 +1166,19 @@ } }, "node_modules/@fluentui/react-checkbox": { - "version": "9.5.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-checkbox/-/react-checkbox-9.5.11.tgz", - "integrity": "sha512-M8DTBQK0Z7+HKfRx4mjypH0fEagKK7YMNhGMy18aW3iYWeooA0ut81MzsRM5feqhl+Q8v4VJ0aN9qHNqshkD5g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-checkbox/-/react-checkbox-9.6.1.tgz", + "integrity": "sha512-Rsf3TmcNrzLuHan9lyUFUmMZnNyvS7DV8C4Vc9lZnZTFRBo94GRMGzu0BcWKFbr3cCDT/r5RmIyQYz0kc7Jd2w==", "license": "MIT", "dependencies": { - "@fluentui/react-field": "^9.4.11", + "@fluentui/react-field": "^9.5.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-label": "^9.3.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1181,18 +1190,18 @@ } }, "node_modules/@fluentui/react-color-picker": { - "version": "9.2.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-color-picker/-/react-color-picker-9.2.11.tgz", - "integrity": "sha512-L1ZKJAyioey3glmzMrpawUrzsdu/Nz0m6nVMOznJVuw0vu0BfQuMh/1/0QOoGYXFEbsc4+gSGSCnah4X0EJIsQ==", + "version": "9.2.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-color-picker/-/react-color-picker-9.2.16.tgz", + "integrity": "sha512-+H8Ea8dwoSeUCTLRpUiGLrRsNvBnlHplnwJPU0isp8jdAfrIM/savZTLj6o4rqNFpNHQqAXxGwNuUV9YfHoJuQ==", "license": "MIT", "dependencies": { "@ctrl/tinycolor": "^3.3.4", - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1204,23 +1213,23 @@ } }, "node_modules/@fluentui/react-combobox": { - "version": "9.16.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-combobox/-/react-combobox-9.16.12.tgz", - "integrity": "sha512-SimZpXzTGyDAGHQZmzUl9AsrIOlLDinTbvEwELEYh9X+yE33SZatcPwdpCmBXldBOs/eh+xOuNSOwgerJ3T3qQ==", + "version": "9.17.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-combobox/-/react-combobox-9.17.1.tgz", + "integrity": "sha512-ezgt6tfOKd3wlG6IHvWl0TPNPpfHRtnEwC2kuqHYH/r1nMNp9edFi8Ya3+1eM7oxai19XW0swt69GPwRu51FVQ==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-field": "^9.4.11", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-field": "^9.5.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-positioning": "^9.20.10", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1232,71 +1241,71 @@ } }, "node_modules/@fluentui/react-components": { - "version": "9.72.7", - "resolved": "https://registry.npmjs.org/@fluentui/react-components/-/react-components-9.72.7.tgz", - "integrity": "sha512-tuC8ZMBQicF4p+f9MJv9cVYZUSktQVreAGJq/YJxQ0Ts1mO2rnAuIBkBFlgjnjyebDiAO1FoAAz/wW99hrIh6A==", - "license": "MIT", - "dependencies": { - "@fluentui/react-accordion": "^9.8.14", - "@fluentui/react-alert": "9.0.0-beta.129", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-avatar": "^9.9.12", - "@fluentui/react-badge": "^9.4.11", - "@fluentui/react-breadcrumb": "^9.3.12", - "@fluentui/react-button": "^9.6.12", - "@fluentui/react-card": "^9.5.6", - "@fluentui/react-carousel": "^9.8.12", - "@fluentui/react-checkbox": "^9.5.11", - "@fluentui/react-color-picker": "^9.2.11", - "@fluentui/react-combobox": "^9.16.12", - "@fluentui/react-dialog": "^9.16.3", - "@fluentui/react-divider": "^9.4.11", - "@fluentui/react-drawer": "^9.10.9", - "@fluentui/react-field": "^9.4.11", - "@fluentui/react-image": "^9.3.11", - "@fluentui/react-infobutton": "9.0.0-beta.107", - "@fluentui/react-infolabel": "^9.4.12", - "@fluentui/react-input": "^9.7.11", - "@fluentui/react-label": "^9.3.11", - "@fluentui/react-link": "^9.7.0", - "@fluentui/react-list": "^9.6.6", - "@fluentui/react-menu": "^9.20.5", - "@fluentui/react-message-bar": "^9.6.14", - "@fluentui/react-motion": "^9.11.4", - "@fluentui/react-nav": "^9.3.14", - "@fluentui/react-overflow": "^9.6.5", - "@fluentui/react-persona": "^9.5.12", - "@fluentui/react-popover": "^9.12.12", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-positioning": "^9.20.10", - "@fluentui/react-progress": "^9.4.11", - "@fluentui/react-provider": "^9.22.11", - "@fluentui/react-radio": "^9.5.11", - "@fluentui/react-rating": "^9.3.11", - "@fluentui/react-search": "^9.3.11", - "@fluentui/react-select": "^9.4.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-skeleton": "^9.4.11", - "@fluentui/react-slider": "^9.5.11", - "@fluentui/react-spinbutton": "^9.5.11", - "@fluentui/react-spinner": "^9.7.11", - "@fluentui/react-swatch-picker": "^9.4.11", - "@fluentui/react-switch": "^9.4.11", - "@fluentui/react-table": "^9.19.5", - "@fluentui/react-tabs": "^9.10.7", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-tag-picker": "^9.7.12", - "@fluentui/react-tags": "^9.7.12", - "@fluentui/react-teaching-popover": "^9.6.12", - "@fluentui/react-text": "^9.6.11", - "@fluentui/react-textarea": "^9.6.11", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-toast": "^9.7.9", - "@fluentui/react-toolbar": "^9.6.12", - "@fluentui/react-tooltip": "^9.8.11", - "@fluentui/react-tree": "^9.15.6", - "@fluentui/react-utilities": "^9.25.4", - "@fluentui/react-virtualizer": "9.0.0-alpha.107", + "version": "9.73.8", + "resolved": "https://registry.npmjs.org/@fluentui/react-components/-/react-components-9.73.8.tgz", + "integrity": "sha512-JG4KQjEvRRfPlh4yt6Rv1/k87ydM2y49r5XPNCnuYHahA7kEM+dY8JdOI7n7FW8bdcvZ7qt4smDrQ2XcPfmxlA==", + "license": "MIT", + "dependencies": { + "@fluentui/react-accordion": "^9.11.0", + "@fluentui/react-alert": "9.0.0-beta.139", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-badge": "^9.5.2", + "@fluentui/react-breadcrumb": "^9.4.1", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-card": "^9.6.1", + "@fluentui/react-carousel": "^9.9.7", + "@fluentui/react-checkbox": "^9.6.1", + "@fluentui/react-color-picker": "^9.2.16", + "@fluentui/react-combobox": "^9.17.1", + "@fluentui/react-dialog": "^9.18.0", + "@fluentui/react-divider": "^9.7.1", + "@fluentui/react-drawer": "^9.12.0", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-image": "^9.4.1", + "@fluentui/react-infobutton": "9.0.0-beta.115", + "@fluentui/react-infolabel": "^9.4.20", + "@fluentui/react-input": "^9.8.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-link": "^9.8.1", + "@fluentui/react-list": "^9.6.14", + "@fluentui/react-menu": "^9.24.1", + "@fluentui/react-message-bar": "^9.7.0", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-nav": "^9.3.24", + "@fluentui/react-overflow": "^9.7.2", + "@fluentui/react-persona": "^9.7.3", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-progress": "^9.5.1", + "@fluentui/react-provider": "^9.22.16", + "@fluentui/react-radio": "^9.6.2", + "@fluentui/react-rating": "^9.4.1", + "@fluentui/react-search": "^9.4.2", + "@fluentui/react-select": "^9.5.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-skeleton": "^9.7.2", + "@fluentui/react-slider": "^9.6.2", + "@fluentui/react-spinbutton": "^9.6.2", + "@fluentui/react-spinner": "^9.8.2", + "@fluentui/react-swatch-picker": "^9.5.2", + "@fluentui/react-switch": "^9.7.2", + "@fluentui/react-table": "^9.19.15", + "@fluentui/react-tabs": "^9.12.1", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-tag-picker": "^9.8.6", + "@fluentui/react-tags": "^9.8.1", + "@fluentui/react-teaching-popover": "^9.6.21", + "@fluentui/react-text": "^9.6.16", + "@fluentui/react-textarea": "^9.7.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-toast": "^9.7.17", + "@fluentui/react-toolbar": "^9.8.0", + "@fluentui/react-tooltip": "^9.10.1", + "@fluentui/react-tree": "^9.16.0", + "@fluentui/react-utilities": "^9.26.3", + "@fluentui/react-virtualizer": "9.0.0-alpha.112", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1308,12 +1317,12 @@ } }, "node_modules/@fluentui/react-context-selector": { - "version": "9.2.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-context-selector/-/react-context-selector-9.2.12.tgz", - "integrity": "sha512-4hj+rv+4Uwn9EeDyXD1YCEpVkm0iMLG403QAGd5vZZhcgB2tg/iazewKeTff+HMRkusx+lWBYzBEGcRohY/FiA==", + "version": "9.2.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-context-selector/-/react-context-selector-9.2.16.tgz", + "integrity": "sha512-D+/X2liT+eZe0rzXbwddPH333ml2SXz71biR13aeyGJQr8+W+icMAIsYhpwk0CC3KtJ3f1/CLTm7vcIrvqsJ4g==", "license": "MIT", "dependencies": { - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-utilities": "^9.26.3", "@swc/helpers": "^0.5.1" }, "peerDependencies": { @@ -1325,23 +1334,23 @@ } }, "node_modules/@fluentui/react-dialog": { - "version": "9.16.3", - "resolved": "https://registry.npmjs.org/@fluentui/react-dialog/-/react-dialog-9.16.3.tgz", - "integrity": "sha512-aUnErTbSf2oqrqbQOCrjXp/12qHVfnxCR71/5hXJLME7BtYZ/m2lvs5r9MTjQSXBy8ar4G5jobS/+XJ0Lq3XqA==", + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-dialog/-/react-dialog-9.18.0.tgz", + "integrity": "sha512-i+V2o0NJ1itjVADJFov5AR/JetpD2hCMiLye0vfi3/XsFMgEPZnGzILVxPCO/ovULTiCyThcL1UvY0d/PYrZfA==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-motion": "^9.11.4", - "@fluentui/react-motion-components-preview": "^0.14.1", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1353,15 +1362,15 @@ } }, "node_modules/@fluentui/react-divider": { - "version": "9.4.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-divider/-/react-divider-9.4.11.tgz", - "integrity": "sha512-aESagOX6l7Ja9lb+3zJa6V5m1mjFnI4NEu8TccAu1VUlMZxX6flbMBJplgjN76dJjcHgs8uoa5xxxD74WNZBXg==", + "version": "9.7.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-divider/-/react-divider-9.7.1.tgz", + "integrity": "sha512-ptymE6iADb/ugezulaMeoAfGxKSwOjHEHBh8N1ydOR3AoOxsSUPkvoPC0mReO/yV5Nas7pz5s5VuJTspmFz0hA==", "license": "MIT", "dependencies": { - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1373,20 +1382,20 @@ } }, "node_modules/@fluentui/react-drawer": { - "version": "9.10.9", - "resolved": "https://registry.npmjs.org/@fluentui/react-drawer/-/react-drawer-9.10.9.tgz", - "integrity": "sha512-tlHZBkILOHnA7Lg2v/vzmOvTNrPYJnPJAqiceuFlUZWncIWWAUfpw4Teh5V0wGNr6/yC/HjUD5xnynvIhr/ZuA==", - "license": "MIT", - "dependencies": { - "@fluentui/react-dialog": "^9.16.3", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-motion": "^9.11.4", - "@fluentui/react-motion-components-preview": "^0.14.1", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-drawer/-/react-drawer-9.12.0.tgz", + "integrity": "sha512-PUXeXUH6JqwpjqYphHesHl75UAFSvxQJQqrevMFHE78ZF0Cqn59Xpa+8hGwRSuoRcYa90jjfHzJOOjN0iNM2iA==", + "license": "MIT", + "dependencies": { + "@fluentui/react-dialog": "^9.18.0", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1398,18 +1407,18 @@ } }, "node_modules/@fluentui/react-field": { - "version": "9.4.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-field/-/react-field-9.4.11.tgz", - "integrity": "sha512-kF93G+LGEKaFJcEAUHJKZUc1xeV/q+JTygYVnEDkPbQ/4j+l+J3rVuHL8U7bhE+8cJG3wDP8jt4jqHsDgKyn5w==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-field/-/react-field-9.5.1.tgz", + "integrity": "sha512-u8J2d3AWb4yZXvy/mQd95y2lTon890RfybBTCbeBUzApGMI/77WqT5pRJ+zTM3lOMToPHVKylchNFusMpJaX9w==", "license": "MIT", "dependencies": { - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-label": "^9.3.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1421,12 +1430,12 @@ } }, "node_modules/@fluentui/react-icons": { - "version": "2.0.315", - "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.315.tgz", - "integrity": "sha512-IITWAQGgU7I32eHPDHi+TUCUF6malP27wZLUV3bqjGVF/x/lfxvTIx8yqv/cxuwF3+ITGFDpl+278ZYJtOI7ww==", + "version": "2.0.324", + "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.324.tgz", + "integrity": "sha512-wbtIQWwoTWNU6KyuF59zZ1viFv1i68iwVa1+so/QnfNKNHIXa2MEZ375Vg/pcubFBqlTxsKMrCBFtHEIzBHG/Q==", "license": "MIT", "dependencies": { - "@griffel/react": "^1.0.0", + "@griffel/react": "^1.6.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -1434,15 +1443,15 @@ } }, "node_modules/@fluentui/react-image": { - "version": "9.3.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-image/-/react-image-9.3.11.tgz", - "integrity": "sha512-aLpz0/C6T0Uit6SmyhOJjYBvndZzfvmKv1vg+JRnE0aHS5jSUPoCLI6apxyMC6/LcqqTBklpqK3AD9kYpUxfqQ==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-image/-/react-image-9.4.1.tgz", + "integrity": "sha512-yNd2Wq2xq952UUEVBkWeEmM7bTKdWx6BnsHPYRf0kdTADox2PquApYXsI1xw2pnAh3GSjARrGi9Eto0qxouLqA==", "license": "MIT", "dependencies": { - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1454,18 +1463,18 @@ } }, "node_modules/@fluentui/react-infobutton": { - "version": "9.0.0-beta.107", - "resolved": "https://registry.npmjs.org/@fluentui/react-infobutton/-/react-infobutton-9.0.0-beta.107.tgz", - "integrity": "sha512-BcI4e+Oj1B/Qk4CMd0O9H0YF+IL4nhK8xuzI5bsZ5mdCaXiwIBgy5RyP8HVSq3y+Ml4XD2IRwufplcxF2cgTOA==", + "version": "9.0.0-beta.115", + "resolved": "https://registry.npmjs.org/@fluentui/react-infobutton/-/react-infobutton-9.0.0-beta.115.tgz", + "integrity": "sha512-b+4B0ODzPEb4jNaW9HdT6VVt3CL5FgPL2yuKzALBsYVl3udJdFpyxHsZEPf3JrVTBL/rgF2fRI1iAioX6Fl7DA==", "license": "MIT", "dependencies": { "@fluentui/react-icons": "^2.0.237", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-label": "^9.3.11", - "@fluentui/react-popover": "^9.12.12", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1477,19 +1486,19 @@ } }, "node_modules/@fluentui/react-infolabel": { - "version": "9.4.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-infolabel/-/react-infolabel-9.4.12.tgz", - "integrity": "sha512-inXlz5EAwQHKsGyB3wc5WmgQ1F9zc18x0HRd/otc2R7Oo1yRW5hXQCG5K5A9/wUge2pRiQVcBCsIurggmCNUhA==", + "version": "9.4.20", + "resolved": "https://registry.npmjs.org/@fluentui/react-infolabel/-/react-infolabel-9.4.20.tgz", + "integrity": "sha512-w4FOnNP+CtbVdKBEO6wXAcmOuPZWvmB/BJj+7J/8cLAQm7+4kQgitFHncU6rtFhPdGbikVoBf707/0R1mA4aIg==", "license": "MIT", "dependencies": { "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-label": "^9.3.11", - "@fluentui/react-popover": "^9.12.12", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1501,16 +1510,16 @@ } }, "node_modules/@fluentui/react-input": { - "version": "9.7.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-input/-/react-input-9.7.11.tgz", - "integrity": "sha512-ae/5ttJf25+J8akeEXpXRFqUAePPt2Moyfx4Tj0u7ZgG1U9IFbcBsshKEHAmIaygueXf6KdRyOduh1CF6a/D2w==", + "version": "9.8.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-input/-/react-input-9.8.2.tgz", + "integrity": "sha512-t9zmqZR4bqeRjpWuCGfI4yrtPoCXFiK2XO4BoV5nNwAesglgz4+Vtso4YXst9QYEAazHtKI73YFJf1mn55hCuA==", "license": "MIT", "dependencies": { - "@fluentui/react-field": "^9.4.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1522,14 +1531,13 @@ } }, "node_modules/@fluentui/react-jsx-runtime": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/@fluentui/react-jsx-runtime/-/react-jsx-runtime-9.3.3.tgz", - "integrity": "sha512-KOy85JqR6MSmp7OKUk/IPleaRlUSWF247AM2Ksu9uEKzDBQ2MO3sYUt8X9457GZjIuKLn5J2Vk127W/Khe+/Bg==", + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-jsx-runtime/-/react-jsx-runtime-9.4.2.tgz", + "integrity": "sha512-y3o0PBg2qzSdvgxDm7rH9BWq7E1h/eUWS+IhjQhd9dRpme6Py01+OLOglHojM5Tc9QjIp2Rjy2mFWBHXOR+8mw==", "license": "MIT", "dependencies": { - "@fluentui/react-utilities": "^9.25.4", - "@swc/helpers": "^0.5.1", - "react-is": "^17.0.2" + "@fluentui/react-utilities": "^9.26.3", + "@swc/helpers": "^0.5.1" }, "peerDependencies": { "@types/react": ">=16.14.0 <20.0.0", @@ -1537,15 +1545,15 @@ } }, "node_modules/@fluentui/react-label": { - "version": "9.3.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-label/-/react-label-9.3.11.tgz", - "integrity": "sha512-9LORj4JQJCbp2J5ftW7ZjDxzD3Y4BkszX3Y7L1mK8DPRVAKOuGiakbH7U0q7ClGOMhCinWIQJjKAwzPZLo7xgA==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-label/-/react-label-9.4.1.tgz", + "integrity": "sha512-4O3cPX6dSJVBKlIEbznjJ08utEc98lKbZz/6MZTTQfFgYl0TxAhxEDsIIIyNjj0Xy9eJpqubJsaswucWXTG/qg==", "license": "MIT", "dependencies": { - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1557,17 +1565,17 @@ } }, "node_modules/@fluentui/react-link": { - "version": "9.7.0", - "resolved": "https://registry.npmjs.org/@fluentui/react-link/-/react-link-9.7.0.tgz", - "integrity": "sha512-NQ5Jhe5WBYfANSmIcl6fE/oBeh7G4iAq1FU9L/hyva5dxQ9OtiOpU5wxqVFLKEID/r144rhdtOZPL5AcAuJKdg==", + "version": "9.8.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-link/-/react-link-9.8.1.tgz", + "integrity": "sha512-ZxrCeX4pMWHujdmYV8b0QW0ztLtu0rHHvRNx67Y3WqSijVyij8QtNNiZ/nab+UDNlz9t8QIXKdWQgYj1uKDpMg==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1579,19 +1587,19 @@ } }, "node_modules/@fluentui/react-list": { - "version": "9.6.6", - "resolved": "https://registry.npmjs.org/@fluentui/react-list/-/react-list-9.6.6.tgz", - "integrity": "sha512-t0ret56WXP86rDfnhuRrWg/DuS2zZkomB/Nu444rVygE8hsjPUTm5DXx7JKy+sGKVLyFbtsbXNMICkbxhGSSRA==", + "version": "9.6.14", + "resolved": "https://registry.npmjs.org/@fluentui/react-list/-/react-list-9.6.14.tgz", + "integrity": "sha512-B1mUQFvJOUlZysSduVnATNZggrGpgEWnW9ZSJAZ17LM0+9nWEQRi40jpUGI/d3PGKHt5O2df78s+1nEPAk0L6A==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-checkbox": "^9.5.11", - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-checkbox": "^9.6.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1603,22 +1611,24 @@ } }, "node_modules/@fluentui/react-menu": { - "version": "9.20.5", - "resolved": "https://registry.npmjs.org/@fluentui/react-menu/-/react-menu-9.20.5.tgz", - "integrity": "sha512-vshb/OXBZxvk+ghdmdVb2mJ/LJBYjlwpZRhWGJ8ZU0hmPTh74m5jTFWditSk8aL9oMvVuIo0MYLQyUJyJsFoqg==", + "version": "9.24.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-menu/-/react-menu-9.24.1.tgz", + "integrity": "sha512-NLB5EhzKFiwax3O5JTRTtsqdEFDGEXzEuP/suyxNAaaQsIuXygo//Rmdq6dSn7GybTpEOZHKxYDyyG7dj+a4YA==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-positioning": "^9.20.10", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1630,20 +1640,20 @@ } }, "node_modules/@fluentui/react-message-bar": { - "version": "9.6.14", - "resolved": "https://registry.npmjs.org/@fluentui/react-message-bar/-/react-message-bar-9.6.14.tgz", - "integrity": "sha512-UR4Uvkx4VHQyS04T5ikf9gYOH52dloo1vjmK+pFKiqRzZhflHEXID9R1AZFuuZ572KUMXnxRlyEevpXnWqE70w==", + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-message-bar/-/react-message-bar-9.7.0.tgz", + "integrity": "sha512-ICFDxZ62r5OG97/FcfK1EfJPxGlyDNyFixLD/a3gOREvEcT/hyZgnlUM9Y30u92HjxChx2SwGWnv3iaQPsvToQ==", "license": "MIT", "dependencies": { - "@fluentui/react-button": "^9.6.12", + "@fluentui/react-button": "^9.9.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-link": "^9.7.0", - "@fluentui/react-motion": "^9.11.4", - "@fluentui/react-motion-components-preview": "^0.14.1", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-link": "^9.8.1", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1655,13 +1665,13 @@ } }, "node_modules/@fluentui/react-motion": { - "version": "9.11.4", - "resolved": "https://registry.npmjs.org/@fluentui/react-motion/-/react-motion-9.11.4.tgz", - "integrity": "sha512-rLxz6DSAtp3O+W+mJnov2qXtvZkIgcC1BQOAyUH6tl6u2YmsC1/zRKWhVsf/WUgZwqu3G4jlq15ptyuCITAcDA==", + "version": "9.15.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-motion/-/react-motion-9.15.0.tgz", + "integrity": "sha512-ZNQHYzE6MRbLQFT08/mrcqQ9k7F5niktRP93X1v/kmwKfPjvdDofySfbhQXQs3zQw600690C9rfJTKUd3h+zlg==", "license": "MIT", "dependencies": { - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-utilities": "^9.26.3", "@swc/helpers": "^0.5.1" }, "peerDependencies": { @@ -1672,9 +1682,9 @@ } }, "node_modules/@fluentui/react-motion-components-preview": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/@fluentui/react-motion-components-preview/-/react-motion-components-preview-0.14.1.tgz", - "integrity": "sha512-+2MK7d2g3mD+6Z3o9/fitO+V4u5OKGeRUoUjwlU1v56JHP43hj+NCJynoe4Cym8FeSwTyipks6hvdqBF4W+jtw==", + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/@fluentui/react-motion-components-preview/-/react-motion-components-preview-0.15.4.tgz", + "integrity": "sha512-gAHPlyEYylZzUSGwc68VaB+vO8CTX6tgIA3d2+jFrpcwvXZjsdCpF1w1zK1+hTuiipmEaZLZyBz0e0CKH2+3XQ==", "license": "MIT", "dependencies": { "@fluentui/react-motion": "*", @@ -1689,25 +1699,25 @@ } }, "node_modules/@fluentui/react-nav": { - "version": "9.3.14", - "resolved": "https://registry.npmjs.org/@fluentui/react-nav/-/react-nav-9.3.14.tgz", - "integrity": "sha512-0Lylul5g/9y3Cay5qHLtzW4SB9kdkTmvjHSffPJZDKE/Wv7GBbDypBxoB+f2L1K4f0qzRJ1NvIiwatH4hAsUDA==", + "version": "9.3.24", + "resolved": "https://registry.npmjs.org/@fluentui/react-nav/-/react-nav-9.3.24.tgz", + "integrity": "sha512-OlB5k5Zev5VNjSRfJvJLO09Hjcv2UHAjLpSVa6gKHx+1NqqSJWZeDLSF7r+/nyZ4CWP5jWZYq7whEu3WvzdVZw==", "license": "MIT", "dependencies": { - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-button": "^9.6.12", - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-divider": "^9.4.11", - "@fluentui/react-drawer": "^9.10.9", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-divider": "^9.7.1", + "@fluentui/react-drawer": "^9.12.0", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-motion": "^9.11.4", - "@fluentui/react-motion-components-preview": "^0.14.1", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-tooltip": "^9.8.11", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-tooltip": "^9.10.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1719,15 +1729,15 @@ } }, "node_modules/@fluentui/react-overflow": { - "version": "9.6.5", - "resolved": "https://registry.npmjs.org/@fluentui/react-overflow/-/react-overflow-9.6.5.tgz", - "integrity": "sha512-4MlXASDodkwk4QWhUPLgMbUPwDYAOAWDnQGJz4q6Hs9eZvx83dSpWdWjkmQ6mwjYf2HwooMkqsjR/kAFvg+ipg==", + "version": "9.7.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-overflow/-/react-overflow-9.7.2.tgz", + "integrity": "sha512-5PA67LgnVmbbOzBN2H5gH3OvSVy1373VJfsHq2+6TLCfm+LXAkWBoFwvBuFI7HsMYae9A0FVlgX7gTsKVfMddw==", "license": "MIT", "dependencies": { - "@fluentui/priority-overflow": "^9.2.1", - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/priority-overflow": "^9.3.0", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1739,17 +1749,17 @@ } }, "node_modules/@fluentui/react-persona": { - "version": "9.5.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-persona/-/react-persona-9.5.12.tgz", - "integrity": "sha512-ja3t1o6XDJWCJnOVDTM48G7bFPAbNxcsQKwAPfiuROVu8ODbTQefutCHl0Hno40AsftQk6N4zGbKcn7BYSZ09Q==", + "version": "9.7.3", + "resolved": "https://registry.npmjs.org/@fluentui/react-persona/-/react-persona-9.7.3.tgz", + "integrity": "sha512-OY3xpSD6l4NDdeKihriC+H0q6P1CA2xyZ+pe/WwfKPnatxs2BALoRFtDQduMO7AK/j0w7UAxnaZrvEeftLen2g==", "license": "MIT", "dependencies": { - "@fluentui/react-avatar": "^9.9.12", - "@fluentui/react-badge": "^9.4.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-badge": "^9.5.2", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1761,21 +1771,23 @@ } }, "node_modules/@fluentui/react-popover": { - "version": "9.12.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-popover/-/react-popover-9.12.12.tgz", - "integrity": "sha512-IytuasB4b4lCnEhFC0OC66a3mzBSePLpg78/BceKYepuG7IC6iGuCwYartqSQCSUlSU12rT02/V0rqCO81f4Nw==", + "version": "9.14.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-popover/-/react-popover-9.14.2.tgz", + "integrity": "sha512-EDvzLkT98/vcCSGrcZWUACGsvLjrHin0Xf9eowMQKiiHFWbu8HNRmr7W2XB9Eja1W5HSIK6+mV8ro9zrLibG4w==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-positioning": "^9.20.10", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1787,14 +1799,14 @@ } }, "node_modules/@fluentui/react-portal": { - "version": "9.8.8", - "resolved": "https://registry.npmjs.org/@fluentui/react-portal/-/react-portal-9.8.8.tgz", - "integrity": "sha512-RVvhWYfcwIUYXiokgFw3oxb7Q6xox2e7jcsgFtheDm2X/BHT6WJigW4OaCjOkvugkBEYQkwgIpL9iS2QG3HMFA==", + "version": "9.8.12", + "resolved": "https://registry.npmjs.org/@fluentui/react-portal/-/react-portal-9.8.12.tgz", + "integrity": "sha512-+WH0wH/5lsodGP6Mam1alHXpkMCYA5uMcnF98RVOs7/GR69KiFcza1mCnvPJUaJ55AfwLuz/xLxuWdWgQnUdMQ==", "license": "MIT", "dependencies": { - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1806,16 +1818,16 @@ } }, "node_modules/@fluentui/react-positioning": { - "version": "9.20.10", - "resolved": "https://registry.npmjs.org/@fluentui/react-positioning/-/react-positioning-9.20.10.tgz", - "integrity": "sha512-mjuiqh+urV5SzAP2NfzUzsvtWut0aNcO9m/jIuz374iTVGRfDNeVIl7aPI4yK5sdCDR6dGALiNMTFHpjz1YXyw==", + "version": "9.22.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-positioning/-/react-positioning-9.22.1.tgz", + "integrity": "sha512-/r1BHQKr/WCjEM8UGloiq7bWWBSYB/Uqt7D1sAF9EHd968VH07cAN3RMVKmWWjeJO31rstOZHdgcz0WHhFF+2Q==", "license": "MIT", "dependencies": { "@floating-ui/devtools": "^0.2.3", "@floating-ui/dom": "^1.6.12", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1", "use-sync-external-store": "^1.2.0" @@ -1828,16 +1840,17 @@ } }, "node_modules/@fluentui/react-progress": { - "version": "9.4.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-progress/-/react-progress-9.4.11.tgz", - "integrity": "sha512-L0Yh2D0vLPJX0jYfc9VHf8c/idW+e/oRxYNXfrTrvtW1bX80bAmrXWgdRPr/VEtvbJh//2ol2TRmTTQsn2ECNQ==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-progress/-/react-progress-9.5.1.tgz", + "integrity": "sha512-EXJ/Bp67d5+bXPNpPabxdtXUgCMTtvYrBoKtIS6wE5KeUzaek/rgQ3v5wnGfbuLnJ4J/kj+n7XQEc9fhoFPy9w==", "license": "MIT", "dependencies": { - "@fluentui/react-field": "^9.4.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1849,17 +1862,17 @@ } }, "node_modules/@fluentui/react-provider": { - "version": "9.22.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-provider/-/react-provider-9.22.11.tgz", - "integrity": "sha512-XrinA7DVEqsPHeN9XljwTENiIQypiF9cmDYXHN9Emsz6Od4hnmsbt4pnR4Xsf+GcSxVtxkIImfgwtS0aENzYbQ==", + "version": "9.22.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-provider/-/react-provider-9.22.16.tgz", + "integrity": "sha512-S77n5ASUWE/V1I6lX09CrHm4TAKSGENhIrKz9qMKDv2Vrq44/j3eGBLz12k8IW4TJVu9nwGwst9kBpCT+3WHpA==", "license": "MIT", "dependencies": { "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/core": "^1.16.0", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" @@ -1872,18 +1885,18 @@ } }, "node_modules/@fluentui/react-radio": { - "version": "9.5.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-radio/-/react-radio-9.5.11.tgz", - "integrity": "sha512-tMxCcqRSSYqYr6hy1dKkzS6LymRc8wM089vr4eBLPQCGCvi3OCd6P7XH8aIcXnzxE3+v03Gs7E/wbzi2CXN6gA==", - "license": "MIT", - "dependencies": { - "@fluentui/react-field": "^9.4.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-label": "^9.3.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-radio/-/react-radio-9.6.2.tgz", + "integrity": "sha512-Sp2us4eWRopUKOMCQw5/iks7euPKY6FeesBCCUIVGBg5VKZf2/CfEtbCa9hMjn4D4PCHGivnUTf23t238mvvnw==", + "license": "MIT", + "dependencies": { + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1895,17 +1908,17 @@ } }, "node_modules/@fluentui/react-rating": { - "version": "9.3.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-rating/-/react-rating-9.3.11.tgz", - "integrity": "sha512-9Bl/sESNbFTbz8peGt9vxLxHDO0AWvS12oMiQ80S1GQOt1ua4S9/SKC83OvyVLEdpBDpBkXTelNz5whczcWexQ==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-rating/-/react-rating-9.4.1.tgz", + "integrity": "sha512-DfWipzrT44j+yaShtfHz96/vHEa5ut5IR1kobrO0bSqAcpetOn327gFeY+sG/W6xzork/STcy/T836yK8A2+DQ==", "license": "MIT", "dependencies": { "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1917,17 +1930,17 @@ } }, "node_modules/@fluentui/react-search": { - "version": "9.3.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-search/-/react-search-9.3.11.tgz", - "integrity": "sha512-inLoPgbGnupfwhBxFS59mF/ThsntusfYp9TaaTB3SJmqfEEx6YXi5soxszzrXsNvrqpgEoCGIduRpEICuUz5pw==", + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-search/-/react-search-9.4.2.tgz", + "integrity": "sha512-PIb50euHoMsKWLqFymf8wo/+z1jrx1MB7uNuhjNT5DvwTP4VYAy5EtRCSwVRyxWNSaWSL6iy6dDy517EQE96mA==", "license": "MIT", "dependencies": { "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-input": "^9.7.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-input": "^9.8.2", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1939,17 +1952,17 @@ } }, "node_modules/@fluentui/react-select": { - "version": "9.4.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-select/-/react-select-9.4.11.tgz", - "integrity": "sha512-/mcdl/lkKccT+GKXu22y2/ANeLhFNUdjkOX+0rvBdl3u49xkqS9Y4Bi0zM1EhhTV2jE8+yjMjzPDzfzJaXVK1A==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-select/-/react-select-9.5.1.tgz", + "integrity": "sha512-8GocQKiUHEUlAks6zA0HbGGSF2lpjuSZuxPzIBqTyuWof8vFiK6eFAcSXb0hTYIVH3RsTihhfc6G3NRnHoBrzg==", "license": "MIT", "dependencies": { - "@fluentui/react-field": "^9.4.11", + "@fluentui/react-field": "^9.5.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1961,12 +1974,12 @@ } }, "node_modules/@fluentui/react-shared-contexts": { - "version": "9.26.0", - "resolved": "https://registry.npmjs.org/@fluentui/react-shared-contexts/-/react-shared-contexts-9.26.0.tgz", - "integrity": "sha512-r52B+LUevs930pe45pFsppM9XNvY+ojgRgnDE+T/6aiwR/Mo4YoGrtjhLEzlQBeTGuySICTeaAiXfuH6Keo5Dg==", + "version": "9.26.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-shared-contexts/-/react-shared-contexts-9.26.2.tgz", + "integrity": "sha512-upKXkwlIp5oIhELr4clAZXQkuCd4GDXM6GZEz8BOmRO+PnxyqmycCXvxDxsmi6XN+0vkGM4joiIgkB14o/FctQ==", "license": "MIT", "dependencies": { - "@fluentui/react-theme": "^9.2.0", + "@fluentui/react-theme": "^9.2.1", "@swc/helpers": "^0.5.1" }, "peerDependencies": { @@ -1975,16 +1988,16 @@ } }, "node_modules/@fluentui/react-skeleton": { - "version": "9.4.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-skeleton/-/react-skeleton-9.4.11.tgz", - "integrity": "sha512-nw6NlTBXS7lNSxsebLuADYQi9gJ83jFBFsFq+AGIpAoZLBOCHOhk8/XwV3vYtPwVrKcZtOtXqh9NdCqTR3OAIA==", + "version": "9.7.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-skeleton/-/react-skeleton-9.7.2.tgz", + "integrity": "sha512-PrUgdSGDAZw9FIP5NyvPoPfHDe2N9VxMyBfyTwWfZVg03dzRfnE3vEqr7N5xyfv4JsRs6u1xSqVn/0jdS0IEMQ==", "license": "MIT", "dependencies": { - "@fluentui/react-field": "^9.4.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -1996,17 +2009,17 @@ } }, "node_modules/@fluentui/react-slider": { - "version": "9.5.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-slider/-/react-slider-9.5.11.tgz", - "integrity": "sha512-kxZKklJbcG/521muQaIDMdcftoClbwV7yMOcu8PMG+VXsaIuoandoBleBYdzM2XdpY62iK6vUPAMZWBZh3B5Ng==", + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-slider/-/react-slider-9.6.2.tgz", + "integrity": "sha512-lVavtTg8eqovfRokeYDk4popwCi8nuicacJ/HZdF3ni5e3y/2WT/bVP0eErS/GvC7+90ACQQ8uxdr4sjjY/HWA==", "license": "MIT", "dependencies": { - "@fluentui/react-field": "^9.4.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2018,18 +2031,18 @@ } }, "node_modules/@fluentui/react-spinbutton": { - "version": "9.5.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-spinbutton/-/react-spinbutton-9.5.11.tgz", - "integrity": "sha512-pYR3RkJfks+0WV47KoDKD04D0pTHtT+lu3AeOpBlIswxtsb1gZEDmTrEHHNeLDKKVhWMWNoEPlxfXuX9tOh7yA==", + "version": "9.6.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-spinbutton/-/react-spinbutton-9.6.2.tgz", + "integrity": "sha512-P4vvJH7P5yHPFAv6aSo3dZxtErN62DiRJN+nEKS+/XBoRGsOGQdqyyx5Q/PQKOmyQrtwuZdXNHUjcyv8b50T2w==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-field": "^9.4.11", + "@fluentui/react-field": "^9.5.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2041,16 +2054,16 @@ } }, "node_modules/@fluentui/react-spinner": { - "version": "9.7.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-spinner/-/react-spinner-9.7.11.tgz", - "integrity": "sha512-MhmAisICa3BzBNQH9CnLI5NVPTTXFo1Yaey8kbQPU+gVVF4vIGORB7M1MXSHFxZvojtFpBixiVHqRwh9mowJww==", + "version": "9.8.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-spinner/-/react-spinner-9.8.2.tgz", + "integrity": "sha512-0LxykLJGUD/I3XEeIXAWznwdg9XRe0piaByR0nLFOOV3UPwkVc2w5UdPhy2Y0NZDvtPHbNaMCuQAq82+bxg/0w==", "license": "MIT", "dependencies": { - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-label": "^9.3.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2062,19 +2075,19 @@ } }, "node_modules/@fluentui/react-swatch-picker": { - "version": "9.4.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-swatch-picker/-/react-swatch-picker-9.4.11.tgz", - "integrity": "sha512-M/ZfHqo63F69y2ymEQDDN/BZuI3afeW3U+omyGZZoHts3rVCjPk6sKFemTRpUhGgGfxBdexWEPithZx3dk0IPA==", + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-swatch-picker/-/react-swatch-picker-9.5.2.tgz", + "integrity": "sha512-DK6UU9OJY9XaGBPU2ROx+B5/7XdwVtHBdVthOAptyKSsYGOdQt5AQqg3ZOXH6r5WYbMRQDuP2OZ2iKtwidFCVQ==", "license": "MIT", "dependencies": { - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-field": "^9.4.11", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-field": "^9.5.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2086,19 +2099,19 @@ } }, "node_modules/@fluentui/react-switch": { - "version": "9.4.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-switch/-/react-switch-9.4.11.tgz", - "integrity": "sha512-/WDcoVFQ3I2fe5FTINfyVTIW6wuTgM5QkJgcwbU7HTANq/+wJ2f8wzywoI4x16cJOckBdy+ByDpW7uJ/Uvs8RA==", + "version": "9.7.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-switch/-/react-switch-9.7.2.tgz", + "integrity": "sha512-j3e5se+3d+befV9MytkxxvJ9nHZOeZ7thKDTF4YVSYf6kcNx9eOlLvPgDjhGO08gzngO4B7aaprhDN7DJc3W1g==", "license": "MIT", "dependencies": { - "@fluentui/react-field": "^9.4.11", + "@fluentui/react-field": "^9.5.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-label": "^9.3.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-label": "^9.4.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2110,23 +2123,23 @@ } }, "node_modules/@fluentui/react-table": { - "version": "9.19.5", - "resolved": "https://registry.npmjs.org/@fluentui/react-table/-/react-table-9.19.5.tgz", - "integrity": "sha512-In9egEdytjFd6N1RBZd5+3UgdXvEVDP7rz+/I79J10ui2+Nb7r9ah68m5CQB15AKA8F5XFDWPEGvGG3Tmuq4Jg==", + "version": "9.19.15", + "resolved": "https://registry.npmjs.org/@fluentui/react-table/-/react-table-9.19.15.tgz", + "integrity": "sha512-OdQ2Nwx2nAlPMlJeyAFrKa3Zy5Ya/H87OU8MvtFJhabM/FkHiZoli/DO1mavVI+jqavOlJuQWmJ55D6jjuGa7g==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-avatar": "^9.9.12", - "@fluentui/react-checkbox": "^9.5.11", - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-checkbox": "^9.6.1", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-radio": "^9.5.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-radio": "^9.6.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2138,17 +2151,17 @@ } }, "node_modules/@fluentui/react-tabs": { - "version": "9.10.7", - "resolved": "https://registry.npmjs.org/@fluentui/react-tabs/-/react-tabs-9.10.7.tgz", - "integrity": "sha512-Kfq6GxZXEKsMdGKmHWNMcEYOYHxl5+fXJOH6ZRgeR2FkHUsPUUe2BHaFnOMRSvCwzECvhOMYs+Ekqt7JzW3BWQ==", + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-tabs/-/react-tabs-9.12.1.tgz", + "integrity": "sha512-WvzOtpC6C/7Mo5X+xmE+3stpCbx2iH9BqrEN5KuGrsHJ78DjMDeabYeL90vlrHBdP4VlTpwdORBui/jtWkxnmQ==", "license": "MIT", "dependencies": { - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2160,14 +2173,14 @@ } }, "node_modules/@fluentui/react-tabster": { - "version": "9.26.10", - "resolved": "https://registry.npmjs.org/@fluentui/react-tabster/-/react-tabster-9.26.10.tgz", - "integrity": "sha512-KrddtwbnbgYVAnOkx1pQsMMgq7Kfi+lMRrUrDDJ9Y5X6wiXiajbWRRxYgKiOJc3MpeDCaTCEtjOWNG92vcinMw==", + "version": "9.26.14", + "resolved": "https://registry.npmjs.org/@fluentui/react-tabster/-/react-tabster-9.26.14.tgz", + "integrity": "sha512-WibgoF67hl6BXfmsY6RSIWSHadeMP/6EDG9gAacfHlwKvK0+FiHp5ernwuXTPAmu2kiHicn2qUZ8EteCFiFryg==", "license": "MIT", "dependencies": { - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1", "keyborg": "^2.6.0", @@ -2181,25 +2194,25 @@ } }, "node_modules/@fluentui/react-tag-picker": { - "version": "9.7.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-tag-picker/-/react-tag-picker-9.7.12.tgz", - "integrity": "sha512-OJucCDub6b3ceGL6v2UXL+SD3x6nJMbmJ70v38BmrA9t3fNcDvn6RnsfHhF2O0pRGGUOrXbK7vDwVhUAG4Py8w==", + "version": "9.8.6", + "resolved": "https://registry.npmjs.org/@fluentui/react-tag-picker/-/react-tag-picker-9.8.6.tgz", + "integrity": "sha512-sOZ+wBA3hgGhKrOP7wbjB2yRvAxjcRXtcj1jDTrtSkaDPXb3K0nGmjiqp2mve995ps3wvCGnNKK4EurX842ZbA==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-combobox": "^9.16.12", - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-field": "^9.4.11", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-combobox": "^9.17.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-field": "^9.5.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-positioning": "^9.20.10", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-tags": "^9.7.12", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-tags": "^9.8.1", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2211,20 +2224,20 @@ } }, "node_modules/@fluentui/react-tags": { - "version": "9.7.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-tags/-/react-tags-9.7.12.tgz", - "integrity": "sha512-G7pxP0GGa6J/7mYvB9ycOmD9Jpm6ByUz6JsJI4OBL9UnhenUVTtE7ZKJ9GJ0SiG0GVxS152aSlOR7NLHV7mCqw==", + "version": "9.8.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-tags/-/react-tags-9.8.1.tgz", + "integrity": "sha512-6ZTW78fu5eWByKHIM3i+raDrX3hwfZ67ONfZ8wEUXfZHowskxqpMHI8Gw7IAMWkC1scgLqEnht8TnHvZgjo7Ug==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-avatar": "^9.9.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-avatar": "^9.11.1", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2236,21 +2249,21 @@ } }, "node_modules/@fluentui/react-teaching-popover": { - "version": "9.6.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-teaching-popover/-/react-teaching-popover-9.6.12.tgz", - "integrity": "sha512-Ugo5SQ3yzSlxUWkeeEdumTWTw662KDh3UPc6RGhU0Jq13skpmsClSJL678BZwsYdAaJXvvG9Bi4PjPeezeB/SA==", + "version": "9.6.21", + "resolved": "https://registry.npmjs.org/@fluentui/react-teaching-popover/-/react-teaching-popover-9.6.21.tgz", + "integrity": "sha512-V86zLB1B8xu3U/02FvvMdsJP+ZC9l3vT9bQ2Gr7hZHxJ4/0NLpVcrYSBFHpON9e/WK3p+A5b5V96p86b5Pavlg==", "license": "MIT", "dependencies": { - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-button": "^9.6.12", - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-popover": "^9.12.12", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-popover": "^9.14.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1", "use-sync-external-store": "^1.2.0" @@ -2263,15 +2276,15 @@ } }, "node_modules/@fluentui/react-text": { - "version": "9.6.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-text/-/react-text-9.6.11.tgz", - "integrity": "sha512-U7EiCesOWjkALf7LM6sy+yvE59Px3c6f27jg4aa21UMo61HCVNbjKV8Lz6GzEftEvv++/EZ25yZBiQcKgh/5iA==", + "version": "9.6.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-text/-/react-text-9.6.16.tgz", + "integrity": "sha512-ZzCSJWQ6LrVuPqA6sqNEZaXbLvhi2NxBOtlMudWlqYzidLQp038d7mMGSzNnhyeblg+gj+bOVE2eOgWFuVHGYw==", "license": "MIT", "dependencies": { - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2283,16 +2296,16 @@ } }, "node_modules/@fluentui/react-textarea": { - "version": "9.6.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-textarea/-/react-textarea-9.6.11.tgz", - "integrity": "sha512-5ds8u8hzSqj8cOy0e7HJWjUMq1aO0MIJiaNt/SyIxoZFvsklj/2yaMRVXpWxr3GvX5bzScvFoBY53gPdLKtE/g==", + "version": "9.7.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-textarea/-/react-textarea-9.7.2.tgz", + "integrity": "sha512-awlkZoW81WaOqSoXTT9rZs3mTAzCCHnC9eAm6J8ZxI5+ASX07BTolBfZ82it5wxOHI5GMDfbFOl+xIy8uAMdzA==", "license": "MIT", "dependencies": { - "@fluentui/react-field": "^9.4.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-field": "^9.5.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2304,32 +2317,32 @@ } }, "node_modules/@fluentui/react-theme": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@fluentui/react-theme/-/react-theme-9.2.0.tgz", - "integrity": "sha512-Q0zp/MY1m5RjlkcwMcjn/PQRT2T+q3bgxuxWbhgaD07V+tLzBhGROvuqbsdg4YWF/IK21zPfLhmGyifhEu0DnQ==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-theme/-/react-theme-9.2.1.tgz", + "integrity": "sha512-lJxfz7LmmglFz+c9C41qmMqaRRZZUPtPPl9DWQ79vH+JwZd4dkN7eA78OTRwcGCOTPEKoLTX72R+EFaWEDlX+w==", "license": "MIT", "dependencies": { - "@fluentui/tokens": "1.0.0-alpha.22", + "@fluentui/tokens": "1.0.0-alpha.23", "@swc/helpers": "^0.5.1" } }, "node_modules/@fluentui/react-toast": { - "version": "9.7.9", - "resolved": "https://registry.npmjs.org/@fluentui/react-toast/-/react-toast-9.7.9.tgz", - "integrity": "sha512-PaFh2CwVK4tgvRzBMb46ODHsB+ZYSYE8mx735vqgIG8Oj1AL3wZ5Y9TrjJGxn/lppZgtnwLgt4GQ+GI7MM+e+g==", + "version": "9.7.17", + "resolved": "https://registry.npmjs.org/@fluentui/react-toast/-/react-toast-9.7.17.tgz", + "integrity": "sha512-DWA5EARWSo1k19iWAulLpKrcUHT+Dq/Bw9zfdpoQEWWybrAZwyN7WiYFkBjKCQRxSp10OjLASSQK91CXfb1wJA==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", + "@fluentui/react-aria": "^9.17.11", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-motion": "^9.11.4", - "@fluentui/react-motion-components-preview": "^0.14.1", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2341,20 +2354,20 @@ } }, "node_modules/@fluentui/react-toolbar": { - "version": "9.6.12", - "resolved": "https://registry.npmjs.org/@fluentui/react-toolbar/-/react-toolbar-9.6.12.tgz", - "integrity": "sha512-AuOZvp6Jcc/Sngk0OddTsHlJVU/u9mVEw6JDhsCYiwKeq04kdgfco1sjSTGjDhJbf1SnkhmyR6YN16SrpVQWtA==", - "license": "MIT", - "dependencies": { - "@fluentui/react-button": "^9.6.12", - "@fluentui/react-context-selector": "^9.2.12", - "@fluentui/react-divider": "^9.4.11", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-radio": "^9.5.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-toolbar/-/react-toolbar-9.8.0.tgz", + "integrity": "sha512-EIe+QWOaFR1pZzENefsFTmjxGa2yJb4A/by3kGuGqSjx7isqPUllPq0/kFQzfoUYgPDJbJtQ+KyuRDekTL0QpQ==", + "license": "MIT", + "dependencies": { + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-context-selector": "^9.2.16", + "@fluentui/react-divider": "^9.7.1", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-radio": "^9.6.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2366,19 +2379,19 @@ } }, "node_modules/@fluentui/react-tooltip": { - "version": "9.8.11", - "resolved": "https://registry.npmjs.org/@fluentui/react-tooltip/-/react-tooltip-9.8.11.tgz", - "integrity": "sha512-ke7Hbom3dtC3f9QjJG/F7QfNfukwTtAhoYLmwwQnXYTh/CIVxoC2rVh4c/V8jUD0lnjNPBZZ5ttVUopWljHuFg==", + "version": "9.10.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-tooltip/-/react-tooltip-9.10.1.tgz", + "integrity": "sha512-IPHBFjqGhaaMDhLt5NSNOE9LEpDOpT7qgEqNz+Mlflo0A4qI2LW/EnkNop7IRmX/bC88A+wUtEONTjjR87dNBw==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-portal": "^9.8.8", - "@fluentui/react-positioning": "^9.20.10", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-portal": "^9.8.12", + "@fluentui/react-positioning": "^9.22.1", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2390,26 +2403,26 @@ } }, "node_modules/@fluentui/react-tree": { - "version": "9.15.6", - "resolved": "https://registry.npmjs.org/@fluentui/react-tree/-/react-tree-9.15.6.tgz", - "integrity": "sha512-L/uc+SgwXW8DXgSZsyIg5tQkixfrGllANg0I2578WRlfOkERehkg1eSW8Uib/Mbk+W3tB0I8CL20ifoSTL7Ztw==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-tree/-/react-tree-9.16.0.tgz", + "integrity": "sha512-c+Q4AVaYk9U69aGDgmJVNne+CtWKS75YIfGoxs6+9+wE2Wqz4T0E+gE1ng7ARCQQgI7E2NEJlot6DuI6nYYrRw==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-aria": "^9.17.6", - "@fluentui/react-avatar": "^9.9.12", - "@fluentui/react-button": "^9.6.12", - "@fluentui/react-checkbox": "^9.5.11", - "@fluentui/react-context-selector": "^9.2.12", + "@fluentui/react-aria": "^9.17.11", + "@fluentui/react-avatar": "^9.11.1", + "@fluentui/react-button": "^9.9.1", + "@fluentui/react-checkbox": "^9.6.1", + "@fluentui/react-context-selector": "^9.2.16", "@fluentui/react-icons": "^2.0.245", - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-motion": "^9.11.4", - "@fluentui/react-motion-components-preview": "^0.14.1", - "@fluentui/react-radio": "^9.5.11", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-tabster": "^9.26.10", - "@fluentui/react-theme": "^9.2.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-motion": "^9.15.0", + "@fluentui/react-motion-components-preview": "^0.15.4", + "@fluentui/react-radio": "^9.6.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-tabster": "^9.26.14", + "@fluentui/react-theme": "^9.2.1", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2421,13 +2434,13 @@ } }, "node_modules/@fluentui/react-utilities": { - "version": "9.25.4", - "resolved": "https://registry.npmjs.org/@fluentui/react-utilities/-/react-utilities-9.25.4.tgz", - "integrity": "sha512-vvEIFTfqkcBnKNJhlm8csdGNtOWDWDkqAM4tGlW7jLlFrhNkOfDsqdNuBElENPNJ1foHyVTF5ZSr20kVoKWPjQ==", + "version": "9.26.3", + "resolved": "https://registry.npmjs.org/@fluentui/react-utilities/-/react-utilities-9.26.3.tgz", + "integrity": "sha512-bXB3jMm/RroT8c5eGZkijkPbLd4MqMI6biBHjavo0e7OkZHv9IPfH2nDkGhSn5Sh8e6kRcX0IjYhbM10WUK2iQ==", "license": "MIT", "dependencies": { "@fluentui/keyboard-keys": "^9.0.8", - "@fluentui/react-shared-contexts": "^9.26.0", + "@fluentui/react-shared-contexts": "^9.26.2", "@swc/helpers": "^0.5.1" }, "peerDependencies": { @@ -2436,14 +2449,14 @@ } }, "node_modules/@fluentui/react-virtualizer": { - "version": "9.0.0-alpha.107", - "resolved": "https://registry.npmjs.org/@fluentui/react-virtualizer/-/react-virtualizer-9.0.0-alpha.107.tgz", - "integrity": "sha512-zpTVzJB2BUNv7QdTUlLSBMCbt/EfALRuls/u/8FYaO4PGOFVeS3equytyxSOizz9zJZVhm8sjdp326DEQNiaPA==", + "version": "9.0.0-alpha.112", + "resolved": "https://registry.npmjs.org/@fluentui/react-virtualizer/-/react-virtualizer-9.0.0-alpha.112.tgz", + "integrity": "sha512-dao/mQssaPFxCXMx7K+G/DrRoZg28kXcE1NGbJ1RPtbkVCzJgwrEEeDhM5/wyOXO/Z5EZ31FIerDDVOyr6FAaw==", "license": "MIT", "dependencies": { - "@fluentui/react-jsx-runtime": "^9.3.3", - "@fluentui/react-shared-contexts": "^9.26.0", - "@fluentui/react-utilities": "^9.25.4", + "@fluentui/react-jsx-runtime": "^9.4.2", + "@fluentui/react-shared-contexts": "^9.26.2", + "@fluentui/react-utilities": "^9.26.3", "@griffel/react": "^1.5.32", "@swc/helpers": "^0.5.1" }, @@ -2455,22 +2468,22 @@ } }, "node_modules/@fluentui/tokens": { - "version": "1.0.0-alpha.22", - "resolved": "https://registry.npmjs.org/@fluentui/tokens/-/tokens-1.0.0-alpha.22.tgz", - "integrity": "sha512-i9fgYyyCWFRdUi+vQwnV6hp7wpLGK4p09B+O/f2u71GBXzPuniubPYvrIJYtl444DD6shLjYToJhQ1S6XTFwLg==", + "version": "1.0.0-alpha.23", + "resolved": "https://registry.npmjs.org/@fluentui/tokens/-/tokens-1.0.0-alpha.23.tgz", + "integrity": "sha512-uxrzF9Z+J10naP0pGS7zPmzSkspSS+3OJDmYIK3o1nkntQrgBXq3dBob4xSlTDm5aOQ0kw6EvB9wQgtlyy4eKQ==", "license": "MIT", "dependencies": { "@swc/helpers": "^0.5.1" } }, "node_modules/@griffel/core": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@griffel/core/-/core-1.19.2.tgz", - "integrity": "sha512-WkB/QQkjy9dE4vrNYGhQvRRUHFkYVOuaznVOMNTDT4pS9aTJ9XPrMTXXlkpcwaf0D3vNKoerj4zAwnU2lBzbOg==", + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/@griffel/core/-/core-1.20.1.tgz", + "integrity": "sha512-ld1mX04zpmeHn8agx4slSEh8kJ+8or3Y0x9gsJNKSKn6GdCkZBSiGUh+oBXCBn8RKzz8l60TA9IhVSStnyKekA==", "license": "MIT", "dependencies": { "@emotion/hash": "^0.9.0", - "@griffel/style-types": "^1.3.0", + "@griffel/style-types": "^1.4.0", "csstype": "^3.1.3", "rtl-css-js": "^1.16.1", "stylis": "^4.2.0", @@ -2478,12 +2491,12 @@ } }, "node_modules/@griffel/react": { - "version": "1.5.32", - "resolved": "https://registry.npmjs.org/@griffel/react/-/react-1.5.32.tgz", - "integrity": "sha512-jN3SmSwAUcWFUQuQ9jlhqZ5ELtKY21foaUR0q1mJtiAeSErVgjkpKJyMLRYpvaFGWrDql0Uz23nXUogXbsS2wQ==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@griffel/react/-/react-1.6.1.tgz", + "integrity": "sha512-mNM4/+dIXzqeHboWpVZ1/jiwTAYNc5/8y/V/HasnQ2QXnV6gSUYpeUk/0n6IFU3NJmVJly9JrLSfNo0hM/IFeA==", "license": "MIT", "dependencies": { - "@griffel/core": "^1.19.2", + "@griffel/core": "^1.20.1", "tslib": "^2.1.0" }, "peerDependencies": { @@ -2491,52 +2504,50 @@ } }, "node_modules/@griffel/style-types": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@griffel/style-types/-/style-types-1.3.0.tgz", - "integrity": "sha512-bHwD3sUE84Xwv4dH011gOKe1jul77M1S6ZFN9Tnq8pvZ48UMdY//vtES6fv7GRS5wXYT4iqxQPBluAiYAfkpmw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@griffel/style-types/-/style-types-1.4.0.tgz", + "integrity": "sha512-vNDfOGV7RN/XkA7vxgf7Z5HgW8eiBm5cHT9wQPhsKB4pxWom5u6eQ9CkYE5mCCTSPl9H6Nd1NBai04d4P6BD7Q==", "license": "MIT", "dependencies": { "csstype": "^3.1.3" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@humanfs/types": "^0.15.0" }, "engines": { - "node": ">=10.10.0" + "node": ">=18.18.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "license": "Apache-2.0", "engines": { - "node": "*" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -2553,13 +2564,19 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, - "license": "BSD-3-Clause" + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", @@ -2611,48 +2628,36 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@reduxjs/toolkit": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.11.2.tgz", + "integrity": "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==", "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@standard-schema/spec": "^1.0.0", + "@standard-schema/utils": "^0.3.0", + "immer": "^11.0.0", + "redux": "^5.0.1", + "redux-thunk": "^3.1.0", + "reselect": "^5.1.0" }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "peerDependencies": { + "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", + "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, - "engines": { - "node": ">= 8" + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-redux": { + "optional": true + } } }, "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", "dev": true, "license": "MIT" }, @@ -2895,9 +2900,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", - "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], @@ -3005,10 +3010,22 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "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/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", + "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" @@ -3068,6 +3085,13 @@ "@types/ms": "*" } }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -3092,6 +3116,13 @@ "@types/unist": "*" } }, + "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, + "license": "MIT" + }, "node_modules/@types/mdast": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", @@ -3108,41 +3139,34 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.19.0" } }, - "node_modules/@types/prop-types": { - "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.27", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "license": "MIT", "peer": true, "dependencies": { - "@types/prop-types": "*", "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "license": "MIT", "peer": true, "peerDependencies": { - "@types/react": "^18.0.0" + "@types/react": "^19.2.0" } }, "node_modules/@types/unist": { @@ -3151,131 +3175,168 @@ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "license": "MIT" }, + "node_modules/@types/use-sync-external-store": { + "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/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz", + "integrity": "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==", + "deprecated": "This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "uuid": "*" + } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", + "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/type-utils": "8.59.0", + "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", + "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@typescript-eslint/parser": "^8.59.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", + "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4" + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", + "debug": "^4.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", + "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.0", + "@typescript-eslint/types": "^8.59.0", + "debug": "^4.4.3" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", + "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", + "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", + "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", + "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", + "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", "dev": true, "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -3283,75 +3344,88 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", + "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "@typescript-eslint/project-service": "8.59.0", + "@typescript-eslint/tsconfig-utils": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", + "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", + "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.59.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -3359,30 +3433,30 @@ "license": "ISC" }, "node_modules/@vitejs/plugin-react": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", - "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.28.0", + "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.27", + "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" + "react-refresh": "^0.18.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "peer": true, @@ -3404,9 +3478,9 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3420,49 +3494,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -3474,49 +3505,45 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/baseline-browser-mapping": { - "version": "2.8.31", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", - "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", + "version": "2.10.21", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.21.tgz", + "integrity": "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/brace-expansion": { - "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" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "fill-range": "^7.1.1" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" } }, "node_modules/browserslist": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.0.tgz", - "integrity": "sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -3535,11 +3562,11 @@ "license": "MIT", "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.8.25", - "caniuse-lite": "^1.0.30001754", - "electron-to-chromium": "^1.5.249", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.1.4" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -3548,20 +3575,10 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/caniuse-lite": { - "version": "1.0.30001757", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", - "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", + "version": "1.0.30001790", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001790.tgz", + "integrity": "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==", "dev": true, "funding": [ { @@ -3589,23 +3606,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -3646,26 +3646,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, "node_modules/comma-separated-tokens": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", @@ -3676,13 +3656,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3770,45 +3743,19 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/electron-to-chromium": { + "version": "1.5.344", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", "dev": true, + "license": "ISC" + }, + "node_modules/embla-carousel": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", + "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.261", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.261.tgz", - "integrity": "sha512-cmyHEWFqEt3ICUNF93ShneOF47DHoSDbLb7E/AonsWcbzg95N+kPXeLNfkdzgTT/vEUcoW76fxbLBkeYtfoM8A==", - "dev": true, - "license": "ISC" - }, - "node_modules/embla-carousel": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", - "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT", - "peer": true + "peer": true }, "node_modules/embla-carousel-autoplay": { "version": "8.6.0", @@ -3894,98 +3841,106 @@ } }, "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.2.1.tgz", + "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^10.2.4", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", - "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", "dev": true, "license": "MIT", "peerDependencies": { - "eslint": ">=8.40" + "eslint": "^9 || ^10" } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -4004,52 +3959,64 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, + "license": "MIT", "engines": { - "node": "*" + "node": ">= 4" } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.16.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^5.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -4115,36 +4082,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4159,40 +4096,17 @@ "dev": true, "license": "MIT" }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" + "node": ">=16.0.0" } }, "node_modules/find-up": { @@ -4213,18 +4127,17 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { @@ -4234,13 +4147,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -4266,28 +4172,6 @@ "node": ">=6.9.0" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4301,84 +4185,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "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" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -4419,6 +4225,23 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -4430,30 +4253,23 @@ } }, "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, + "node_modules/immer": { + "version": "11.1.4", + "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", + "integrity": "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==", "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/immer" } }, "node_modules/imurmurhash": { @@ -4466,25 +4282,6 @@ "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, "node_modules/inline-style-parser": { "version": "0.2.7", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", @@ -4558,26 +4355,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -4601,20 +4378,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } + "license": "MIT" }, "node_modules/jsesc": { "version": "3.1.0", @@ -4709,13 +4474,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -4726,18 +4484,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4901,16 +4647,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, "node_modules/micromark": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", @@ -5353,31 +5089,17 @@ ], "license": "MIT" }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.2" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -5416,22 +5138,12 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", "dev": true, "license": "MIT" }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5482,19 +5194,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", @@ -5530,16 +5229,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -5550,16 +5239,6 @@ "node": ">=8" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5567,19 +5246,6 @@ "dev": true, "license": "ISC" }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -5639,73 +5305,33 @@ "node": ">=6" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "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/react": { - "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==", + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", + "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", "peer": true, "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-dom/node_modules/scheduler": { - "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" + "react": "^19.2.5" } }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "license": "MIT" - }, "node_modules/react-markdown": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.1.0.tgz", - "integrity": "sha512-xaijuJB0kzGiUdG7nc2MOMDUDBWPyGAjZtUrow9XxUeua8IqeP+VlIfAZ3bphpcLTnSZXz6z9jcVC/TCwbfgdw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", "license": "MIT", "dependencies": { "@types/hast": "^3.0.0", @@ -5729,16 +5355,56 @@ "react": ">=18" } }, + "node_modules/react-redux": { + "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", + "peer": true, + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "@types/react": "^18.2.25 || ^19", + "react": "^18.0 || ^19", + "redux": "^5.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "redux": { + "optional": true + } + } + }, "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT", + "peer": true + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -5772,43 +5438,11 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==", + "license": "MIT" }, "node_modules/rollup": { "version": "4.59.0", @@ -5855,20 +5489,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.59.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", - "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, "node_modules/rtl-css-js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", @@ -5878,30 +5498,6 @@ "@babel/runtime": "^7.1.2" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -5910,9 +5506,9 @@ "peer": true }, "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "bin": { @@ -5945,16 +5541,6 @@ "node": ">=8" } }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5989,32 +5575,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/style-to-js": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", @@ -6034,44 +5594,24 @@ } }, "node_modules/stylis": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", - "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", "license": "MIT" }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/tabster": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/tabster/-/tabster-8.5.6.tgz", - "integrity": "sha512-2vfrRGrx8O9BjdrtSlVA5fvpmbq5HQBRN13XFRg6LAvZ1Fr3QdBnswgT4YgFS5Bhoo5nxwgjRaRueI2Us/dv7g==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/tabster/-/tabster-8.7.0.tgz", + "integrity": "sha512-AKYquti8AdWzuqJdQo4LUMQDZrHoYQy6V+8yUq2PmgLZV10EaB+8BD0nWOfC/3TBp4mPNg4fbHkz6SFtkr0PpA==", "license": "MIT", "dependencies": { "keyborg": "2.6.0", "tslib": "^2.8.1" }, "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "4.40.0" + "@rollup/rollup-linux-x64-gnu": "4.53.3" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -6108,9 +5648,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "peer": true, @@ -6121,19 +5661,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -6155,16 +5682,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/tslib": { @@ -6186,19 +5713,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -6215,9 +5729,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "dev": true, "license": "MIT" }, @@ -6309,9 +5823,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", - "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -6359,16 +5873,16 @@ } }, "node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist-node/bin/uuid" } }, "node_modules/vfile": { @@ -6400,9 +5914,9 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "dev": true, "license": "MIT", "peer": true, @@ -6494,9 +6008,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "peer": true, @@ -6533,13 +6047,6 @@ "node": ">=0.10.0" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -6560,6 +6067,30 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/src/App/package.json b/src/App/package.json new file mode 100644 index 000000000..013a74cfc --- /dev/null +++ b/src/App/package.json @@ -0,0 +1,39 @@ +{ + "name": "content-generation-frontend", + "version": "1.0.0", + "description": "Frontend for Intelligent Content Generation Accelerator", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" + }, + "dependencies": { + "@fluentui/react-components": "^9.73.1", + "@fluentui/react-icons": "^2.0.320", + "@reduxjs/toolkit": "^2.2.0", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-markdown": "^10.1.0", + "react-redux": "^9.1.0", + "uuid": "^14.0.0" + }, + "devDependencies": { + "@types/node": "^25.3.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@types/uuid": "^11.0.0", + "@typescript-eslint/eslint-plugin": "^8.56.1", + "@typescript-eslint/parser": "^8.56.1", + "@vitejs/plugin-react": "^5.1.4", + "eslint": "^10.0.2", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "typescript": "^5.5.2", + "vite": "^7.3.2" + }, + "overrides": { + "@rollup/rollup-linux-x64-gnu": "4.59.0" + } +} diff --git a/src/app/frontend-server/package-lock.json b/src/App/server/package-lock.json similarity index 66% rename from src/app/frontend-server/package-lock.json rename to src/App/server/package-lock.json index 276450dde..d2c760bf7 100644 --- a/src/app/frontend-server/package-lock.json +++ b/src/App/server/package-lock.json @@ -8,8 +8,8 @@ "name": "contentgen-frontend", "version": "1.0.0", "dependencies": { - "express": "^4.18.2", - "http-proxy-middleware": "^2.0.6" + "express": "^5.2.1", + "http-proxy-middleware": "^3.0.5" }, "engines": { "node": ">=18.0.0" @@ -25,55 +25,49 @@ } }, "node_modules/@types/node": { - "version": "24.10.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", - "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.19.0" } }, "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/braces": { @@ -127,15 +121,16 @@ } }, "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/content-type": { @@ -157,18 +152,29 @@ } }, "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } }, "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { - "ms": "2.0.0" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/depd": { @@ -180,16 +186,6 @@ "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -271,45 +267,42 @@ "license": "MIT" }, "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" }, "funding": { "type": "opencollective", @@ -329,27 +322,30 @@ } }, "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -376,12 +372,12 @@ } }, "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/function-bind": { @@ -455,9 +451,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -501,39 +497,36 @@ } }, "node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.5.tgz", + "integrity": "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg==", "license": "MIT", "dependencies": { - "@types/http-proxy": "^1.17.8", + "@types/http-proxy": "^1.17.15", + "debug": "^4.3.6", "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "is-glob": "^4.0.3", + "is-plain-object": "^5.0.0", + "micromatch": "^4.0.8" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/inherits": { @@ -581,18 +574,21 @@ "node": ">=0.12.0" } }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -603,32 +599,26 @@ } }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", + "engines": { + "node": ">=18" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -642,49 +632,41 @@ "node": ">=8.6" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { - "mime-db": "1.52.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "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": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -714,6 +696,15 @@ "node": ">= 0.8" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -724,15 +715,19 @@ } }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "license": "MIT", "engines": { "node": ">=8.6" @@ -755,9 +750,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -779,18 +774,18 @@ } }, "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", + "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, "node_modules/requires-port": { @@ -799,25 +794,21 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "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/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -826,137 +817,48 @@ "license": "MIT" }, "node_modules/send": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz", - "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "node": ">= 18" }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-static/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "node": ">= 18" }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static/node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-static/node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serve-static/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/setprototypeof": { @@ -985,13 +887,13 @@ } }, "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==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -1068,22 +970,23 @@ } }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "license": "MIT" }, "node_modules/unpipe": { @@ -1095,15 +998,6 @@ "node": ">= 0.8" } }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1112,6 +1006,12 @@ "engines": { "node": ">= 0.8" } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" } } } diff --git a/src/app/frontend-server/package.json b/src/App/server/package.json similarity index 80% rename from src/app/frontend-server/package.json rename to src/App/server/package.json index 262373682..46a37af2e 100644 --- a/src/app/frontend-server/package.json +++ b/src/App/server/package.json @@ -7,8 +7,8 @@ "start": "node server.js" }, "dependencies": { - "express": "^4.18.2", - "http-proxy-middleware": "^2.0.6" + "express": "^5.2.1", + "http-proxy-middleware": "^3.0.5" }, "engines": { "node": ">=18.0.0" diff --git a/src/app/frontend-server/server.js b/src/App/server/server.js similarity index 97% rename from src/app/frontend-server/server.js rename to src/App/server/server.js index cb61a1dee..011b7dd14 100644 --- a/src/app/frontend-server/server.js +++ b/src/App/server/server.js @@ -22,7 +22,7 @@ app.use('/api', createProxyMiddleware({ target: BACKEND_URL, changeOrigin: true, pathRewrite: { - '^/api': '/api' + '^/': '/api/' }, agent: httpAgent, // Increase timeout for long-running requests (10 minutes) @@ -56,7 +56,7 @@ app.use('/api', createProxyMiddleware({ app.use(express.static(path.join(__dirname, 'static'))); // Serve index.html for all other routes (SPA support) -app.get('*', (req, res) => { +app.get('/{*path}', (req, res) => { res.sendFile(path.join(__dirname, 'static', 'index.html')); }); diff --git a/src/App/src/App.tsx b/src/App/src/App.tsx new file mode 100644 index 000000000..05c668d36 --- /dev/null +++ b/src/App/src/App.tsx @@ -0,0 +1,95 @@ +import { useEffect, useRef } from 'react'; + +import { useAppDispatch, useAppSelector } from './store/hooks'; +import { fetchAppConfig, fetchCurrentUser, toggleChatHistory } from './store/appSlice'; +import { ChatPanel } from './components/ChatPanel'; +import { ChatHistory } from './components/ChatHistory'; +import { AppHeader } from './components/AppHeader'; +import { useChatOrchestrator } from './hooks/useChatOrchestrator'; +import { useContentGeneration } from './hooks/useContentGeneration'; +import { useConversationActions } from './hooks/useConversationActions'; + + +function App() { + const dispatch = useAppDispatch(); + + // Select state from Redux store + const { userName, imageGenerationEnabled, showChatHistory } = useAppSelector(state => state.app); + const { conversationId, conversationTitle, messages, isLoading, generationStatus, historyRefreshTrigger } = useAppSelector(state => state.chat); + const { pendingBrief, confirmedBrief, selectedProducts, availableProducts, generatedContent } = useAppSelector(state => state.content); + + // Abort controller for cancelling ongoing requests + const abortControllerRef = useRef(null); + + // Custom hooks for business logic + const { handleSendMessage } = useChatOrchestrator(abortControllerRef); + const { handleGenerateContent } = useContentGeneration(abortControllerRef); + const { + handleBriefConfirm, + handleBriefCancel, + handleProductSelect, + handleStopGeneration, + handleSelectConversation, + handleNewConversation, + } = useConversationActions(abortControllerRef); + + // Fetch app config and user info on mount + useEffect(() => { + dispatch(fetchAppConfig()); + dispatch(fetchCurrentUser()); + }, [dispatch]); + + return ( +
+ {/* Header */} + dispatch(toggleChatHistory())} + /> + + {/* Main Content */} +
+ {/* Chat Panel - main area */} +
+ +
+ + {/* Chat History Sidebar - RIGHT side */} + {showChatHistory && ( +
+ +
+ )} +
+
+ ); +} + +export default App; diff --git a/src/App/src/api/index.ts b/src/App/src/api/index.ts new file mode 100644 index 000000000..1c1b8a8d7 --- /dev/null +++ b/src/App/src/api/index.ts @@ -0,0 +1,155 @@ +/** + * API service for interacting with the Content Generation backend + * Uses centralized httpClient for consistent fetch handling + */ + +import type { + AgentResponse, + AppConfig, + CreativeBrief, + MessageRequest, + MessageResponse, + Product, +} from '../types'; +import { httpClient } from '../utils/httpClient'; +import { POLLING_CONFIG } from '../utils/constants'; + +/** + * Send a message or action to the /api/chat endpoint + */ +export async function sendMessage( + request: MessageRequest, + signal?: AbortSignal +): Promise { + return httpClient.post('/chat', request, signal); +} + +/** + * Get application configuration including feature flags + */ +export async function getAppConfig(): Promise { + return httpClient.get('/config'); +} + +/** + * Request for content generation + */ +export interface GenerateRequest { + conversation_id: string; + user_id: string; + brief: CreativeBrief; + products: Product[]; + generate_images: boolean; +} + +type StageMessageFn = (elapsedSeconds: number) => string; + +/** + * Shared polling loop for task completion + */ +async function* pollLoop( + taskId: string, + signal: AbortSignal | undefined, + getStageMessage: StageMessageFn, +): AsyncGenerator { + const { maxAttempts, intervalMs } = POLLING_CONFIG; + let attempts = 0; + + while (attempts < maxAttempts) { + if (signal?.aborted) { + throw new DOMException('Operation cancelled by user', 'AbortError'); + } + + await new Promise(resolve => setTimeout(resolve, intervalMs)); + attempts++; + + if (signal?.aborted) { + throw new DOMException('Operation cancelled by user', 'AbortError'); + } + + try { + const statusData = await httpClient.get<{ status: string; result?: unknown; error?: string }>( + `/generate/status/${taskId}`, signal + ); + + if (statusData.status === 'completed') { + yield { + type: 'agent_response', + content: JSON.stringify(statusData.result), + is_final: true, + } as AgentResponse; + return; + } + + if (statusData.status === 'failed') { + throw new Error(statusData.error || 'Task failed'); + } + + // Running — yield heartbeat + yield { + type: 'heartbeat', + content: getStageMessage(attempts), + elapsed: attempts, + is_final: false, + } as AgentResponse; + } catch (error) { + // Continue polling on transient errors until max attempts + if (attempts >= maxAttempts) throw error; + } + } + + throw new Error('Task timed out after 10 minutes'); +} + +/** Stage messages for full content generation */ +function generationStageMessage(elapsed: number): string { + if (elapsed < 10) return 'Analyzing creative brief...'; + if (elapsed < 25) return 'Generating marketing copy...'; + if (elapsed < 35) return 'Creating image prompt...'; + if (elapsed < 55) return 'Generating image with AI...'; + if (elapsed < 70) return 'Running compliance check...'; + return 'Finalizing content...'; +} + +/** Stage messages for image regeneration */ +function regenerationStageMessage(elapsed: number): string { + if (elapsed < 10) return 'Starting regeneration...'; + if (elapsed < 30) return 'Generating new image...'; + if (elapsed < 50) return 'Processing image...'; + return 'Finalizing...'; +} + +/** + * Generate content from a confirmed brief + */ +export async function* streamGenerateContent( + request: GenerateRequest, + signal?: AbortSignal +): AsyncGenerator { + const startData = await httpClient.post<{ task_id: string }>('/generate/start', { + brief: request.brief, + products: request.products || [], + generate_images: request.generate_images, + conversation_id: request.conversation_id, + user_id: request.user_id || 'anonymous', + }, signal); + + yield { + type: 'status', + content: 'Generation started...', + is_final: false, + } as AgentResponse; + + yield* pollLoop(startData.task_id, signal, generationStageMessage); +} + +/** + * Poll for task completion using task_id + * Used for both content generation and image regeneration + */ +export async function* pollTaskStatus( + taskId: string, + signal?: AbortSignal +): AsyncGenerator { + yield* pollLoop(taskId, signal, regenerationStageMessage); +} \ No newline at end of file diff --git a/src/App/src/components/AppHeader.tsx b/src/App/src/components/AppHeader.tsx new file mode 100644 index 000000000..dd7205737 --- /dev/null +++ b/src/App/src/components/AppHeader.tsx @@ -0,0 +1,66 @@ +/** + * AppHeader component - application header bar + * Extracted from App.tsx and wrapped with React.memo + */ + +import React from 'react'; +import { + Text, + Avatar, + Button, + Tooltip, + tokens, +} from '@fluentui/react-components'; +import { + History24Regular, + History24Filled, +} from '@fluentui/react-icons'; +import ContosoLogo from '../styles/images/contoso.svg'; + +interface AppHeaderProps { + userName: string; + showChatHistory: boolean; + onToggleChatHistory: () => void; +} + +export const AppHeader = React.memo(function AppHeader({ + userName, + showChatHistory, + onToggleChatHistory, +}: AppHeaderProps) { + return ( +
+
+ Contoso + + Contoso + +
+
+ +
+
+ ); +}); + +AppHeader.displayName = 'AppHeader'; diff --git a/src/app/frontend/src/components/BriefReview.tsx b/src/App/src/components/BriefReview.tsx similarity index 72% rename from src/app/frontend/src/components/BriefReview.tsx rename to src/App/src/components/BriefReview.tsx index 6ea755905..3c1ccb2ec 100644 --- a/src/app/frontend/src/components/BriefReview.tsx +++ b/src/App/src/components/BriefReview.tsx @@ -1,9 +1,11 @@ +import React from 'react'; import { Button, Text, tokens, } from '@fluentui/react-components'; import type { CreativeBrief } from '../types'; +import { AI_DISCLAIMER, BRIEF_FIELD_CONFIG, BRIEF_FIELD_KEYS, BRIEF_FIELD_LABELS } from '../utils/constants'; interface BriefReviewProps { brief: CreativeBrief; @@ -12,47 +14,17 @@ interface BriefReviewProps { isAwaitingResponse?: boolean; } -// Mapping of field keys to user-friendly labels for the 9 key areas -const fieldLabels: Record = { - overview: 'Overview', - objectives: 'Objectives', - target_audience: 'Target Audience', - key_message: 'Key Message', - tone_and_style: 'Tone and Style', - deliverable: 'Deliverable', - timelines: 'Timelines', - visual_guidelines: 'Visual Guidelines', - cta: 'Call to Action', -}; - -export function BriefReview({ +export const BriefReview = React.memo(function BriefReview({ brief, onConfirm, onStartOver, isAwaitingResponse = false, }: BriefReviewProps) { - const allFields: (keyof CreativeBrief)[] = [ - 'overview', 'objectives', 'target_audience', 'key_message', - 'tone_and_style', 'deliverable', 'timelines', 'visual_guidelines', 'cta' - ]; - const populatedFields = allFields.filter(key => brief[key]?.trim()).length; - const missingFields = allFields.filter(key => !brief[key]?.trim()); - - // Define the order and labels for display in the card - const displayOrder: { key: keyof CreativeBrief; label: string }[] = [ - { key: 'overview', label: 'Campaign Objective' }, - { key: 'objectives', label: 'Objectives' }, - { key: 'target_audience', label: 'Target Audience' }, - { key: 'key_message', label: 'Key Message' }, - { key: 'tone_and_style', label: 'Tone & Style' }, - { key: 'visual_guidelines', label: 'Visual Guidelines' }, - { key: 'deliverable', label: 'Deliverables' }, - { key: 'timelines', label: 'Timelines' }, - { key: 'cta', label: 'Call to Action' }, - ]; + const populatedFields = BRIEF_FIELD_KEYS.filter(key => brief[key]?.trim()).length; + const missingFields = BRIEF_FIELD_KEYS.filter(key => !brief[key]?.trim()); - // Filter to only populated fields - const populatedDisplayFields = displayOrder.filter(({ key }) => brief[key]?.trim()); + // Filter to only populated fields for display + const populatedDisplayFields = BRIEF_FIELD_CONFIG.filter(({ key }) => brief[key]?.trim()); return (
I've captured {populatedFields} of 9 key areas. Would you like to add more details? - You are missing: {missingFields.map(f => fieldLabels[f]).join(', ')}. + You are missing: {missingFields.map(f => BRIEF_FIELD_LABELS[f]).join(', ')}.

You can tell me things like:
    @@ -177,9 +149,11 @@ export function BriefReview({ paddingTop: '8px', }}> - AI-generated content may be incorrect + {AI_DISCLAIMER}
); -} +}); + +BriefReview.displayName = 'BriefReview'; diff --git a/src/app/frontend/src/components/ChatHistory.tsx b/src/App/src/components/ChatHistory.tsx similarity index 87% rename from src/app/frontend/src/components/ChatHistory.tsx rename to src/App/src/components/ChatHistory.tsx index f258d2a48..6ccab6eeb 100644 --- a/src/app/frontend/src/components/ChatHistory.tsx +++ b/src/App/src/components/ChatHistory.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback, useRef } from 'react'; +import React, { useState, useEffect, useCallback, useRef } from 'react'; import { Button, Text, @@ -26,6 +26,10 @@ import { Edit20Regular, DismissCircle20Regular, } from '@fluentui/react-icons'; +import { httpClient } from '../utils/httpClient'; + +const MIN_NAME_LENGTH = 5; +const MAX_NAME_LENGTH = 50; interface ConversationSummary { id: string; @@ -45,7 +49,7 @@ interface ChatHistoryProps { isGenerating?: boolean; // True when content generation is in progress } -export function ChatHistory({ +export const ChatHistory = React.memo(function ChatHistory({ currentConversationId, currentConversationTitle, currentMessages = [], @@ -65,18 +69,12 @@ export function ChatHistory({ const handleClearAllConversations = useCallback(async () => { setIsClearing(true); try { - const response = await fetch('/api/conversations', { - method: 'DELETE', - }); - if (response.ok) { - setConversations([]); - onNewConversation(); - setIsClearAllDialogOpen(false); - } else { - console.error('Failed to clear all conversations'); - } - } catch (err) { - console.error('Error clearing all conversations:', err); + await httpClient.delete('/conversations'); + setConversations([]); + onNewConversation(); + setIsClearAllDialogOpen(false); + } catch { + // Clear failed — no action needed } finally { setIsClearing(false); } @@ -84,41 +82,24 @@ export function ChatHistory({ const handleDeleteConversation = useCallback(async (conversationId: string) => { try { - const response = await fetch(`/api/conversations/${conversationId}`, { - method: 'DELETE', - }); - if (response.ok) { - setConversations(prev => prev.filter(c => c.id !== conversationId)); - if (conversationId === currentConversationId) { - onNewConversation(); - } - } else { - console.error('Failed to delete conversation'); + await httpClient.delete(`/conversations/${conversationId}`); + setConversations(prev => prev.filter(c => c.id !== conversationId)); + if (conversationId === currentConversationId) { + onNewConversation(); } - } catch (err) { - console.error('Error deleting conversation:', err); + } catch { + // Delete failed — no action needed } }, [currentConversationId, onNewConversation]); const handleRenameConversation = useCallback(async (conversationId: string, newTitle: string) => { try { - const response = await fetch(`/api/conversations/${conversationId}`, { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ title: newTitle }), - }); - - if (response.ok) { - setConversations(prev => prev.map(c => - c.id === conversationId ? { ...c, title: newTitle } : c - )); - } else { - console.error('Failed to rename conversation'); - } - } catch (err) { - console.error('Error renaming conversation:', err); + await httpClient.put(`/conversations/${conversationId}`, { title: newTitle }); + setConversations(prev => prev.map(c => + c.id === conversationId ? { ...c, title: newTitle } : c + )); + } catch { + // Rename failed — no action needed } }, []); @@ -126,17 +107,9 @@ export function ChatHistory({ setIsLoading(true); setError(null); try { - // Backend gets user from auth headers, no need to pass user_id - const response = await fetch('/api/conversations'); - if (response.ok) { - const data = await response.json(); - setConversations(data.conversations || []); - } else { - // If no conversations endpoint, use empty list - setConversations([]); - } - } catch (err) { - console.error('Error loading conversations:', err); + const data = await httpClient.get<{ conversations: ConversationSummary[] }>('/conversations'); + setConversations(data.conversations || []); + } catch { setError('Unable to load conversation history'); setConversations([]); } finally { @@ -379,7 +352,9 @@ export function ChatHistory({ ); -} +}); + +ChatHistory.displayName = 'ChatHistory'; interface ConversationItemProps { conversation: ConversationSummary; @@ -391,7 +366,7 @@ interface ConversationItemProps { disabled?: boolean; } -function ConversationItem({ +const ConversationItem = React.memo(function ConversationItem({ conversation, isActive, onSelect, @@ -418,12 +393,12 @@ function ConversationItem({ const trimmedValue = renameValue.trim(); // Validate before API call - if (trimmedValue.length < 5) { - setRenameError('Conversation name must be at least 5 characters'); + if (trimmedValue.length < MIN_NAME_LENGTH) { + setRenameError(`Conversation name must be at least ${MIN_NAME_LENGTH} characters`); return; } - if (trimmedValue.length > 50) { - setRenameError('Conversation name cannot exceed 50 characters'); + if (trimmedValue.length > MAX_NAME_LENGTH) { + setRenameError(`Conversation name cannot exceed ${MAX_NAME_LENGTH} characters`); return; } if (!/[a-zA-Z0-9]/.test(trimmedValue)) { @@ -550,18 +525,18 @@ function ConversationItem({ { const newValue = e.target.value; setRenameValue(newValue); if (newValue.trim() === '') { setRenameError('Conversation name cannot be empty or contain only spaces'); - } else if (newValue.trim().length < 5) { - setRenameError('Conversation name must be at least 5 characters'); + } else if (newValue.trim().length < MIN_NAME_LENGTH) { + setRenameError(`Conversation name must be at least ${MIN_NAME_LENGTH} characters`); } else if (!/[a-zA-Z0-9]/.test(newValue)) { setRenameError('Conversation name must contain at least one letter or number'); - } else if (newValue.length > 50) { - setRenameError('Conversation name cannot exceed 50 characters'); + } else if (newValue.length > MAX_NAME_LENGTH) { + setRenameError(`Conversation name cannot exceed ${MAX_NAME_LENGTH} characters`); } else { setRenameError(''); } @@ -584,7 +559,7 @@ function ConversationItem({ display: 'block' }} > - Maximum 50 characters ({renameValue.length}/50) + Maximum {MAX_NAME_LENGTH} characters ({renameValue.length}/{MAX_NAME_LENGTH}) {renameError && ( 50} + disabled={renameValue.trim().length < MIN_NAME_LENGTH || !/[a-zA-Z0-9]/.test(renameValue) || renameValue.length > MAX_NAME_LENGTH} > Rename @@ -640,4 +615,6 @@ function ConversationItem({ ); -} +}); + +ConversationItem.displayName = 'ConversationItem'; diff --git a/src/App/src/components/ChatInput.tsx b/src/App/src/components/ChatInput.tsx new file mode 100644 index 000000000..b51983af2 --- /dev/null +++ b/src/App/src/components/ChatInput.tsx @@ -0,0 +1,150 @@ +/** + * ChatInput component - message input with send/new-chat buttons + * Extracted from ChatPanel.tsx and wrapped with React.memo + */ + +import React, { useState } from 'react'; +import { + Text, + Button, + Tooltip, + tokens, +} from '@fluentui/react-components'; +import { + Send20Regular, + Add20Regular, +} from '@fluentui/react-icons'; +import { AI_DISCLAIMER } from '../utils/constants'; + +interface ChatInputProps { + onSendMessage: (message: string) => void; + isLoading: boolean; + hasMessages: boolean; + onNewConversation?: () => void; + initialValue?: string; + onInputChange?: (value: string) => void; +} + +export const ChatInput = React.memo(function ChatInput({ + onSendMessage, + isLoading, + hasMessages, + onNewConversation, + initialValue = '', + onInputChange, +}: ChatInputProps) { + const [inputValue, setInputValue] = useState(initialValue); + + // Sync external value changes (e.g., from suggestion clicks) + React.useEffect(() => { + setInputValue(initialValue); + }, [initialValue]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (inputValue.trim() && !isLoading) { + onSendMessage(inputValue.trim()); + setInputValue(''); + onInputChange?.(''); + } + }; + + const handleChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + onInputChange?.(e.target.value); + }; + + return ( +
+ {/* Input Box */} +
+ { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSubmit(e); + } + }} + placeholder="Type a message" + disabled={isLoading} + style={{ + flex: 1, + border: 'none', + outline: 'none', + backgroundColor: 'transparent', + fontFamily: 'var(--fontFamilyBase)', + fontSize: '14px', + color: tokens.colorNeutralForeground1, + }} + /> + + {/* Icons on the right */} +
+ +
+
+ + {/* Disclaimer */} + + {AI_DISCLAIMER} + +
+ ); +}); + +ChatInput.displayName = 'ChatInput'; diff --git a/src/App/src/components/ChatPanel.tsx b/src/App/src/components/ChatPanel.tsx new file mode 100644 index 000000000..60a7f2f60 --- /dev/null +++ b/src/App/src/components/ChatPanel.tsx @@ -0,0 +1,177 @@ +import React, { useState, useRef, useEffect, useCallback } from 'react'; +import type { ChatMessage, CreativeBrief, Product, GeneratedContent } from '../types'; +import { BriefReview } from './BriefReview'; +import { ConfirmedBriefView } from './ConfirmedBriefView'; +import { SelectedProductView } from './SelectedProductView'; +import { ProductReview } from './ProductReview'; +import { InlineContentPreview } from './InlineContentPreview'; +import { WelcomeCard } from './WelcomeCard'; +import { MessageBubble } from './MessageBubble'; +import { TypingIndicator } from './TypingIndicator'; +import { ChatInput } from './ChatInput'; + +interface ChatPanelProps { + messages: ChatMessage[]; + onSendMessage: (message: string) => void; + isLoading: boolean; + generationStatus?: string; + onStopGeneration?: () => void; + // Inline component props + pendingBrief?: CreativeBrief | null; + confirmedBrief?: CreativeBrief | null; + generatedContent?: GeneratedContent | null; + selectedProducts?: Product[]; + availableProducts?: Product[]; + onBriefConfirm?: () => void; + onBriefCancel?: () => void; + onGenerateContent?: () => void; + onRegenerateContent?: () => void; + onProductSelect?: (product: Product) => void; + // Feature flags + imageGenerationEnabled?: boolean; + // New chat + onNewConversation?: () => void; +} + +export const ChatPanel = React.memo(function ChatPanel({ + messages, + onSendMessage, + isLoading, + generationStatus, + onStopGeneration, + pendingBrief, + confirmedBrief, + generatedContent, + selectedProducts = [], + availableProducts = [], + onBriefConfirm, + onBriefCancel, + onGenerateContent, + onRegenerateContent, + onProductSelect, + imageGenerationEnabled = true, + onNewConversation, +}: ChatPanelProps) { + const [inputValue, setInputValue] = useState(''); + const messagesEndRef = useRef(null); + const messagesContainerRef = useRef(null); + + // Scroll to bottom when messages change + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, [messages, pendingBrief, confirmedBrief, generatedContent, isLoading, generationStatus]); + + // Determine if we should show inline components + const showBriefReview = !!(pendingBrief && onBriefConfirm && onBriefCancel); + const showProductReview = !!(confirmedBrief && !generatedContent && onGenerateContent); + const showContentPreview = !!(generatedContent && onRegenerateContent); + const showWelcome = messages.length === 0 && !showBriefReview && !showProductReview && !showContentPreview; + + // Handle suggestion click from welcome card + const handleSuggestionClick = useCallback((prompt: string) => { + setInputValue(prompt); + }, []); + + const handleSendMessage = useCallback((msg: string) => { + onSendMessage(msg); + setInputValue(''); + }, [onSendMessage]); + + return ( +
+ {/* Messages Area */} +
+ {showWelcome ? ( + + ) : ( + <> + {messages.map((message) => ( + + ))} + + {/* Brief Review - Read Only with Conversational Prompts */} + {showBriefReview && ( + + )} + + {/* Confirmed Brief View - Persistent read-only view */} + {confirmedBrief && !pendingBrief && ( + + )} + + {/* Selected Product View - Persistent read-only view after content generation */} + {generatedContent && selectedProducts.length > 0 && ( + + )} + + {/* Product Review - Conversational Product Selection */} + {showProductReview && ( + + )} + + {/* Inline Content Preview */} + {showContentPreview && ( + 0 ? selectedProducts[0] : undefined} + imageGenerationEnabled={imageGenerationEnabled} + /> + )} + + {/* Loading/Typing Indicator */} + {isLoading && ( + + )} + + )} + +
+
+ + {/* Input Area */} + 0} + onNewConversation={onNewConversation} + /> +
+ ); +}); + +ChatPanel.displayName = 'ChatPanel'; + + diff --git a/src/app/frontend/src/components/ConfirmedBriefView.tsx b/src/App/src/components/ConfirmedBriefView.tsx similarity index 74% rename from src/app/frontend/src/components/ConfirmedBriefView.tsx rename to src/App/src/components/ConfirmedBriefView.tsx index e7feb9416..a1d7ce1fc 100644 --- a/src/app/frontend/src/components/ConfirmedBriefView.tsx +++ b/src/App/src/components/ConfirmedBriefView.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { Text, Badge, @@ -7,24 +8,13 @@ import { Checkmark20Regular, } from '@fluentui/react-icons'; import type { CreativeBrief } from '../types'; +import { BRIEF_FIELD_CONFIG } from '../utils/constants'; interface ConfirmedBriefViewProps { brief: CreativeBrief; } -const briefFields: { key: keyof CreativeBrief; label: string }[] = [ - { key: 'overview', label: 'Overview' }, - { key: 'objectives', label: 'Objectives' }, - { key: 'target_audience', label: 'Target Audience' }, - { key: 'key_message', label: 'Key Message' }, - { key: 'tone_and_style', label: 'Tone & Style' }, - { key: 'deliverable', label: 'Deliverable' }, - { key: 'timelines', label: 'Timelines' }, - { key: 'visual_guidelines', label: 'Visual Guidelines' }, - { key: 'cta', label: 'Call to Action' }, -]; - -export function ConfirmedBriefView({ brief }: ConfirmedBriefViewProps) { +export const ConfirmedBriefView = React.memo(function ConfirmedBriefView({ brief }: ConfirmedBriefViewProps) { return (
- {briefFields.map(({ key, label }) => { + {BRIEF_FIELD_CONFIG.map(({ key, label }) => { const value = brief[key]; if (!value?.trim()) return null; @@ -88,4 +78,6 @@ export function ConfirmedBriefView({ brief }: ConfirmedBriefViewProps) {
); -} +}); + +ConfirmedBriefView.displayName = 'ConfirmedBriefView'; diff --git a/src/app/frontend/src/components/InlineContentPreview.tsx b/src/App/src/components/InlineContentPreview.tsx similarity index 73% rename from src/app/frontend/src/components/InlineContentPreview.tsx rename to src/App/src/components/InlineContentPreview.tsx index 3ee0eead2..7f39825bb 100644 --- a/src/app/frontend/src/components/InlineContentPreview.tsx +++ b/src/App/src/components/InlineContentPreview.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import { Button, Text, @@ -22,6 +22,10 @@ import { ShieldError20Regular, } from '@fluentui/react-icons'; import type { GeneratedContent, ComplianceViolation, Product } from '../types'; +import { AI_DISCLAIMER, PRODUCT_DEFAULTS } from '../utils/constants'; +import { useWindowSize } from '../hooks/useWindowSize'; +import { isContentFilterError, getErrorMessage, copyToClipboard } from '../utils/errorUtils'; +import { downloadImageWithBanner } from '../utils/imageDownload'; interface InlineContentPreviewProps { content: GeneratedContent; @@ -31,20 +35,7 @@ interface InlineContentPreviewProps { imageGenerationEnabled?: boolean; } -// Custom hook for responsive breakpoints -function useWindowSize() { - const [windowWidth, setWindowWidth] = useState(typeof window !== 'undefined' ? window.innerWidth : 1200); - - useEffect(() => { - const handleResize = () => setWindowWidth(window.innerWidth); - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, []); - - return windowWidth; -} - -export function InlineContentPreview({ +export const InlineContentPreview = React.memo(function InlineContentPreview({ content, onRegenerate, isLoading, @@ -57,32 +48,6 @@ export function InlineContentPreview({ const isSmall = windowWidth < 768; - // Helper to detect content filter errors - const isContentFilterError = (errorMessage?: string): boolean => { - if (!errorMessage) return false; - const filterPatterns = [ - 'content_filter', 'ContentFilter', 'content management policy', - 'ResponsibleAI', 'responsible_ai_policy', 'content filtering', - 'filtered', 'safety system', 'self_harm', 'sexual', 'violence', 'hate', - ]; - return filterPatterns.some(pattern => - errorMessage.toLowerCase().includes(pattern.toLowerCase()) - ); - }; - - const getErrorMessage = (errorMessage?: string): { title: string; description: string } => { - if (isContentFilterError(errorMessage)) { - return { - title: 'Content Filtered', - description: 'Your request was blocked by content safety filters. Please try modifying your creative brief.', - }; - } - return { - title: 'Generation Failed', - description: errorMessage || 'An error occurred. Please try again.', - }; - }; - const handleCopyText = () => { const textToCopy = [ text_content?.headline && `✨ ${text_content.headline} ✨`, @@ -90,103 +55,18 @@ export function InlineContentPreview({ text_content?.tagline, ].filter(Boolean).join('\n\n'); - navigator.clipboard.writeText(textToCopy); + copyToClipboard(textToCopy); setCopied(true); setTimeout(() => setCopied(false), 2000); }; - const handleDownloadImage = async () => { + const handleDownloadImage = () => { if (!image_content?.image_url) return; - - try { - const canvas = document.createElement('canvas'); - const ctx = canvas.getContext('2d'); - if (!ctx) return; - - const img = new Image(); - img.crossOrigin = 'anonymous'; - - img.onload = () => { - // Calculate banner height - const bannerHeight = Math.max(60, img.height * 0.1); - const padding = Math.max(16, img.width * 0.03); - - // Set canvas size to include bottom banner - canvas.width = img.width; - canvas.height = img.height + bannerHeight; - - // Draw the image at the top - ctx.drawImage(img, 0, 0); - - // Draw white banner at the bottom - ctx.fillStyle = '#ffffff'; - ctx.fillRect(0, img.height, img.width, bannerHeight); - - // Draw banner border line - ctx.strokeStyle = '#e5e5e5'; - ctx.lineWidth = 1; - ctx.beginPath(); - ctx.moveTo(0, img.height); - ctx.lineTo(img.width, img.height); - ctx.stroke(); - - // Draw text in the banner - const headlineText = selectedProduct?.product_name || text_content?.headline || 'Your Product'; - const headlineFontSize = Math.max(18, Math.min(36, img.width * 0.04)); - const taglineText = text_content?.tagline || ''; - const taglineFontSize = Math.max(12, Math.min(20, img.width * 0.025)); - - // Draw headline - ctx.font = `600 ${headlineFontSize}px Georgia, serif`; - ctx.fillStyle = '#1a1a1a'; - ctx.fillText(headlineText, padding, img.height + padding + headlineFontSize * 0.8, img.width - padding * 2); - - // Draw tagline if available - if (taglineText) { - ctx.font = `400 italic ${taglineFontSize}px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`; - ctx.fillStyle = '#666666'; - ctx.fillText(taglineText, padding, img.height + padding + headlineFontSize + taglineFontSize * 0.8 + 4, img.width - padding * 2); - } - - canvas.toBlob((blob) => { - if (blob) { - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = 'generated-marketing-image.png'; - link.click(); - URL.revokeObjectURL(url); - } - }, 'image/png'); - }; - - img.onerror = () => { - if (image_content?.image_url) { - const link = document.createElement('a'); - link.href = image_content.image_url; - link.download = 'generated-image.png'; - link.click(); - } - }; - - img.src = image_content.image_url; - } catch { - if (image_content?.image_url) { - const link = document.createElement('a'); - link.href = image_content.image_url; - link.download = 'generated-image.png'; - link.click(); - } - } + const headline = selectedProduct?.product_name || text_content?.headline || 'Your Product'; + downloadImageWithBanner(image_content.image_url, headline, text_content?.tagline); }; - // Get product display name - const getProductDisplayName = () => { - if (selectedProduct) { - return selectedProduct.product_name; - } - return text_content?.headline || 'Your Content'; - }; + const productDisplayName = selectedProduct?.product_name || text_content?.headline || 'Your Content'; return (
- ✨ Discover the serene elegance of {getProductDisplayName()}. + ✨ Discover the serene elegance of {productDisplayName}. )} @@ -299,7 +179,7 @@ export function InlineContentPreview({
{image_content.alt_text - AI-generated content may be incorrect + {AI_DISCLAIMER} {/* Collapsible Compliance Section */} @@ -512,9 +392,11 @@ export function InlineContentPreview({ )}
); -} +}); + +InlineContentPreview.displayName = 'InlineContentPreview'; -function ViolationCard({ violation }: { violation: ComplianceViolation }) { +const ViolationCard = React.memo(function ViolationCard({ violation }: { violation: ComplianceViolation }) { const getSeverityStyles = () => { switch (violation.severity) { case 'error': @@ -558,4 +440,6 @@ function ViolationCard({ violation }: { violation: ComplianceViolation }) {
); -} +}); + +ViolationCard.displayName = 'ViolationCard'; diff --git a/src/App/src/components/MessageBubble.tsx b/src/App/src/components/MessageBubble.tsx new file mode 100644 index 000000000..73d8b11ab --- /dev/null +++ b/src/App/src/components/MessageBubble.tsx @@ -0,0 +1,118 @@ +/** + * MessageBubble component - displays a single chat message + * Extracted from ChatPanel.tsx and wrapped with React.memo + */ + +import React, { useState } from 'react'; +import { + Text, + Badge, + Button, + Tooltip, + tokens, +} from '@fluentui/react-components'; +import { Copy20Regular } from '@fluentui/react-icons'; +import ReactMarkdown from 'react-markdown'; +import type { ChatMessage } from '../types'; +import { AI_DISCLAIMER } from '../utils/constants'; +import { copyToClipboard } from '../utils/errorUtils'; + +interface MessageBubbleProps { + message: ChatMessage; +} + +export const MessageBubble = React.memo(function MessageBubble({ message }: MessageBubbleProps) { + const isUser = message.role === 'user'; + const [copied, setCopied] = useState(false); + + const onCopy = () => { + copyToClipboard(message.content); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+ {/* Agent badge for assistant messages */} + {!isUser && message.agent && ( + + {message.agent} + + )} + + {/* Message content with markdown */} +
+ + {message.content} + + + {/* Footer for assistant messages */} + {!isUser && ( +
+ + {AI_DISCLAIMER} + + +
+ +
+
+ )} +
+
+ ); +}); + +MessageBubble.displayName = 'MessageBubble'; diff --git a/src/App/src/components/ProductCard.tsx b/src/App/src/components/ProductCard.tsx new file mode 100644 index 000000000..f5c520caf --- /dev/null +++ b/src/App/src/components/ProductCard.tsx @@ -0,0 +1,127 @@ +import React from 'react'; +import { Text, tokens } from '@fluentui/react-components'; +import { Box20Regular } from '@fluentui/react-icons'; +import type { Product } from '../types'; +import { PRODUCT_DEFAULTS } from '../utils/constants'; + +interface ProductCardProps { + product: Product; + /** Image/icon size in px */ + size?: 'small' | 'medium'; + /** Optional selection border */ + isSelected?: boolean; + /** Card click handler (omit for read-only cards) */ + onClick?: () => void; + disabled?: boolean; +} + +const SIZE_MAP = { + small: { img: 56, radius: '6px', textSize: 300 as const, priceSize: 200 as const }, + medium: { img: 80, radius: '8px', textSize: 400 as const, priceSize: 300 as const }, +}; + +export const ProductCard = React.memo(function ProductCard({ + product, + size = 'medium', + isSelected = false, + onClick, + disabled = false, +}: ProductCardProps) { + const { img, radius, textSize, priceSize } = SIZE_MAP[size]; + + return ( +
+ {/* Product image or fallback icon */} + {product.image_url ? ( + {product.product_name} + ) : ( +
+ +
+ )} + + {/* Product info */} +
+ + {product.product_name} + + + {product.tags || product.description || PRODUCT_DEFAULTS.fallbackTags} + + + ${product.price?.toFixed(2) || PRODUCT_DEFAULTS.fallbackPrice.toFixed(2)} USD + +
+
+ ); +}); + +ProductCard.displayName = 'ProductCard'; diff --git a/src/App/src/components/ProductReview.tsx b/src/App/src/components/ProductReview.tsx new file mode 100644 index 000000000..2e10db7e1 --- /dev/null +++ b/src/App/src/components/ProductReview.tsx @@ -0,0 +1,121 @@ +import React from 'react'; +import { + Button, + Text, + tokens, +} from '@fluentui/react-components'; +import { + Sparkle20Regular, +} from '@fluentui/react-icons'; +import type { Product } from '../types'; +import { AI_DISCLAIMER } from '../utils/constants'; +import { ProductCard } from './ProductCard'; + +interface ProductReviewProps { + products: Product[]; + onConfirm: () => void; + isAwaitingResponse?: boolean; + availableProducts?: Product[]; + onProductSelect?: (product: Product) => void; + disabled?: boolean; +} + +export const ProductReview = React.memo(function ProductReview({ + products, + onConfirm, + isAwaitingResponse = false, + availableProducts = [], + onProductSelect, + disabled = false, +}: ProductReviewProps) { + const displayProducts = availableProducts.length > 0 ? availableProducts : products; + const selectedProductIds = new Set(products.map(p => p.sku || p.product_name)); + + return ( +
+
+ + Here is the list of available paints: + +
+ + {displayProducts.length > 0 ? ( +
+ {displayProducts.map((product, index) => ( + onProductSelect?.(product)} + disabled={disabled} + /> + ))} +
+ ) : ( +
+ + No products available. + +
+ )} + + {displayProducts.length > 0 && ( +
+ + {products.length === 0 && ( + + Select a product to continue + + )} +
+ )} + +
+ + {AI_DISCLAIMER} + +
+
+ ); +}); + +ProductReview.displayName = 'ProductReview'; diff --git a/src/App/src/components/SelectedProductView.tsx b/src/App/src/components/SelectedProductView.tsx new file mode 100644 index 000000000..f2427c14e --- /dev/null +++ b/src/App/src/components/SelectedProductView.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { + Badge, + tokens, +} from '@fluentui/react-components'; +import { + Checkmark20Regular, +} from '@fluentui/react-icons'; +import type { Product } from '../types'; +import { ProductCard } from './ProductCard'; + +interface SelectedProductViewProps { + products: Product[]; +} + +export const SelectedProductView = React.memo(function SelectedProductView({ products }: SelectedProductViewProps) { + if (products.length === 0) return null; + + return ( +
+
+ } + > + Products Selected + +
+ +
+ {products.map((product, index) => ( + + ))} +
+
+ ); +}); + +SelectedProductView.displayName = 'SelectedProductView'; diff --git a/src/App/src/components/TypingIndicator.tsx b/src/App/src/components/TypingIndicator.tsx new file mode 100644 index 000000000..3a436ce76 --- /dev/null +++ b/src/App/src/components/TypingIndicator.tsx @@ -0,0 +1,70 @@ +/** + * TypingIndicator component - shows loading/thinking state + * Extracted from ChatPanel.tsx and wrapped with React.memo + */ + +import React from 'react'; +import { + Text, + Button, + Tooltip, + tokens, +} from '@fluentui/react-components'; +import { Stop24Regular } from '@fluentui/react-icons'; + +interface TypingIndicatorProps { + generationStatus?: string; + onStopGeneration?: () => void; +} + +export const TypingIndicator = React.memo(function TypingIndicator({ + generationStatus, + onStopGeneration, +}: TypingIndicatorProps) { + return ( +
+
+ + + + + +
+ + {generationStatus || 'Thinking...'} + + {onStopGeneration && ( + + + + )} +
+ ); +}); + +TypingIndicator.displayName = 'TypingIndicator'; diff --git a/src/app/frontend/src/components/WelcomeCard.tsx b/src/App/src/components/WelcomeCard.tsx similarity index 87% rename from src/app/frontend/src/components/WelcomeCard.tsx rename to src/App/src/components/WelcomeCard.tsx index ab5740708..2188aca99 100644 --- a/src/app/frontend/src/components/WelcomeCard.tsx +++ b/src/App/src/components/WelcomeCard.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { Card, Text, @@ -7,19 +8,16 @@ import FirstPromptIcon from '../styles/images/firstprompt.png'; import SecondPromptIcon from '../styles/images/secondprompt.png'; interface SuggestionCard { - title: string; prompt: string; icon: string; } const suggestions: SuggestionCard[] = [ { - title: "I need to create a social media post about paint products for home remodels. The campaign is titled \"Brighten Your Springtime\" and the audience is new homeowners. I need marketing copy plus an image. The image should be an informal living room with tasteful furnishings.", prompt: "I need to create a social media post about paint products for home remodels. The campaign is titled \"Brighten Your Springtime\" and the audience is new homeowners. I need marketing copy plus an image. The image should be an informal living room with tasteful furnishings.", icon: FirstPromptIcon, }, { - title: "Generate a social media campaign with ad copy and an image. This is for \"Back to School\" and the audience is parents of school age children. Tone is playful and humorous. The image must have minimal kids accessories in a children's bedroom. Show the room in a wide view.", prompt: "Generate a social media campaign with ad copy and an image. This is for \"Back to School\" and the audience is parents of school age children. Tone is playful and humorous. The image must have minimal kids accessories in a children's bedroom. Show the room in a wide view.", icon: SecondPromptIcon, } @@ -30,7 +28,7 @@ interface WelcomeCardProps { currentInput?: string; } -export function WelcomeCard({ onSuggestionClick, currentInput = '' }: WelcomeCardProps) { +export const WelcomeCard = React.memo(function WelcomeCard({ onSuggestionClick, currentInput = '' }: WelcomeCardProps) { const selectedIndex = suggestions.findIndex(s => s.prompt === currentInput); return (
- {suggestion.title} + {suggestion.prompt}
@@ -155,4 +153,6 @@ export function WelcomeCard({ onSuggestionClick, currentInput = '' }: WelcomeCar ); -} +}); + +WelcomeCard.displayName = 'WelcomeCard'; diff --git a/src/App/src/hooks/useChatOrchestrator.ts b/src/App/src/hooks/useChatOrchestrator.ts new file mode 100644 index 000000000..0853ba159 --- /dev/null +++ b/src/App/src/hooks/useChatOrchestrator.ts @@ -0,0 +1,185 @@ +/** + * Custom hook for chat message orchestration + * Extracts handleSendMessage logic from App.tsx + */ + +import { useCallback } from 'react'; +import { useAppDispatch, useAppSelector } from '../store/hooks'; +import { + addMessage, + setIsLoading, + setGenerationStatus, + incrementHistoryRefresh, + setConversationTitle, +} from '../store/chatSlice'; +import { + setPendingBrief, + setConfirmedBrief, + setSelectedProducts, + setAvailableProducts, + setGeneratedContent, +} from '../store/contentSlice'; +import { sendMessage, pollTaskStatus } from '../api'; +import { createMessage, createErrorMessage, createCancelMessage } from '../utils/messageUtils'; +import { parseEventContent, mergeRegenerationResult } from '../utils/contentParser'; +import type { CreativeBrief, Product, GeneratedContent } from '../types'; + +export function useChatOrchestrator( + abortControllerRef: React.MutableRefObject +) { + const dispatch = useAppDispatch(); + const conversationId = useAppSelector(state => state.chat.conversationId); + const conversationTitle = useAppSelector(state => state.chat.conversationTitle); + const userId = useAppSelector(state => state.app.userId); + const confirmedBrief = useAppSelector(state => state.content.confirmedBrief); + const selectedProducts = useAppSelector(state => state.content.selectedProducts); + const availableProducts = useAppSelector(state => state.content.availableProducts); + const generatedContent = useAppSelector(state => state.content.generatedContent); + + const handleSendMessage = useCallback(async (content: string) => { + const userMessage = createMessage('user', content); + dispatch(addMessage(userMessage)); + dispatch(setIsLoading(true)); + dispatch(setGenerationStatus('Processing your request...')); + + // Create new abort controller for this request + abortControllerRef.current = new AbortController(); + const signal = abortControllerRef.current.signal; + + try { + let productsToSend = selectedProducts; + if (generatedContent && confirmedBrief && availableProducts.length > 0) { + const contentLower = content.toLowerCase(); + const mentionedProduct = availableProducts.find(p => + contentLower.includes(p.product_name.toLowerCase()) + ); + if (mentionedProduct && mentionedProduct.product_name !== selectedProducts[0]?.product_name) { + productsToSend = [mentionedProduct]; + } + } + + // Send message with context + const response = await sendMessage({ + conversation_id: conversationId, + user_id: userId, + message: content, + ...(confirmedBrief && { brief: confirmedBrief }), + ...(productsToSend.length > 0 && { selected_products: productsToSend }), + ...(generatedContent && { has_generated_content: true }), + }, signal); + + // Handle response based on action_type + switch (response.action_type) { + case 'brief_parsed': { + const brief = response.data?.brief as CreativeBrief | undefined; + const title = response.data?.generated_title as string | undefined; + if (brief) dispatch(setPendingBrief(brief)); + if (title && !conversationTitle) dispatch(setConversationTitle(title)); + break; + } + + case 'clarification_needed': { + const brief = response.data?.brief as CreativeBrief | undefined; + if (brief) dispatch(setPendingBrief(brief)); + break; + } + + case 'brief_confirmed': { + const brief = response.data?.brief as CreativeBrief | undefined; + const products = response.data?.products as Product[] | undefined; + if (brief) { + dispatch(setConfirmedBrief(brief)); + dispatch(setPendingBrief(null)); + } + if (products) dispatch(setAvailableProducts(products)); + break; + } + + case 'products_selected': { + const products = response.data?.products as Product[] | undefined; + if (products) dispatch(setSelectedProducts(products)); + break; + } + + case 'content_generated': + case 'image_regenerated': { + const gc = response.data?.generated_content as GeneratedContent | undefined; + if (gc) dispatch(setGeneratedContent(gc)); + break; + } + + case 'regeneration_started': { + const taskId = response.data?.task_id as string; + if (!taskId) throw new Error('No task_id received for regeneration'); + + dispatch(setGenerationStatus('Regenerating image...')); + + for await (const event of pollTaskStatus(taskId, signal)) { + if (event.type === 'heartbeat') { + const statusMessage = (event.content as string) || 'Regenerating image...'; + const elapsed = (event as { elapsed?: number }).elapsed || 0; + dispatch(setGenerationStatus(elapsed > 0 ? `${statusMessage} (${elapsed}s)` : statusMessage)); + } else if (event.type === 'agent_response' && event.is_final) { + const result = parseEventContent(event.content); + + // Update selected products if backend provided new ones + const newProducts = result?.selected_products as Product[] | undefined; + if (newProducts && newProducts.length > 0) { + dispatch(setSelectedProducts(newProducts)); + } + + // Update confirmed brief if backend provided an updated one + const updatedBrief = result?.updated_brief as CreativeBrief | undefined; + if (updatedBrief) dispatch(setConfirmedBrief(updatedBrief)); + + // Merge regeneration result with existing generated content + dispatch(setGeneratedContent(mergeRegenerationResult(result, generatedContent))); + } else if (event.type === 'error') { + throw new Error(event.content || 'Regeneration failed'); + } + } + + dispatch(setGenerationStatus('')); + break; + } + + case 'start_over': { + dispatch(setPendingBrief(null)); + dispatch(setConfirmedBrief(null)); + dispatch(setSelectedProducts([])); + dispatch(setGeneratedContent(null)); + break; + } + + case 'rai_blocked': + case 'error': + case 'chat_response': + default: + break; + } + + // Add assistant message from response + if (response.message) { + dispatch(addMessage(createMessage('assistant', response.message))); + } + + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + dispatch(addMessage(createCancelMessage())); + } else { + dispatch(addMessage(createErrorMessage())); + } + } finally { + dispatch(setIsLoading(false)); + dispatch(setGenerationStatus('')); + abortControllerRef.current = null; + dispatch(incrementHistoryRefresh()); + } + }, [ + dispatch, conversationId, userId, conversationTitle, + confirmedBrief, selectedProducts, generatedContent, + availableProducts, abortControllerRef, + ]); + + return { handleSendMessage }; +} diff --git a/src/App/src/hooks/useContentGeneration.ts b/src/App/src/hooks/useContentGeneration.ts new file mode 100644 index 000000000..d8f34e283 --- /dev/null +++ b/src/App/src/hooks/useContentGeneration.ts @@ -0,0 +1,75 @@ +/** + * Custom hook for content generation orchestration + * Extracts handleGenerateContent logic from App.tsx + */ + +import { useCallback } from 'react'; +import { useAppDispatch, useAppSelector } from '../store/hooks'; +import { + addMessage, + setIsLoading, + setGenerationStatus, +} from '../store/chatSlice'; +import { setGeneratedContent } from '../store/contentSlice'; +import { streamGenerateContent } from '../api'; +import { createErrorMessage, createCancelMessage } from '../utils/messageUtils'; +import { parseGeneratedContent, parseEventContent } from '../utils/contentParser'; + +export function useContentGeneration( + abortControllerRef: React.MutableRefObject +) { + const dispatch = useAppDispatch(); + const conversationId = useAppSelector(state => state.chat.conversationId); + const userId = useAppSelector(state => state.app.userId); + const confirmedBrief = useAppSelector(state => state.content.confirmedBrief); + const selectedProducts = useAppSelector(state => state.content.selectedProducts); + const imageGenerationEnabled = useAppSelector(state => state.app.imageGenerationEnabled); + + const handleGenerateContent = useCallback(async () => { + if (!confirmedBrief) return; + + dispatch(setIsLoading(true)); + dispatch(setGenerationStatus('Starting content generation...')); + + // Create new abort controller for this request + abortControllerRef.current = new AbortController(); + const signal = abortControllerRef.current.signal; + + try { + for await (const event of streamGenerateContent({ + conversation_id: conversationId, + user_id: userId, + brief: confirmedBrief, + products: selectedProducts, + generate_images: imageGenerationEnabled, + }, signal)) { + + if (event.type === 'heartbeat') { + const statusMessage = (event.content as string) || 'Generating content...'; + const elapsed = (event as { elapsed?: number }).elapsed || 0; + dispatch(setGenerationStatus(elapsed > 0 ? `${statusMessage} (${elapsed}s)` : statusMessage)); + } else if (event.type === 'agent_response' && event.is_final) { + const result = parseEventContent(event.content); + const generated = parseGeneratedContent(result); + dispatch(setGeneratedContent(generated)); + } else if (event.type === 'error') { + throw new Error(event.content || 'Generation failed'); + } + } + + dispatch(setGenerationStatus('')); + } catch (error) { + if (error instanceof Error && error.name === 'AbortError') { + dispatch(addMessage(createCancelMessage('Content generation stopped.'))); + } else { + dispatch(addMessage(createErrorMessage('Sorry, there was an error generating content. Please try again.'))); + } + } finally { + dispatch(setIsLoading(false)); + dispatch(setGenerationStatus('')); + abortControllerRef.current = null; + } + }, [dispatch, confirmedBrief, selectedProducts, conversationId, userId, imageGenerationEnabled, abortControllerRef]); + + return { handleGenerateContent }; +} diff --git a/src/App/src/hooks/useConversationActions.ts b/src/App/src/hooks/useConversationActions.ts new file mode 100644 index 000000000..7a2e370aa --- /dev/null +++ b/src/App/src/hooks/useConversationActions.ts @@ -0,0 +1,178 @@ +/** + * Custom hook for conversation action handlers + * Extracts brief confirm/cancel, product selection, conversation management from App.tsx + */ + +import { useCallback } from 'react'; +import { useAppDispatch, useAppSelector } from '../store/hooks'; +import { + addMessage, + setConversationId, + setConversationTitle, + setMessages, + resetChat, +} from '../store/chatSlice'; +import { + setPendingBrief, + setConfirmedBrief, + setSelectedProducts, + setAvailableProducts, + setGeneratedContent, + resetContent, +} from '../store/contentSlice'; +import { sendMessage } from '../api'; +import { httpClient } from '../utils/httpClient'; +import { createMessage } from '../utils/messageUtils'; +import { restoreGeneratedContent } from '../utils/contentParser'; +import type { ChatMessage, CreativeBrief, Product } from '../types'; + +async function fetchProducts(): Promise { + try { + const data = await httpClient.get<{ products: Product[] }>('/products'); + return data.products || []; + } catch { + return []; + } +} + +export function useConversationActions( + abortControllerRef: React.MutableRefObject +) { + const dispatch = useAppDispatch(); + const conversationId = useAppSelector(state => state.chat.conversationId); + const userId = useAppSelector(state => state.app.userId); + const pendingBrief = useAppSelector(state => state.content.pendingBrief); + const availableProducts = useAppSelector(state => state.content.availableProducts); + const selectedProducts = useAppSelector(state => state.content.selectedProducts); + + const handleBriefConfirm = useCallback(async () => { + if (!pendingBrief) return; + + try { + const response = await sendMessage({ + conversation_id: conversationId, + user_id: userId, + action: 'confirm_brief', + brief: pendingBrief, + }); + + // Update state based on response + if (response.action_type === 'brief_confirmed') { + const brief = response.data?.brief as CreativeBrief | undefined; + if (brief) { + dispatch(setConfirmedBrief(brief)); + } else { + dispatch(setConfirmedBrief(pendingBrief)); + } + dispatch(setPendingBrief(null)); + + // Fetch products separately after confirmation + const products = await fetchProducts(); + dispatch(setAvailableProducts(products)); + } + + // Add assistant message + if (response.message) { + dispatch(addMessage(createMessage('assistant', response.message, 'ProductAgent'))); + } + } catch { + // Brief confirmation failed — no action needed + } + }, [dispatch, conversationId, userId, pendingBrief]); + + const handleBriefCancel = useCallback(async () => { + dispatch(setPendingBrief(null)); + dispatch(addMessage( + createMessage('assistant', 'No problem. Please provide your creative brief again or ask me any questions.') + )); + }, [dispatch]); + + const handleProductSelect = useCallback((product: Product) => { + const isSelected = selectedProducts.some( + p => (p.sku || p.product_name) === (product.sku || product.product_name) + ); + + if (isSelected) { + dispatch(setSelectedProducts([])); + } else { + // Single selection mode + dispatch(setSelectedProducts([product])); + } + }, [dispatch, selectedProducts]); + + const handleStopGeneration = useCallback(() => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + }, [abortControllerRef]); + + const handleSelectConversation = useCallback(async (selectedConversationId: string) => { + try { + const data = await httpClient.get>(`/conversations/${selectedConversationId}?user_id=${encodeURIComponent(userId)}`); + dispatch(setConversationId(selectedConversationId)); + dispatch(setConversationTitle(null)); + + const loadedMessages: ChatMessage[] = ((data.messages as Array<{ role: string; content: string; timestamp?: string; agent?: string }>) || []).map( + (msg, index: number) => ({ + id: `${selectedConversationId}-${index}`, + role: msg.role as 'user' | 'assistant', + content: msg.content, + timestamp: msg.timestamp || new Date().toISOString(), + agent: msg.agent, + }) + ); + dispatch(setMessages(loadedMessages)); + + // Only set confirmedBrief if the brief was actually confirmed + const metadata = data.metadata as Record | undefined; + const briefWasConfirmed = metadata?.brief_confirmed || data.generated_content; + if (briefWasConfirmed && data.brief) { + dispatch(setConfirmedBrief(data.brief as CreativeBrief)); + dispatch(setPendingBrief(null)); + } else if (data.brief) { + dispatch(setPendingBrief(data.brief as CreativeBrief)); + dispatch(setConfirmedBrief(null)); + } else { + dispatch(setPendingBrief(null)); + dispatch(setConfirmedBrief(null)); + } + + // Restore availableProducts for product/color name detection + if (data.brief && availableProducts.length === 0) { + const products = await fetchProducts(); + dispatch(setAvailableProducts(products)); + } + + if (data.generated_content) { + const gc = data.generated_content as Record; + dispatch(setGeneratedContent(restoreGeneratedContent(gc))); + + const selectedProds = gc.selected_products; + if (Array.isArray(selectedProds) && selectedProds.length > 0) { + dispatch(setSelectedProducts(selectedProds as Product[])); + } else { + dispatch(setSelectedProducts([])); + } + } else { + dispatch(setGeneratedContent(null)); + dispatch(setSelectedProducts([])); + } + } catch { + // Conversation load failed — no action needed + } + }, [dispatch, userId, availableProducts.length]); + + const handleNewConversation = useCallback(() => { + dispatch(resetChat()); + dispatch(resetContent()); + }, [dispatch]); + + return { + handleBriefConfirm, + handleBriefCancel, + handleProductSelect, + handleStopGeneration, + handleSelectConversation, + handleNewConversation, + }; +} diff --git a/src/App/src/hooks/useWindowSize.ts b/src/App/src/hooks/useWindowSize.ts new file mode 100644 index 000000000..3ceffbccc --- /dev/null +++ b/src/App/src/hooks/useWindowSize.ts @@ -0,0 +1,16 @@ +import { useState, useEffect } from 'react'; + +/** Returns current window width, updating on resize. */ +export function useWindowSize(): number { + const [windowWidth, setWindowWidth] = useState( + typeof window !== 'undefined' ? window.innerWidth : 1200 + ); + + useEffect(() => { + const handleResize = () => setWindowWidth(window.innerWidth); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, []); + + return windowWidth; +} diff --git a/src/app/frontend/src/main.tsx b/src/App/src/main.tsx similarity index 59% rename from src/app/frontend/src/main.tsx rename to src/App/src/main.tsx index e59c93df9..462ebe3e9 100644 --- a/src/app/frontend/src/main.tsx +++ b/src/App/src/main.tsx @@ -1,13 +1,17 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import { FluentProvider, webLightTheme } from '@fluentui/react-components'; +import { Provider } from 'react-redux'; +import { store } from './store/store'; import App from './App'; import './styles/global.css'; ReactDOM.createRoot(document.getElementById('root')!).render( - - - + + + + + ); diff --git a/src/App/src/store/appSlice.ts b/src/App/src/store/appSlice.ts new file mode 100644 index 000000000..ebf882e79 --- /dev/null +++ b/src/App/src/store/appSlice.ts @@ -0,0 +1,87 @@ +/** + * App slice - manages application-level state + * (user info, feature flags, UI toggles) + */ + +import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; +import type { AppConfig } from '../types'; +import { getAppConfig } from '../api'; +import { httpClient } from '../utils/httpClient'; + +interface AppState { + userId: string; + userName: string; + imageGenerationEnabled: boolean; + showChatHistory: boolean; +} + +const initialState: AppState = { + userId: '', + userName: '', + imageGenerationEnabled: true, + showChatHistory: true, +}; + +export const fetchAppConfig = createAsyncThunk( + 'app/fetchConfig', + async () => { + const config: AppConfig = await getAppConfig(); + return config; + } +); + +export const fetchCurrentUser = createAsyncThunk( + 'app/fetchCurrentUser', + async () => { + try { + const payload = await httpClient.fetchExternal; + }>>('/.auth/me'); + const userClaims = payload[0]?.user_claims || []; + const objectIdClaim = userClaims.find( + (claim) => + claim.typ === 'http://schemas.microsoft.com/identity/claims/objectidentifier' + ); + const nameClaim = userClaims.find( + (claim) => claim.typ === 'name' + ); + return { + userId: objectIdClaim?.val || 'anonymous', + userName: nameClaim?.val || '', + }; + } catch { + return { userId: 'anonymous', userName: '' }; + } + } +); + +const appSlice = createSlice({ + name: 'app', + initialState, + reducers: { + toggleChatHistory(state) { + state.showChatHistory = !state.showChatHistory; + }, + }, + extraReducers: (builder) => { + builder + .addCase(fetchAppConfig.fulfilled, (state, action) => { + state.imageGenerationEnabled = action.payload.enable_image_generation; + }) + .addCase(fetchAppConfig.rejected, (state) => { + // Default to enabled if config fetch fails + state.imageGenerationEnabled = true; + }) + .addCase(fetchCurrentUser.fulfilled, (state, action) => { + state.userId = action.payload.userId; + state.userName = action.payload.userName; + }) + .addCase(fetchCurrentUser.rejected, (state) => { + state.userId = 'anonymous'; + state.userName = ''; + }); + }, +}); + +export const { toggleChatHistory } = appSlice.actions; +export default appSlice.reducer; diff --git a/src/App/src/store/chatSlice.ts b/src/App/src/store/chatSlice.ts new file mode 100644 index 000000000..e754fb412 --- /dev/null +++ b/src/App/src/store/chatSlice.ts @@ -0,0 +1,73 @@ +/** + * Chat slice - manages conversation and messaging state + */ + +import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; +import { v4 as uuidv4 } from 'uuid'; +import type { ChatMessage } from '../types'; + +interface ChatState { + conversationId: string; + conversationTitle: string | null; + messages: ChatMessage[]; + isLoading: boolean; + generationStatus: string; + historyRefreshTrigger: number; +} + +const initialState: ChatState = { + conversationId: uuidv4(), + conversationTitle: null, + messages: [], + isLoading: false, + generationStatus: '', + historyRefreshTrigger: 0, +}; + +const chatSlice = createSlice({ + name: 'chat', + initialState, + reducers: { + setConversationId(state, action: PayloadAction) { + state.conversationId = action.payload; + }, + setConversationTitle(state, action: PayloadAction) { + state.conversationTitle = action.payload; + }, + addMessage(state, action: PayloadAction) { + state.messages.push(action.payload); + }, + setMessages(state, action: PayloadAction) { + state.messages = action.payload; + }, + setIsLoading(state, action: PayloadAction) { + state.isLoading = action.payload; + }, + setGenerationStatus(state, action: PayloadAction) { + state.generationStatus = action.payload; + }, + incrementHistoryRefresh(state) { + state.historyRefreshTrigger += 1; + }, + resetChat(state) { + state.conversationId = uuidv4(); + state.conversationTitle = null; + state.messages = []; + state.isLoading = false; + state.generationStatus = ''; + }, + }, +}); + +export const { + setConversationId, + setConversationTitle, + addMessage, + setMessages, + setIsLoading, + setGenerationStatus, + incrementHistoryRefresh, + resetChat, +} = chatSlice.actions; + +export default chatSlice.reducer; diff --git a/src/App/src/store/contentSlice.ts b/src/App/src/store/contentSlice.ts new file mode 100644 index 000000000..09b4ba7fe --- /dev/null +++ b/src/App/src/store/contentSlice.ts @@ -0,0 +1,62 @@ +/** + * Content slice - manages creative brief, product selection, and generated content state + */ + +import { createSlice, type PayloadAction } from '@reduxjs/toolkit'; +import type { CreativeBrief, Product, GeneratedContent } from '../types'; + +interface ContentState { + pendingBrief: CreativeBrief | null; + confirmedBrief: CreativeBrief | null; + selectedProducts: Product[]; + availableProducts: Product[]; + generatedContent: GeneratedContent | null; +} + +const initialState: ContentState = { + pendingBrief: null, + confirmedBrief: null, + selectedProducts: [], + availableProducts: [], + generatedContent: null, +}; + +const contentSlice = createSlice({ + name: 'content', + initialState, + reducers: { + setPendingBrief(state, action: PayloadAction) { + state.pendingBrief = action.payload; + }, + setConfirmedBrief(state, action: PayloadAction) { + state.confirmedBrief = action.payload; + }, + setSelectedProducts(state, action: PayloadAction) { + state.selectedProducts = action.payload; + }, + setAvailableProducts(state, action: PayloadAction) { + state.availableProducts = action.payload; + }, + setGeneratedContent(state, action: PayloadAction) { + state.generatedContent = action.payload; + }, + resetContent(state) { + state.pendingBrief = null; + state.confirmedBrief = null; + state.selectedProducts = []; + state.availableProducts = []; + state.generatedContent = null; + }, + }, +}); + +export const { + setPendingBrief, + setConfirmedBrief, + setSelectedProducts, + setAvailableProducts, + setGeneratedContent, + resetContent, +} = contentSlice.actions; + +export default contentSlice.reducer; diff --git a/src/App/src/store/hooks.ts b/src/App/src/store/hooks.ts new file mode 100644 index 000000000..4139570b7 --- /dev/null +++ b/src/App/src/store/hooks.ts @@ -0,0 +1,9 @@ +/** + * Typed Redux hooks for use throughout the application + */ + +import { useDispatch, useSelector } from 'react-redux'; +import type { RootState, AppDispatch } from './store'; + +export const useAppDispatch = useDispatch.withTypes(); +export const useAppSelector = useSelector.withTypes(); diff --git a/src/App/src/store/store.ts b/src/App/src/store/store.ts new file mode 100644 index 000000000..450c7d7b1 --- /dev/null +++ b/src/App/src/store/store.ts @@ -0,0 +1,19 @@ +/** + * Redux store configuration + */ + +import { configureStore } from '@reduxjs/toolkit'; +import appReducer from './appSlice'; +import chatReducer from './chatSlice'; +import contentReducer from './contentSlice'; + +export const store = configureStore({ + reducer: { + app: appReducer, + chat: chatReducer, + content: contentReducer, + }, +}); + +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; diff --git a/src/app/frontend/src/styles/global.css b/src/App/src/styles/global.css similarity index 83% rename from src/app/frontend/src/styles/global.css rename to src/App/src/styles/global.css index 6780b41e8..2b24512f0 100644 --- a/src/app/frontend/src/styles/global.css +++ b/src/App/src/styles/global.css @@ -195,66 +195,6 @@ html, body, #root { Input Wrapper - Coral Style ============================================ */ -.input-wrapper { - margin: 0 16px 16px 16px; - display: inline-flex; - flex-direction: column; - gap: 8px; - align-items: stretch; - width: calc(100% - 32px); - padding: 8px; - border-radius: var(--borderRadiusLarge, 8px); - background-color: var(--colorNeutralBackground1, #ffffff); - border: 1px solid var(--colorNeutralStroke1, #d1d1d1); - transition: border-color 0.2s ease-in-out; - position: relative; - box-sizing: border-box; -} - -.input-wrapper:hover { - border-color: var(--colorNeutralStroke1Hover, #c7c7c7); -} - -.input-wrapper:active, -.input-wrapper.focused { - border-color: var(--colorNeutralStroke1Pressed, #b3b3b3); -} - -/* Input Field */ -.input-field { - resize: none; - overflow-y: scroll; - height: auto; - min-height: 24px; - max-height: 150px; - padding: 8px; - background-color: transparent; - border: none; - outline: none; - font-family: var(--fontFamilyBase); - font-size: var(--fontSizeBase300, 14px); - font-weight: var(--fontWeightRegular, 400); - color: var(--colorNeutralForeground1, #242424); - line-height: 1.5; - box-sizing: border-box; -} - -/* Focus Indicator Line */ -.focus-indicator { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 2px; - background-color: var(--colorCompoundBrandStroke, #0078d4); - transform: scaleX(0); - transition: transform 0.2s ease-in-out; -} - -.input-wrapper.focused .focus-indicator { - transform: scaleX(1); -} - /* ============================================ Assistant Message Footer ============================================ */ @@ -316,22 +256,6 @@ html, body, #root { animation-delay: 0.4s; } -/* ============================================ - Scroll Button - ============================================ */ - -.scroll-to-bottom { - position: absolute; - left: 50%; - transform: translateX(-50%); - z-index: 1000; - border-radius: 50%; - padding: 8px; - cursor: pointer; - box-shadow: var(--shadow8); - margin-bottom: 16px; -} - /* ============================================ Animations ============================================ */ @@ -429,30 +353,6 @@ pre code.language-bash { background-color: transparent; } -/* ============================================ - Loading Spinner - ============================================ */ - -.loading-spinner { - width: 16px; - height: 16px; - border: 2px solid var(--colorNeutralStroke3, #f3f3f3); - border-top: 2px solid var(--colorBrandForeground1, #0078d4); - border-radius: 50%; - animation: spin 1s linear infinite; -} - -.loading-conversation { - color: var(--colorNeutralForeground2); -} - -.loading-conversation .spinner { - border: 2px solid var(--colorNeutralStroke3); - border-top: 2px solid var(--colorBrandForeground1); - border-radius: 50%; - animation: spin 1s linear infinite; -} - /* ============================================ Compliance Severity Colors ============================================ */ diff --git a/src/app/frontend/src/styles/images/SamplePrompt.png b/src/App/src/styles/images/SamplePrompt.png similarity index 100% rename from src/app/frontend/src/styles/images/SamplePrompt.png rename to src/App/src/styles/images/SamplePrompt.png diff --git a/src/app/frontend/src/styles/images/contoso.svg b/src/App/src/styles/images/contoso.svg similarity index 100% rename from src/app/frontend/src/styles/images/contoso.svg rename to src/App/src/styles/images/contoso.svg diff --git a/src/app/frontend/src/styles/images/firstprompt.png b/src/App/src/styles/images/firstprompt.png similarity index 100% rename from src/app/frontend/src/styles/images/firstprompt.png rename to src/App/src/styles/images/firstprompt.png diff --git a/src/app/frontend/src/styles/images/secondprompt.png b/src/App/src/styles/images/secondprompt.png similarity index 100% rename from src/app/frontend/src/styles/images/secondprompt.png rename to src/App/src/styles/images/secondprompt.png diff --git a/src/app/frontend/src/types/index.ts b/src/App/src/types/index.ts similarity index 78% rename from src/app/frontend/src/types/index.ts rename to src/App/src/types/index.ts index 33de59481..cc6747862 100644 --- a/src/app/frontend/src/types/index.ts +++ b/src/App/src/types/index.ts @@ -47,14 +47,6 @@ export interface ChatMessage { violations?: ComplianceViolation[]; } -export interface Conversation { - id: string; - user_id: string; - messages: ChatMessage[]; - brief?: CreativeBrief; - updated_at: string; -} - export interface AgentResponse { type: 'agent_response' | 'error' | 'status' | 'heartbeat'; agent?: string; @@ -72,29 +64,6 @@ export interface AgentResponse { }; } -export interface BrandGuidelines { - tone: string; - voice: string; - primary_color: string; - secondary_color: string; - prohibited_words: string[]; - required_disclosures: string[]; - max_headline_length: number; - max_body_length: number; - require_cta: boolean; -} - -export interface ParsedBriefResponse { - brief?: CreativeBrief; - requires_confirmation: boolean; - requires_clarification?: boolean; - clarifying_questions?: string; - rai_blocked?: boolean; - message: string; - conversation_id?: string; - generated_title?: string; -} - export interface GeneratedContent { text_content?: { headline?: string; diff --git a/src/App/src/utils/constants.ts b/src/App/src/utils/constants.ts new file mode 100644 index 000000000..dbd25aae4 --- /dev/null +++ b/src/App/src/utils/constants.ts @@ -0,0 +1,42 @@ +/** + * Shared constants used across components + */ + +import type { CreativeBrief } from '../types'; + +/** Canonical field display order and labels for CreativeBrief */ +export const BRIEF_FIELD_CONFIG: { key: keyof CreativeBrief; label: string }[] = [ + { key: 'overview', label: 'Overview' }, + { key: 'objectives', label: 'Objectives' }, + { key: 'target_audience', label: 'Target Audience' }, + { key: 'key_message', label: 'Key Message' }, + { key: 'tone_and_style', label: 'Tone & Style' }, + { key: 'visual_guidelines', label: 'Visual Guidelines' }, + { key: 'deliverable', label: 'Deliverable' }, + { key: 'timelines', label: 'Timelines' }, + { key: 'cta', label: 'Call to Action' }, +]; + +/** All brief field keys in display order */ +export const BRIEF_FIELD_KEYS: (keyof CreativeBrief)[] = BRIEF_FIELD_CONFIG.map(f => f.key); + +/** Map from field key to label */ +export const BRIEF_FIELD_LABELS: Record = Object.fromEntries( + BRIEF_FIELD_CONFIG.map(f => [f.key, f.label]) +) as Record; + +/** Default product fallback values */ +export const PRODUCT_DEFAULTS = { + fallbackTags: 'soft white, airy, minimal, fresh', + fallbackPrice: 59.95, + fallbackAltText: 'Generated marketing image', +} as const; + +/** Polling configuration for content generation */ +export const POLLING_CONFIG = { + maxAttempts: 600, + intervalMs: 1000, +} as const; + +/** Standard AI disclaimer shown in multiple components */ +export const AI_DISCLAIMER = 'AI-generated content may be incorrect'; diff --git a/src/App/src/utils/contentParser.ts b/src/App/src/utils/contentParser.ts new file mode 100644 index 000000000..fe35b508b --- /dev/null +++ b/src/App/src/utils/contentParser.ts @@ -0,0 +1,133 @@ +/** + * Content parsing utilities for processing API responses + */ + +import type { GeneratedContent } from '../types'; +import { PRODUCT_DEFAULTS } from './constants'; + +/** + * Convert Azure blob storage URLs to proxy API URLs + */ +export function convertBlobUrl(imageUrl: string): string { + if (imageUrl && imageUrl.includes('blob.core.windows.net')) { + const parts = imageUrl.split('/'); + const filename = parts[parts.length - 1]; + const convId = parts[parts.length - 2]; + return `/api/images/${convId}/${filename}`; + } + return imageUrl; +} + +/** + * Parse text_content which may be a JSON string or object + */ +export function parseTextContent(raw: unknown): Record | undefined { + if (typeof raw === 'string') { + try { + return JSON.parse(raw); + } catch { + return undefined; + } + } + return raw as Record | undefined; +} + +/** + * Parse event content string to result object + */ +export function parseEventContent(content: string | unknown): Record { + if (typeof content === 'string') { + try { + return JSON.parse(content); + } catch { + return {}; + } + } + return (content as Record) || {}; +} + +/** + * Parse the final agent response into a GeneratedContent object + */ +export function parseGeneratedContent(result: Record): GeneratedContent { + let imageUrl = result?.image_url as string | undefined; + if (imageUrl) { + imageUrl = convertBlobUrl(imageUrl); + } + + const textContent = parseTextContent(result?.text_content); + + return { + text_content: textContent ? { + headline: textContent.headline as string | undefined, + body: textContent.body as string | undefined, + cta_text: textContent.cta as string | undefined, + } : { + headline: result?.headline as string | undefined, + body: result?.body as string | undefined, + cta_text: result?.cta as string | undefined, + }, + image_content: imageUrl ? { + image_url: imageUrl, + prompt_used: result?.image_prompt as string | undefined, + alt_text: (result?.image_revised_prompt as string) || PRODUCT_DEFAULTS.fallbackAltText, + } : undefined, + violations: (result?.violations as unknown as GeneratedContent['violations']) || [], + requires_modification: (result?.requires_modification as boolean) || false, + }; +} + +/** + * Merge a regeneration result into existing generated content. + * Keeps existing text/image if not present in the new result. + */ +export function mergeRegenerationResult( + result: Record, + existing: GeneratedContent | null, +): GeneratedContent { + const parsed = parseGeneratedContent(result); + return { + text_content: parsed.text_content?.headline ? parsed.text_content : existing?.text_content, + image_content: parsed.image_content || existing?.image_content, + violations: existing?.violations || [], + requires_modification: existing?.requires_modification || false, + }; +} + +/** + * Restore GeneratedContent from a persisted conversation payload. + * Handles image_base64, tagline, error fields etc. that aren't in the live generation path. + */ +export function restoreGeneratedContent(gc: Record): GeneratedContent { + let textContent = gc.text_content; + if (typeof textContent === 'string') { + textContent = parseTextContent(textContent); + } + + let imageUrl = gc.image_url as string | undefined; + if (imageUrl) imageUrl = convertBlobUrl(imageUrl); + if (!imageUrl && gc.image_base64) { + imageUrl = `data:image/png;base64,${gc.image_base64}`; + } + + const tc = textContent as Record | undefined; + + return { + text_content: tc ? { + headline: tc.headline as string | undefined, + body: tc.body as string | undefined, + cta_text: tc.cta as string | undefined, + tagline: tc.tagline as string | undefined, + } : undefined, + image_content: (imageUrl || gc.image_prompt) ? { + image_url: imageUrl, + prompt_used: gc.image_prompt as string | undefined, + alt_text: (gc.image_revised_prompt as string) || PRODUCT_DEFAULTS.fallbackAltText, + } : undefined, + violations: (gc.violations as GeneratedContent['violations']) || [], + requires_modification: (gc.requires_modification as boolean) || false, + error: gc.error as string | undefined, + image_error: gc.image_error as string | undefined, + text_error: gc.text_error as string | undefined, + }; +} diff --git a/src/App/src/utils/errorUtils.ts b/src/App/src/utils/errorUtils.ts new file mode 100644 index 000000000..df683f22c --- /dev/null +++ b/src/App/src/utils/errorUtils.ts @@ -0,0 +1,34 @@ +/** Content filter detection patterns */ +const FILTER_PATTERNS = [ + 'content_filter', 'contentfilter', 'content management policy', + 'responsibleai', 'responsible_ai_policy', 'content filtering', + 'filtered', 'safety system', 'self_harm', 'sexual', 'violence', 'hate', +]; + +/** Check if an error message indicates a content safety filter was triggered */ +export function isContentFilterError(errorMessage?: string): boolean { + if (!errorMessage) return false; + const lower = errorMessage.toLowerCase(); + return FILTER_PATTERNS.some(pattern => lower.includes(pattern)); +} + +/** Get a user-friendly title + description for an error */ +export function getErrorMessage(errorMessage?: string): { title: string; description: string } { + if (isContentFilterError(errorMessage)) { + return { + title: 'Content Filtered', + description: 'Your request was blocked by content safety filters. Please try modifying your creative brief.', + }; + } + return { + title: 'Generation Failed', + description: errorMessage || 'An error occurred. Please try again.', + }; +} + +/** Copy text to the clipboard, silently swallowing errors */ +export function copyToClipboard(text: string): Promise { + return navigator.clipboard.writeText(text).catch(() => { + // Clipboard write failed — no action needed + }); +} diff --git a/src/App/src/utils/httpClient.ts b/src/App/src/utils/httpClient.ts new file mode 100644 index 000000000..0acadaaaa --- /dev/null +++ b/src/App/src/utils/httpClient.ts @@ -0,0 +1,79 @@ +/** + * HTTP Client utility for making API requests + * Centralizes fetch logic with consistent error handling and request interceptors + */ + +const API_BASE = '/api'; + +type RequestInterceptor = (config: RequestInit) => RequestInit; + +class HttpClient { + private baseUrl: string; + private requestInterceptors: RequestInterceptor[] = []; + + constructor(baseUrl: string = API_BASE) { + this.baseUrl = baseUrl; + } + + /** Register a request interceptor to modify every outgoing request */ + addRequestInterceptor(interceptor: RequestInterceptor): void { + this.requestInterceptors.push(interceptor); + } + + private applyInterceptors(config: RequestInit): RequestInit { + return this.requestInterceptors.reduce((cfg, fn) => fn(cfg), config); + } + + private async request(method: string, path: string, options: RequestInit = {}): Promise { + const config = this.applyInterceptors({ ...options, method }); + const response = await fetch(`${this.baseUrl}${path}`, config); + if (!response.ok) { + throw new Error(`${method} ${path} failed: ${response.statusText}`); + } + return response.json(); + } + + async get(path: string, signal?: AbortSignal): Promise { + return this.request('GET', path, { signal }); + } + + async post(path: string, body: unknown, signal?: AbortSignal): Promise { + return this.request('POST', path, { + signal, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + } + + async delete(path: string, signal?: AbortSignal): Promise { + const config = this.applyInterceptors({ method: 'DELETE', signal }); + const response = await fetch(`${this.baseUrl}${path}`, config); + if (!response.ok) { + throw new Error(`DELETE ${path} failed: ${response.statusText}`); + } + } + + async put(path: string, body: unknown, signal?: AbortSignal): Promise { + return this.request('PUT', path, { + signal, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + } + + /** Fetch without base URL prefix, for non-API endpoints like /.auth/me */ + async fetchExternal(url: string, options?: RequestInit): Promise { + const config = this.applyInterceptors(options || {}); + const response = await fetch(url, config); + if (!response.ok) { + throw new Error(`${config.method || 'GET'} ${url} failed: ${response.statusText}`); + } + const contentType = response.headers.get('content-type') || ''; + if (!contentType.toLowerCase().includes('application/json')) { + throw new Error(`${config.method || 'GET'} ${url} returned non-JSON response (content-type: ${contentType || 'unknown'})`); + } + return response.json(); + } +} + +export const httpClient = new HttpClient(); diff --git a/src/App/src/utils/imageDownload.ts b/src/App/src/utils/imageDownload.ts new file mode 100644 index 000000000..8ef35c544 --- /dev/null +++ b/src/App/src/utils/imageDownload.ts @@ -0,0 +1,74 @@ +/** + * Download an image with a branded banner overlay. + * Falls back to a plain download if canvas rendering fails. + */ +export function downloadImageWithBanner( + imageUrl: string, + headline: string, + tagline?: string, +): void { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + if (!ctx) { + triggerDownload(imageUrl); + return; + } + + const img = new Image(); + img.crossOrigin = 'anonymous'; + + img.onload = () => { + const bannerHeight = Math.max(60, img.height * 0.1); + const padding = Math.max(16, img.width * 0.03); + + canvas.width = img.width; + canvas.height = img.height + bannerHeight; + + // Draw image + ctx.drawImage(img, 0, 0); + + // Draw white banner + ctx.fillStyle = '#ffffff'; + ctx.fillRect(0, img.height, img.width, bannerHeight); + + // Banner border + ctx.strokeStyle = '#e5e5e5'; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(0, img.height); + ctx.lineTo(img.width, img.height); + ctx.stroke(); + + // Headline + const headlineFontSize = Math.max(18, Math.min(36, img.width * 0.04)); + ctx.font = `600 ${headlineFontSize}px Georgia, serif`; + ctx.fillStyle = '#1a1a1a'; + ctx.fillText(headline, padding, img.height + padding + headlineFontSize * 0.8, img.width - padding * 2); + + // Tagline + if (tagline) { + const taglineFontSize = Math.max(12, Math.min(20, img.width * 0.025)); + ctx.font = `400 italic ${taglineFontSize}px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif`; + ctx.fillStyle = '#666666'; + ctx.fillText(tagline, padding, img.height + padding + headlineFontSize + taglineFontSize * 0.8 + 4, img.width - padding * 2); + } + + canvas.toBlob((blob) => { + if (blob) { + const url = URL.createObjectURL(blob); + triggerDownload(url, 'generated-marketing-image.png'); + URL.revokeObjectURL(url); + } + }, 'image/png'); + }; + + img.onerror = () => triggerDownload(imageUrl); + img.src = imageUrl; +} + +function triggerDownload(url: string, filename = 'generated-image.png'): void { + const link = document.createElement('a'); + link.href = url; + link.download = filename; + link.click(); +} diff --git a/src/App/src/utils/messageUtils.ts b/src/App/src/utils/messageUtils.ts new file mode 100644 index 000000000..ed5b3093a --- /dev/null +++ b/src/App/src/utils/messageUtils.ts @@ -0,0 +1,32 @@ +/** + * Message utility functions for creating ChatMessage objects + */ + +import { v4 as uuidv4 } from 'uuid'; +import type { ChatMessage } from '../types'; + +export function createMessage( + role: 'user' | 'assistant', + content: string, + agent?: string +): ChatMessage { + return { + id: uuidv4(), + role, + content, + timestamp: new Date().toISOString(), + ...(agent && { agent }), + }; +} + +export function createErrorMessage( + content: string = 'Sorry, there was an error processing your request. Please try again.' +): ChatMessage { + return createMessage('assistant', content); +} + +export function createCancelMessage( + content: string = 'Generation stopped.' +): ChatMessage { + return createMessage('assistant', content); +} diff --git a/src/app/frontend/src/vite-env.d.ts b/src/App/src/vite-env.d.ts similarity index 100% rename from src/app/frontend/src/vite-env.d.ts rename to src/App/src/vite-env.d.ts diff --git a/src/app/frontend/tsconfig.json b/src/App/tsconfig.json similarity index 100% rename from src/app/frontend/tsconfig.json rename to src/App/tsconfig.json diff --git a/src/app/frontend/tsconfig.node.json b/src/App/tsconfig.node.json similarity index 100% rename from src/app/frontend/tsconfig.node.json rename to src/App/tsconfig.node.json diff --git a/src/app/frontend/vite.config.ts b/src/App/vite.config.ts similarity index 100% rename from src/app/frontend/vite.config.ts rename to src/App/vite.config.ts diff --git a/src/app/frontend/package.json b/src/app/frontend/package.json deleted file mode 100644 index 2479885d7..000000000 --- a/src/app/frontend/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "content-generation-frontend", - "version": "1.0.0", - "description": "Frontend for Intelligent Content Generation Accelerator", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" - }, - "dependencies": { - "@fluentui/react-components": "^9.54.0", - "@fluentui/react-icons": "^2.0.245", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-markdown": "^9.0.1", - "uuid": "^10.0.0" - }, - "devDependencies": { - "@types/node": "^24.10.1", - "@types/react": "^18.3.3", - "@types/react-dom": "^18.3.0", - "@types/uuid": "^10.0.0", - "@typescript-eslint/eslint-plugin": "^7.15.0", - "@typescript-eslint/parser": "^7.15.0", - "@vitejs/plugin-react": "^4.3.1", - "eslint": "^8.57.0", - "eslint-plugin-react-hooks": "^4.6.2", - "eslint-plugin-react-refresh": "^0.4.7", - "typescript": "^5.5.2", - "vite": "^7.3.1" - } -} diff --git a/src/app/frontend/src/App.tsx b/src/app/frontend/src/App.tsx deleted file mode 100644 index bc6beff8c..000000000 --- a/src/app/frontend/src/App.tsx +++ /dev/null @@ -1,766 +0,0 @@ -import { useState, useCallback, useEffect, useRef } from 'react'; -import { - Text, - Avatar, - Button, - Tooltip, - tokens, -} from '@fluentui/react-components'; -import { - History24Regular, - History24Filled, -} from '@fluentui/react-icons'; -import { v4 as uuidv4 } from 'uuid'; - -import { ChatPanel } from './components/ChatPanel'; -import { ChatHistory } from './components/ChatHistory'; -import type { ChatMessage, CreativeBrief, Product, GeneratedContent } from './types'; -import ContosoLogo from './styles/images/contoso.svg'; - - -function App() { - const [conversationId, setConversationId] = useState(() => uuidv4()); - const [conversationTitle, setConversationTitle] = useState(null); - const [userId, setUserId] = useState(''); - const [userName, setUserName] = useState(''); - const [messages, setMessages] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [generationStatus, setGenerationStatus] = useState(''); - - // Feature flags from config - const [imageGenerationEnabled, setImageGenerationEnabled] = useState(true); - - // Brief confirmation flow - const [pendingBrief, setPendingBrief] = useState(null); - const [confirmedBrief, setConfirmedBrief] = useState(null); - - // Product selection - const [selectedProducts, setSelectedProducts] = useState([]); - const [availableProducts, setAvailableProducts] = useState([]); - - // Generated content - const [generatedContent, setGeneratedContent] = useState(null); - - // Trigger for refreshing chat history - const [historyRefreshTrigger, setHistoryRefreshTrigger] = useState(0); - - // Toggle for showing/hiding chat history panel - const [showChatHistory, setShowChatHistory] = useState(true); - - // Abort controller for cancelling ongoing requests - const abortControllerRef = useRef(null); - - // Fetch app config on mount - useEffect(() => { - const fetchConfig = async () => { - try { - const { getAppConfig } = await import('./api'); - const config = await getAppConfig(); - setImageGenerationEnabled(config.enable_image_generation); - } catch (err) { - console.error('Error fetching config:', err); - // Default to enabled if config fetch fails - setImageGenerationEnabled(true); - } - }; - fetchConfig(); - }, []); - - // Fetch current user on mount - using /.auth/me (Azure App Service built-in auth endpoint) - useEffect(() => { - const fetchUser = async () => { - try { - const response = await fetch('/.auth/me'); - if (response.ok) { - const payload = await response.json(); - - // Extract user ID from objectidentifier claim - const userClaims = payload[0]?.user_claims || []; - const objectIdClaim = userClaims.find( - (claim: { typ: string; val: string }) => - claim.typ === 'http://schemas.microsoft.com/identity/claims/objectidentifier' - ); - setUserId(objectIdClaim?.val || 'anonymous'); - - // Extract display name from 'name' claim - const nameClaim = userClaims.find( - (claim: { typ: string; val: string }) => claim.typ === 'name' - ); - setUserName(nameClaim?.val || ''); - } - } catch (err) { - console.error('Error fetching user:', err); - setUserId('anonymous'); - setUserName(''); - } - }; - fetchUser(); - }, []); - - // Handle selecting a conversation from history - const handleSelectConversation = useCallback(async (selectedConversationId: string) => { - try { - const response = await fetch(`/api/conversations/${selectedConversationId}?user_id=${encodeURIComponent(userId)}`); - if (response.ok) { - const data = await response.json(); - setConversationId(selectedConversationId); - setConversationTitle(null); // Will use title from conversation list - const loadedMessages: ChatMessage[] = (data.messages || []).map((msg: { role: string; content: string; timestamp?: string; agent?: string }, index: number) => ({ - id: `${selectedConversationId}-${index}`, - role: msg.role as 'user' | 'assistant', - content: msg.content, - timestamp: msg.timestamp || new Date().toISOString(), - agent: msg.agent, - })); - setMessages(loadedMessages); - - // Only set confirmedBrief if the brief was actually confirmed - // Check metadata.brief_confirmed flag or if content was generated (implying confirmation) - const briefWasConfirmed = data.metadata?.brief_confirmed || data.generated_content; - if (briefWasConfirmed && data.brief) { - setConfirmedBrief(data.brief); - setPendingBrief(null); - } else if (data.brief) { - // Brief exists but wasn't confirmed - show it as pending for confirmation - setPendingBrief(data.brief); - setConfirmedBrief(null); - } else { - setPendingBrief(null); - setConfirmedBrief(null); - } - - // Restore availableProducts so product/color name detection works - // when regenerating images in a restored conversation - if (data.brief && availableProducts.length === 0) { - try { - const productsResponse = await fetch('/api/products'); - if (productsResponse.ok) { - const productsData = await productsResponse.json(); - setAvailableProducts(productsData.products || []); - } - } catch (err) { - console.error('Error loading products for restored conversation:', err); - } - } - - if (data.generated_content) { - const gc = data.generated_content; - let textContent = gc.text_content; - if (typeof textContent === 'string') { - try { - textContent = JSON.parse(textContent); - } catch { - } - } - - let imageUrl: string | undefined = gc.image_url; - if (imageUrl && imageUrl.includes('blob.core.windows.net')) { - const parts = imageUrl.split('/'); - const filename = parts[parts.length - 1]; - const convId = parts[parts.length - 2]; - imageUrl = `/api/images/${convId}/${filename}`; - } - if (!imageUrl && gc.image_base64) { - imageUrl = `data:image/png;base64,${gc.image_base64}`; - } - - const restoredContent: GeneratedContent = { - text_content: typeof textContent === 'object' && textContent ? { - headline: textContent?.headline, - body: textContent?.body, - cta_text: textContent?.cta, - tagline: textContent?.tagline, - } : undefined, - image_content: (imageUrl || gc.image_prompt) ? { - image_url: imageUrl, - prompt_used: gc.image_prompt, - alt_text: gc.image_revised_prompt || 'Generated marketing image', - } : undefined, - violations: gc.violations || [], - requires_modification: gc.requires_modification || false, - error: gc.error, - image_error: gc.image_error, - text_error: gc.text_error, - }; - setGeneratedContent(restoredContent); - - if (gc.selected_products && Array.isArray(gc.selected_products)) { - setSelectedProducts(gc.selected_products); - } else { - setSelectedProducts([]); - } - } else { - setGeneratedContent(null); - setSelectedProducts([]); - } - } - } catch (error) { - console.error('Error loading conversation:', error); - } - }, [userId, availableProducts.length]); - - // Handle starting a new conversation - const handleNewConversation = useCallback(() => { - setConversationId(uuidv4()); - setConversationTitle(null); - setMessages([]); - setPendingBrief(null); - setConfirmedBrief(null); - setGeneratedContent(null); - setSelectedProducts([]); - }, []); - - const handleSendMessage = useCallback(async (content: string) => { - const userMessage: ChatMessage = { - id: uuidv4(), - role: 'user', - content, - timestamp: new Date().toISOString(), - }; - - setMessages(prev => [...prev, userMessage]); - setIsLoading(true); - setGenerationStatus('Processing your request...'); - - // Create new abort controller for this request - abortControllerRef.current = new AbortController(); - const signal = abortControllerRef.current.signal; - - try { - const { sendMessage } = await import('./api'); - - let productsToSend = selectedProducts; - if (generatedContent && confirmedBrief && availableProducts.length > 0) { - const contentLower = content.toLowerCase(); - const mentionedProduct = availableProducts.find(p => - contentLower.includes(p.product_name.toLowerCase()) - ); - if (mentionedProduct && mentionedProduct.product_name !== selectedProducts[0]?.product_name) { - productsToSend = [mentionedProduct]; - } - } - - // Send message - include brief if confirmed, products if we have them - const response = await sendMessage({ - conversation_id: conversationId, - user_id: userId, - message: content, - ...(confirmedBrief && { brief: confirmedBrief }), - ...(productsToSend.length > 0 && { selected_products: productsToSend }), - ...(generatedContent && { has_generated_content: true }), - }, signal); - - // Handle response based on action_type - switch (response.action_type) { - case 'brief_parsed': { - const brief = response.data?.brief as CreativeBrief | undefined; - const title = response.data?.generated_title as string | undefined; - if (brief) { - setPendingBrief(brief); - } - if (title && !conversationTitle) { - setConversationTitle(title); - } - break; - } - - case 'clarification_needed': { - const brief = response.data?.brief as CreativeBrief | undefined; - if (brief) { - setPendingBrief(brief); - } - break; - } - - case 'brief_confirmed': { - const brief = response.data?.brief as CreativeBrief | undefined; - const products = response.data?.products as Product[] | undefined; - if (brief) { - setConfirmedBrief(brief); - setPendingBrief(null); - } - if (products) { - setAvailableProducts(products); - } - break; - } - - case 'products_selected': { - const products = response.data?.products as Product[] | undefined; - if (products) { - setSelectedProducts(products); - } - break; - } - - case 'content_generated': { - const generatedContent = response.data?.generated_content as GeneratedContent | undefined; - if (generatedContent) { - setGeneratedContent(generatedContent); - } - break; - } - - case 'image_regenerated': { - const generatedContent = response.data?.generated_content as GeneratedContent | undefined; - if (generatedContent) { - setGeneratedContent(generatedContent); - } - break; - } - - case 'regeneration_started': { - // Poll for completion using task_id from response - // Backend already started the regeneration task via /api/chat - const { pollTaskStatus } = await import('./api'); - const taskId = response.data?.task_id as string; - - if (!taskId) { - throw new Error('No task_id received for regeneration'); - } - - setGenerationStatus('Regenerating image...'); - - for await (const event of pollTaskStatus(taskId, signal)) { - - if (event.type === 'heartbeat') { - const statusMessage = (event.content as string) || 'Regenerating image...'; - const elapsed = (event as { elapsed?: number }).elapsed || 0; - setGenerationStatus(elapsed > 0 ? `${statusMessage} (${elapsed}s)` : statusMessage); - } else if (event.type === 'agent_response' && event.is_final) { - let result: Record | undefined = event.content as unknown as Record; - if (typeof event.content === 'string') { - try { result = JSON.parse(event.content); } catch { result = {}; } - } - - let imageUrl = result?.image_url as string | undefined; - if (imageUrl && imageUrl.includes('blob.core.windows.net')) { - const parts = imageUrl.split('/'); - const filename = parts[parts.length - 1]; - const convId = parts[parts.length - 2]; - imageUrl = `/api/images/${convId}/${filename}`; - } - - // Parse text_content if it's a JSON string - let textContent = result?.text_content; - if (typeof textContent === 'string') { - try { textContent = JSON.parse(textContent); } catch { /* keep as-is */ } - } - - // Update selected products if backend provided new ones (product change) - const newProducts = result?.selected_products as Product[] | undefined; - if (newProducts && newProducts.length > 0) { - setSelectedProducts(newProducts); - } - - // Update confirmed brief if backend provided an updated one (with accumulated modifications) - const updatedBrief = result?.updated_brief as CreativeBrief | undefined; - if (updatedBrief) { - setConfirmedBrief(updatedBrief); - } - - setGeneratedContent(prev => ({ - ...prev, - // Update text content if provided (with new product name) - text_content: textContent ? { - headline: (textContent as Record).headline as string | undefined, - body: (textContent as Record).body as string | undefined, - cta_text: (textContent as Record).cta as string | undefined, - } : prev?.text_content, - image_content: imageUrl ? { - image_url: imageUrl, - prompt_used: result?.image_prompt as string | undefined, - alt_text: (result?.image_revised_prompt as string) || 'Regenerated marketing image', - } : prev?.image_content, - violations: prev?.violations || [], - requires_modification: prev?.requires_modification || false, - })); - } else if (event.type === 'error') { - throw new Error(event.content || 'Regeneration failed'); - } - } - - setGenerationStatus(''); - break; - } - - case 'start_over': { - setPendingBrief(null); - setConfirmedBrief(null); - setSelectedProducts([]); - setGeneratedContent(null); - break; - } - - case 'rai_blocked': - case 'error': - case 'chat_response': - default: - // Just show the message - break; - } - - // Add assistant message from response - if (response.message) { - const assistantMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: response.message, - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, assistantMessage]); - } - - } catch (error) { - // Check if this was a user-initiated cancellation - if (error instanceof Error && error.name === 'AbortError') { - console.log('Request cancelled by user'); - const cancelMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: 'Generation stopped.', - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, cancelMessage]); - } else { - console.error('Error sending message:', error); - const errorMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: 'Sorry, there was an error processing your request. Please try again.', - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, errorMessage]); - } - } finally { - setIsLoading(false); - setGenerationStatus(''); - abortControllerRef.current = null; - // Trigger refresh of chat history after message is sent - setHistoryRefreshTrigger(prev => prev + 1); - } - }, [conversationId, userId, conversationTitle, confirmedBrief, selectedProducts, generatedContent]); - - const handleBriefConfirm = useCallback(async () => { - if (!pendingBrief) return; - - try { - const { sendMessage } = await import('./api'); - - const response = await sendMessage({ - conversation_id: conversationId, - user_id: userId, - action: 'confirm_brief', - brief: pendingBrief, - }); - - // Update state based on response - if (response.action_type === 'brief_confirmed') { - const brief = response.data?.brief as CreativeBrief | undefined; - if (brief) { - setConfirmedBrief(brief); - } else { - setConfirmedBrief(pendingBrief); - } - setPendingBrief(null); - - // Fetch products separately after confirmation - try { - const productsResponse = await fetch('/api/products'); - if (productsResponse.ok) { - const productsData = await productsResponse.json(); - setAvailableProducts(productsData.products || []); - } - } catch (err) { - console.error('Error loading products after confirmation:', err); - } - } - - // Add assistant message - if (response.message) { - const assistantMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: response.message, - agent: 'ProductAgent', - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, assistantMessage]); - } - } catch (error) { - console.error('Error confirming brief:', error); - } - }, [conversationId, userId, pendingBrief]); - - const handleBriefCancel = useCallback(async () => { - setPendingBrief(null); - - const assistantMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: 'No problem. Please provide your creative brief again or ask me any questions.', - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, assistantMessage]); - }, []); - - const handleProductsStartOver = useCallback(async () => { - try { - const { sendMessage } = await import('./api'); - - const response = await sendMessage({ - conversation_id: conversationId, - user_id: userId, - action: 'start_over', - }); - - console.log('Start over response:', response); - - // Reset all local state - setPendingBrief(null); - setConfirmedBrief(null); - setSelectedProducts([]); - setGeneratedContent(null); - - // Add assistant message - if (response.message) { - const assistantMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: response.message, - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, assistantMessage]); - } - } catch (error) { - console.error('Error starting over:', error); - // Still reset local state even if backend call fails - setPendingBrief(null); - setConfirmedBrief(null); - setSelectedProducts([]); - setGeneratedContent(null); - - const assistantMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: 'Starting over. Please provide your creative brief to begin a new campaign.', - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, assistantMessage]); - } - }, [conversationId, userId]); - - const handleProductSelect = useCallback((product: Product) => { - const isSelected = selectedProducts.some( - p => (p.sku || p.product_name) === (product.sku || product.product_name) - ); - - if (isSelected) { - // Deselect - but user must have at least one selected to proceed - setSelectedProducts([]); - } else { - // Single selection mode - replace any existing selection - setSelectedProducts([product]); - } - }, [selectedProducts]); - - const handleStopGeneration = useCallback(() => { - if (abortControllerRef.current) { - abortControllerRef.current.abort(); - } - }, []); - - const handleGenerateContent = useCallback(async () => { - if (!confirmedBrief) return; - - setIsLoading(true); - setGenerationStatus('Starting content generation...'); - - // Create new abort controller for this request - abortControllerRef.current = new AbortController(); - const signal = abortControllerRef.current.signal; - - try { - const { streamGenerateContent } = await import('./api'); - - for await (const event of streamGenerateContent({ - conversation_id: conversationId, - user_id: userId, - brief: confirmedBrief as unknown as Record, - products: selectedProducts as unknown as Array>, - generate_images: imageGenerationEnabled, - }, signal)) { - - if (event.type === 'heartbeat') { - const statusMessage = (event.content as string) || 'Generating content...'; - const elapsed = (event as { elapsed?: number }).elapsed || 0; - setGenerationStatus(elapsed > 0 ? `${statusMessage} (${elapsed}s)` : statusMessage); - } else if (event.type === 'agent_response' && event.is_final) { - let result: Record | undefined = event.content as unknown as Record; - if (typeof event.content === 'string') { - try { - result = JSON.parse(event.content); - } catch { - result = {}; - } - } - - let imageUrl = result?.image_url as string | undefined; - - // Convert blob URLs to proxy URLs - if (imageUrl && imageUrl.includes('blob.core.windows.net')) { - const parts = imageUrl.split('/'); - const filename = parts[parts.length - 1]; - const convId = parts[parts.length - 2]; - imageUrl = `/api/images/${convId}/${filename}`; - } - - // Parse text_content - it may be a JSON string from backend - let textContent: Record | undefined; - const rawTextContent = result?.text_content; - if (typeof rawTextContent === 'string') { - try { - textContent = JSON.parse(rawTextContent); - } catch { - console.error('Failed to parse text_content JSON string'); - textContent = undefined; - } - } else { - textContent = rawTextContent as Record | undefined; - } - - const generatedContent: GeneratedContent = { - text_content: textContent ? { - headline: textContent.headline as string | undefined, - body: textContent.body as string | undefined, - cta_text: textContent.cta as string | undefined, - } : { - headline: result?.headline as string | undefined, - body: result?.body as string | undefined, - cta_text: result?.cta as string | undefined, - }, - image_content: imageUrl ? { - image_url: imageUrl, - prompt_used: result?.image_prompt as string | undefined, - alt_text: (result?.image_revised_prompt as string) || 'Generated marketing image', - } : undefined, - violations: (result?.violations as unknown as GeneratedContent['violations']) || [], - requires_modification: (result?.requires_modification as boolean) || false, - }; - - setGeneratedContent(generatedContent); - } else if (event.type === 'error') { - throw new Error(event.content || 'Generation failed'); - } - } - - setGenerationStatus(''); - } catch (error) { - // Check if this was a user-initiated cancellation - if (error instanceof Error && error.name === 'AbortError') { - console.log('Content generation cancelled by user'); - const cancelMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: 'Content generation stopped.', - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, cancelMessage]); - } else { - console.error('Error generating content:', error); - const errorMessage: ChatMessage = { - id: uuidv4(), - role: 'assistant', - content: 'Sorry, there was an error generating content. Please try again.', - timestamp: new Date().toISOString(), - }; - setMessages(prev => [...prev, errorMessage]); - } - } finally { - setIsLoading(false); - setGenerationStatus(''); - abortControllerRef.current = null; - } - }, [confirmedBrief, selectedProducts, conversationId, userId, imageGenerationEnabled]); - - return ( -
- {/* Header */} -
-
- Contoso - - Contoso - -
-
- -
-
- - {/* Main Content */} -
- {/* Chat Panel - main area */} -
- -
- - {/* Chat History Sidebar - RIGHT side */} - {showChatHistory && ( -
- -
- )} -
-
- ); -} - -export default App; diff --git a/src/app/frontend/src/api/index.ts b/src/app/frontend/src/api/index.ts deleted file mode 100644 index 36781b77b..000000000 --- a/src/app/frontend/src/api/index.ts +++ /dev/null @@ -1,252 +0,0 @@ -/** - * API service for interacting with the Content Generation backend - */ - -import type { - AgentResponse, - AppConfig, - MessageRequest, - MessageResponse, -} from '../types'; - -const API_BASE = '/api'; - -/** - * Send a message or action to the /api/chat endpoint - */ -export async function sendMessage( - request: MessageRequest, - signal?: AbortSignal -): Promise { - const response = await fetch(`${API_BASE}/chat`, { - signal, - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(request), - }); - - if (!response.ok) { - throw new Error(`Message request failed: ${response.statusText}`); - } - - return response.json(); -} - -/** - * Get application configuration including feature flags - */ -export async function getAppConfig(): Promise { - const response = await fetch(`${API_BASE}/config`); - - if (!response.ok) { - throw new Error(`Failed to get config: ${response.statusText}`); - } - - return response.json(); -} - -/** - * Request for content generation - */ -export interface GenerateRequest { - conversation_id: string; - user_id: string; - brief: Record; - products: Array>; - generate_images: boolean; -} - -/** - * Generate content from a confirmed brief - */ -export async function* streamGenerateContent( - request: GenerateRequest, - signal?: AbortSignal -): AsyncGenerator { - // Use polling-based approach for reliability with long-running tasks - const startResponse = await fetch(`${API_BASE}/generate/start`, { - signal, - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - brief: request.brief, - products: request.products || [], - generate_images: request.generate_images, - conversation_id: request.conversation_id, - user_id: request.user_id || 'anonymous', - }), - }); - - if (!startResponse.ok) { - throw new Error(`Content generation failed to start: ${startResponse.statusText}`); - } - - const startData = await startResponse.json(); - const taskId = startData.task_id; - - console.log(`Generation started with task ID: ${taskId}`); - - // Yield initial status - yield { - type: 'status', - content: 'Generation started...', - is_final: false, - } as AgentResponse; - - // Poll for completion - let attempts = 0; - const maxAttempts = 600; // 10 minutes max with 1-second polling (image generation can take 3-5 min) - const pollInterval = 1000; // 1 second - - while (attempts < maxAttempts) { - // Check if cancelled before waiting - if (signal?.aborted) { - throw new DOMException('Generation cancelled by user', 'AbortError'); - } - - await new Promise(resolve => setTimeout(resolve, pollInterval)); - attempts++; - - // Check if cancelled after waiting - if (signal?.aborted) { - throw new DOMException('Generation cancelled by user', 'AbortError'); - } - - try { - const statusResponse = await fetch(`${API_BASE}/generate/status/${taskId}`, { signal }); - if (!statusResponse.ok) { - throw new Error(`Failed to get task status: ${statusResponse.statusText}`); - } - - const statusData = await statusResponse.json(); - - if (statusData.status === 'completed') { - // Yield the final result - yield { - type: 'agent_response', - content: JSON.stringify(statusData.result), - is_final: true, - } as AgentResponse; - return; - } else if (statusData.status === 'failed') { - throw new Error(statusData.error || 'Generation failed'); - } else if (statusData.status === 'running') { - // Determine progress stage based on elapsed time - // Typical generation: 0-10s briefing, 10-25s copy, 25-45s image, 45-60s compliance - const elapsedSeconds = attempts; - let stage: number; - let stageMessage: string; - - if (elapsedSeconds < 10) { - stage = 0; - stageMessage = 'Analyzing creative brief...'; - } else if (elapsedSeconds < 25) { - stage = 1; - stageMessage = 'Generating marketing copy...'; - } else if (elapsedSeconds < 35) { - stage = 2; - stageMessage = 'Creating image prompt...'; - } else if (elapsedSeconds < 55) { - stage = 3; - stageMessage = 'Generating image with AI...'; - } else if (elapsedSeconds < 70) { - stage = 4; - stageMessage = 'Running compliance check...'; - } else { - stage = 5; - stageMessage = 'Finalizing content...'; - } - - // Send status update every second for smoother progress - yield { - type: 'heartbeat', - content: stageMessage, - count: stage, - elapsed: elapsedSeconds, - is_final: false, - } as AgentResponse; - } - } catch (error) { - console.error(`Error polling task ${taskId}:`, error); - // Continue polling on transient errors - if (attempts >= maxAttempts) { - throw error; - } - } - } - - throw new Error('Generation timed out after 10 minutes'); -} - -/** - * Poll for task completion using task_id - * Used for both content generation and image regeneration - */ -export async function* pollTaskStatus( - taskId: string, - signal?: AbortSignal -): AsyncGenerator { - let attempts = 0; - const maxAttempts = 600; // 10 minutes max with 1-second polling - const pollInterval = 1000; // 1 second - - while (attempts < maxAttempts) { - if (signal?.aborted) { - throw new DOMException('Operation cancelled by user', 'AbortError'); - } - - await new Promise(resolve => setTimeout(resolve, pollInterval)); - attempts++; - - if (signal?.aborted) { - throw new DOMException('Operation cancelled by user', 'AbortError'); - } - - try { - const statusResponse = await fetch(`${API_BASE}/generate/status/${taskId}`, { signal }); - if (!statusResponse.ok) { - throw new Error(`Failed to get task status: ${statusResponse.statusText}`); - } - - const statusData = await statusResponse.json(); - - if (statusData.status === 'completed') { - yield { - type: 'agent_response', - content: JSON.stringify(statusData.result), - is_final: true, - } as AgentResponse; - return; - } else if (statusData.status === 'failed') { - throw new Error(statusData.error || 'Task failed'); - } else { - // Yield heartbeat with progress - const elapsedSeconds = attempts; - let stageMessage: string; - - if (elapsedSeconds < 10) { - stageMessage = 'Starting regeneration...'; - } else if (elapsedSeconds < 30) { - stageMessage = 'Generating new image...'; - } else if (elapsedSeconds < 50) { - stageMessage = 'Processing image...'; - } else { - stageMessage = 'Finalizing...'; - } - - yield { - type: 'heartbeat', - content: stageMessage, - elapsed: elapsedSeconds, - is_final: false, - } as AgentResponse; - } - } catch (error) { - if (attempts >= maxAttempts) { - throw error; - } - } - } - - throw new Error('Task timed out after 10 minutes'); -} \ No newline at end of file diff --git a/src/app/frontend/src/components/ChatPanel.tsx b/src/app/frontend/src/components/ChatPanel.tsx deleted file mode 100644 index bf757acf9..000000000 --- a/src/app/frontend/src/components/ChatPanel.tsx +++ /dev/null @@ -1,437 +0,0 @@ -import { useState, useRef, useEffect } from 'react'; -import { - Button, - Text, - Badge, - tokens, - Tooltip, -} from '@fluentui/react-components'; -import { - Send20Regular, - Stop24Regular, - Add20Regular, - Copy20Regular, -} from '@fluentui/react-icons'; -import ReactMarkdown from 'react-markdown'; -import type { ChatMessage, CreativeBrief, Product, GeneratedContent } from '../types'; -import { BriefReview } from './BriefReview'; -import { ConfirmedBriefView } from './ConfirmedBriefView'; -import { SelectedProductView } from './SelectedProductView'; -import { ProductReview } from './ProductReview'; -import { InlineContentPreview } from './InlineContentPreview'; -import { WelcomeCard } from './WelcomeCard'; - -interface ChatPanelProps { - messages: ChatMessage[]; - onSendMessage: (message: string) => void; - isLoading: boolean; - generationStatus?: string; - onStopGeneration?: () => void; - // Inline component props - pendingBrief?: CreativeBrief | null; - confirmedBrief?: CreativeBrief | null; - generatedContent?: GeneratedContent | null; - selectedProducts?: Product[]; - availableProducts?: Product[]; - onBriefConfirm?: () => void; - onBriefCancel?: () => void; - onGenerateContent?: () => void; - onRegenerateContent?: () => void; - onProductsStartOver?: () => void; - onProductSelect?: (product: Product) => void; - // Feature flags - imageGenerationEnabled?: boolean; - // New chat - onNewConversation?: () => void; -} - -export function ChatPanel({ - messages, - onSendMessage, - isLoading, - generationStatus, - onStopGeneration, - pendingBrief, - confirmedBrief, - generatedContent, - selectedProducts = [], - availableProducts = [], - onBriefConfirm, - onBriefCancel, - onGenerateContent, - onRegenerateContent, - onProductsStartOver, - onProductSelect, - imageGenerationEnabled = true, - onNewConversation, -}: ChatPanelProps) { - const [inputValue, setInputValue] = useState(''); - const messagesEndRef = useRef(null); - const messagesContainerRef = useRef(null); - const inputContainerRef = useRef(null); - - // Scroll to bottom when messages change - useEffect(() => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }, [messages, pendingBrief, confirmedBrief, generatedContent, isLoading, generationStatus]); - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - if (inputValue.trim() && !isLoading) { - onSendMessage(inputValue.trim()); - setInputValue(''); - } - }; - - // Determine if we should show inline components - const showBriefReview = pendingBrief && onBriefConfirm && onBriefCancel; - const showProductReview = confirmedBrief && !generatedContent && onGenerateContent; - const showContentPreview = generatedContent && onRegenerateContent; - const showWelcome = messages.length === 0 && !showBriefReview && !showProductReview && !showContentPreview; - - // Handle suggestion click from welcome card - const handleSuggestionClick = (prompt: string) => { - setInputValue(prompt); - }; - - return ( -
- {/* Messages Area */} -
- {showWelcome ? ( - - ) : ( - <> - {messages.map((message) => ( - - ))} - - {/* Brief Review - Read Only with Conversational Prompts */} - {showBriefReview && ( - - )} - - {/* Confirmed Brief View - Persistent read-only view */} - {confirmedBrief && !pendingBrief && ( - - )} - - {/* Selected Product View - Persistent read-only view after content generation */} - {generatedContent && selectedProducts.length > 0 && ( - - )} - - {/* Product Review - Conversational Product Selection */} - {showProductReview && ( - {})} - isAwaitingResponse={isLoading} - onProductSelect={onProductSelect} - disabled={isLoading} - /> - )} - - {/* Inline Content Preview */} - {showContentPreview && ( - 0 ? selectedProducts[0] : undefined} - imageGenerationEnabled={imageGenerationEnabled} - /> - )} - - {/* Loading/Typing Indicator - Coral Style */} - {isLoading && ( -
-
- - - - - -
- - {generationStatus || 'Thinking...'} - - {onStopGeneration && ( - - - - )} -
- )} - - )} - -
-
- - {/* Input Area - Simple single-line like Figma */} -
- {/* Input Box */} -
- setInputValue(e.target.value)} - onKeyDown={(e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - handleSubmit(e); - } - }} - placeholder="Type a message" - disabled={isLoading} - style={{ - flex: 1, - border: 'none', - outline: 'none', - backgroundColor: 'transparent', - fontFamily: 'var(--fontFamilyBase)', - fontSize: '14px', - color: tokens.colorNeutralForeground1, - }} - /> - - {/* Icons on the right */} -
- -
-
- - {/* Disclaimer - Outside the input box */} - - AI generated content may be incorrect - -
-
- ); -} - -// Copy function for messages -const handleCopy = (text: string) => { - navigator.clipboard.writeText(text).catch((err) => { - console.error('Failed to copy text:', err); - }); -}; - -function MessageBubble({ message }: { message: ChatMessage }) { - const isUser = message.role === 'user'; - const [copied, setCopied] = useState(false); - - const onCopy = () => { - handleCopy(message.content); - setCopied(true); - setTimeout(() => setCopied(false), 2000); - }; - - return ( -
- {/* Agent badge for assistant messages */} - {!isUser && message.agent && ( - - {message.agent} - - )} - - {/* Message content with markdown */} -
- - {message.content} - - - {/* Footer for assistant messages - Coral style */} - {!isUser && ( -
- - AI-generated content may be incorrect - - -
- -
-
- )} -
-
- ); -} diff --git a/src/app/frontend/src/components/ProductReview.tsx b/src/app/frontend/src/components/ProductReview.tsx deleted file mode 100644 index 9c0d9e960..000000000 --- a/src/app/frontend/src/components/ProductReview.tsx +++ /dev/null @@ -1,227 +0,0 @@ -import { - Button, - Text, - tokens, -} from '@fluentui/react-components'; -import { - Sparkle20Regular, - Box20Regular, -} from '@fluentui/react-icons'; -import type { Product } from '../types'; - -interface ProductReviewProps { - products: Product[]; - onConfirm: () => void; - onStartOver: () => void; - isAwaitingResponse?: boolean; - availableProducts?: Product[]; - onProductSelect?: (product: Product) => void; - disabled?: boolean; -} - -export function ProductReview({ - products, - onConfirm, - onStartOver: _onStartOver, - isAwaitingResponse = false, - availableProducts = [], - onProductSelect, - disabled = false, -}: ProductReviewProps) { - const displayProducts = availableProducts.length > 0 ? availableProducts : products; - const selectedProductIds = new Set(products.map(p => p.sku || p.product_name)); - - const isProductSelected = (product: Product): boolean => { - return selectedProductIds.has(product.sku || product.product_name); - }; - - const handleProductClick = (product: Product) => { - if (onProductSelect) { - onProductSelect(product); - } - }; - - return ( -
-
- - Here is the list of available paints: - -
- - {displayProducts.length > 0 ? ( -
- {displayProducts.map((product, index) => ( - handleProductClick(product)} - disabled={disabled} - /> - ))} -
- ) : ( -
- - No products available. - -
- )} - - {displayProducts.length > 0 && ( -
- - {products.length === 0 && ( - - Select a product to continue - - )} -
- )} - -
- - AI-generated content may be incorrect - -
-
- ); -} - -interface ProductCardGridProps { - product: Product; - isSelected: boolean; - onClick: () => void; - disabled?: boolean; -} - -function ProductCardGrid({ product, isSelected, onClick, disabled = false }: ProductCardGridProps) { - return ( -
- {product.image_url ? ( - {product.product_name} - ) : ( -
- -
- )} - -
- - {product.product_name} - - - {product.tags || product.description || 'soft white, airy, minimal, fresh'} - - - ${product.price?.toFixed(2) || '59.95'} USD - -
-
- ); -} diff --git a/src/app/frontend/src/components/SelectedProductView.tsx b/src/app/frontend/src/components/SelectedProductView.tsx deleted file mode 100644 index a4c4540f6..000000000 --- a/src/app/frontend/src/components/SelectedProductView.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { - Text, - Badge, - tokens, -} from '@fluentui/react-components'; -import { - Checkmark20Regular, - Box20Regular, -} from '@fluentui/react-icons'; -import type { Product } from '../types'; - -interface SelectedProductViewProps { - products: Product[]; -} - -export function SelectedProductView({ products }: SelectedProductViewProps) { - if (products.length === 0) return null; - - return ( -
-
- } - > - Products Selected - -
- -
- {products.map((product, index) => ( -
- {product.image_url ? ( - {product.product_name} - ) : ( -
- -
- )} - -
- - {product.product_name} - - - {product.tags || product.description || 'soft white, airy, minimal, fresh'} - - - ${product.price?.toFixed(2) || '59.95'} USD - -
-
- ))} -
-
- ); -}