From 24be759391337d05286ae46db5c0722bd7171fac Mon Sep 17 00:00:00 2001 From: zedy Date: Sun, 5 Nov 2023 11:08:23 +0800 Subject: [PATCH 01/37] azdevify this sample and add azd new feature in devcontainer --- .devcontainer/devcontainer.json | 31 + README.md | 38 + azure.yaml | 26 + infra/app/function.bicep | 90 ++ infra/app/resources.bicep | 84 ++ infra/app/storage.bicep | 63 + infra/app/websiteadmin.bicep | 70 + infra/app/websiteapi.bicep | 71 + infra/core/ai/cognitiveservices.bicep | 42 + infra/core/host/appservice-appsettings.bicep | 17 + infra/core/host/appservice.bicep | 118 ++ infra/core/host/appserviceplan.bicep | 22 + infra/core/host/functions.bicep | 86 ++ .../applicationinsights-dashboard.bicep | 1236 +++++++++++++++++ infra/core/monitor/applicationinsights.bicep | 31 + infra/core/monitor/loganalytics.bicep | 22 + infra/core/monitor/monitoring.bicep | 34 + infra/core/search/search-services.bicep | 63 + infra/main.bicep | 409 ++++++ infra/main.parameters.json | 12 + 20 files changed, 2565 insertions(+) create mode 100644 .devcontainer/devcontainer.json create mode 100644 azure.yaml create mode 100644 infra/app/function.bicep create mode 100644 infra/app/resources.bicep create mode 100644 infra/app/storage.bicep create mode 100644 infra/app/websiteadmin.bicep create mode 100644 infra/app/websiteapi.bicep create mode 100644 infra/core/ai/cognitiveservices.bicep create mode 100644 infra/core/host/appservice-appsettings.bicep create mode 100644 infra/core/host/appservice.bicep create mode 100644 infra/core/host/appserviceplan.bicep create mode 100644 infra/core/host/functions.bicep create mode 100644 infra/core/monitor/applicationinsights-dashboard.bicep create mode 100644 infra/core/monitor/applicationinsights.bicep create mode 100644 infra/core/monitor/loganalytics.bicep create mode 100644 infra/core/monitor/monitoring.bicep create mode 100644 infra/core/search/search-services.bicep create mode 100644 infra/main.bicep create mode 100644 infra/main.parameters.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..345e94164 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,31 @@ +{ + "name": "Chat with your data Solution Accelerator", + "image": "mcr.microsoft.com/devcontainers/python:3.11", + "features": { + "ghcr.io/devcontainers/features/node:1": { + "version": "16", + "nodeGypDependencies": false + }, + "ghcr.io/devcontainers/features/powershell:1.1.1": {}, + "ghcr.io/devcontainers/features/azure-cli:1.2.1": {}, + "ghcr.io/azure/azure-dev/azd:latest": {} + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-azuretools.azure-dev", + "ms-azuretools.vscode-bicep", + "ms-python.python", + "esbenp.prettier-vscode" + ] + } + }, + "forwardPorts": [ + 50505 + ], + "postCreateCommand": "", + "remoteUser": "vscode", + "hostRequirements": { + "memory": "8gb" + } +} \ No newline at end of file diff --git a/README.md b/README.md index 1e352b869..faea432db 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,44 @@ Out-of-the-box, you can upload the following file types: ![A screenshot of the chat app.](./media/chat-app.png) +## Running the sample using Azd template + +The Azure Developer CLI (`azd`) is a developer-centric command-line interface (CLI) tool for creating Azure applications. + +You need to install it before running and deploying with Azure Developer CLI. + +### Windows + +```powershell +powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' | Invoke-Expression" +``` + +### Linux/MacOS + +``` +curl -fsSL https://aka.ms/install-azd.sh | bash +``` + +After logging in with the following command, you will be able to use the azd cli to quickly provision and deploy the application. + +``` +azd auth login +``` + +Then, execute the `azd init` command to initialize the environment. +``` +azd init -t jongio/chat-with-your-data-solution-accelerator +``` +According to the prompt, enter an `env name`. + +Run `azd up` to provision all the resources to Azure and deploy the code to those resources. +``` +azd up +``` + +According to the prompt, select `subscription` and `location`, these are the necessary parameters when you create resources. Wait a moment for the resource deployment to complete, click the Website endpoint and you will see the web app page. + +You can also run the sample directly locally (See below). ## Development and run the accelerator locally diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 000000000..710d75f0c --- /dev/null +++ b/azure.yaml @@ -0,0 +1,26 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json + +name: chat-with-your-data-solution-accelerator + +services: + Website: + project: . + language: py + host: appservice + hooks: + prepackage: + windows: + shell: pwsh + run: cd ./frontend;npm install;npm run build + interactive: true + continueOnError: false + + WebsiteName_admin: + project: ./backend + language: py + host: appservice + + Function: + project: ./backend + language: py + host: function diff --git a/infra/app/function.bicep b/infra/app/function.bicep new file mode 100644 index 000000000..e4088b824 --- /dev/null +++ b/infra/app/function.bicep @@ -0,0 +1,90 @@ +param name string +param location string = '' +param appServicePlanId string +param storageAccountName string = '' +param tags object = {} + +param appSettings array = [] +param serviceName string = 'Function' +param applicationInsightsName string = '' +param runtimeName string +param runtimeVersion string +param ClientKey string +param AzureOpenAIName string = '' +param AzureCognitiveSearchName string = '' +param FormRecognizerName string = '' +param ContentSafetyName string = '' + + +module Function '../core/host/functions.bicep' = { + name: '${name}-app-module' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + appServicePlanId: appServicePlanId + storageAccountName: storageAccountName + runtimeName: runtimeName + runtimeVersion: runtimeVersion + appSettings: union(toObject(appSettings, entry => entry.name, entry => entry.value), { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value + APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey + AZURE_OPENAI_KEY: openai.listKeys().key1 + AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey + AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 + AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 + }) + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { + name: AzureOpenAIName +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { + name: AzureCognitiveSearchName +} + +resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { + name: FormRecognizerName +} + +resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { + name: ContentSafetyName +} + +resource FunctionName_default_clientKey 'Microsoft.Web/sites/host/functionKeys@2018-11-01' = { + name: '${name}/default/clientKey' + properties: { + name: 'ClientKey' + value: ClientKey + } + dependsOn: [ + Function + WaitFunctionDeploymentSection + ] +} + +resource WaitFunctionDeploymentSection 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + kind: 'AzurePowerShell' + name: 'WaitFunctionDeploymentSection' + location: location + properties: { + azPowerShellVersion: '3.0' + scriptContent: 'start-sleep -Seconds 300' + cleanupPreference: 'Always' + retentionInterval: 'PT1H' + } + dependsOn: [ + Function + ] +} diff --git a/infra/app/resources.bicep b/infra/app/resources.bicep new file mode 100644 index 000000000..c2fd7c1b0 --- /dev/null +++ b/infra/app/resources.bicep @@ -0,0 +1,84 @@ +param ContentSafetyName string +param FormRecognizerName string +param EventGridSystemTopicName string +param Location string +param StorageAccountId string +param QueueName string +param BlobContainerName string + +resource FormRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { + name: FormRecognizerName + location: Location + sku: { + name: 'S0' + } + kind: 'FormRecognizer' + identity: { + type: 'None' + } + properties: { + networkAcls: { + defaultAction: 'Allow' + virtualNetworkRules: [] + ipRules: [] + } + publicNetworkAccess: 'Enabled' + } +} + +resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { + name: ContentSafetyName + location: Location + sku: { + name: 'S0' + } + kind: 'ContentSafety' + identity: { + type: 'None' + } + properties: { + networkAcls: { + defaultAction: 'Allow' + virtualNetworkRules: [] + ipRules: [] + } + } +} + +resource EventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { + name: EventGridSystemTopicName + location: Location + properties: { + source: StorageAccountId + topicType: 'Microsoft.Storage.StorageAccounts' + } +} + +resource EventGridSystemTopicName_BlobEvents 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2021-12-01' = { + parent: EventGridSystemTopic + name: 'BlobEvents' + properties: { + destination: { + endpointType: 'StorageQueue' + properties: { + queueMessageTimeToLiveInSeconds: -1 + queueName: QueueName + resourceId: StorageAccountId + } + } + filter: { + includedEventTypes: [ + 'Microsoft.Storage.BlobCreated' + 'Microsoft.Storage.BlobDeleted' + ] + enableAdvancedFilteringOnArrays: true + subjectBeginsWith: '/blobServices/default/containers/${BlobContainerName}/blobs/' + } + labels: [] + eventDeliverySchema: 'EventGridSchema' + retryPolicy: { + maxDeliveryAttempts: 30 + eventTimeToLiveInMinutes: 1440 + } + } +} diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep new file mode 100644 index 000000000..ed6803437 --- /dev/null +++ b/infra/app/storage.bicep @@ -0,0 +1,63 @@ +param StorageAccountName string = 'Enabled' +param Location string = '' +param BlobContainerName string = 'Enabled' + +resource StorageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: StorageAccountName + location: Location + kind: 'StorageV2' + sku: { + name: 'Standard_GRS' + } +} + +resource StorageAccountName_default_BlobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { + name: '${StorageAccountName}/default/${BlobContainerName}' + properties: { + publicAccess: 'None' + } + dependsOn: [ + StorageAccount + ] +} + +resource StorageAccountName_default_config 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { + name: '${StorageAccountName}/default/config' + properties: { + publicAccess: 'None' + } + dependsOn: [ + StorageAccount + ] +} + +resource StorageAccountName_default 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { + parent: StorageAccount + name: 'default' + properties: { + cors: { + corsRules: [] + } + } +} + +resource StorageAccountName_default_doc_processing 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { + parent: StorageAccountName_default + name: 'doc-processing' + properties: { + metadata: {} + } + dependsOn: [] +} + +resource StorageAccountName_default_doc_processing_poison 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { + parent: StorageAccountName_default + name: 'doc-processing-poison' + properties: { + metadata: {} + } + dependsOn: [] +} + +output StorageAccountId string = StorageAccount.id +output StorageAccountName_default_doc_processing_name string = StorageAccountName_default_doc_processing.name diff --git a/infra/app/websiteadmin.bicep b/infra/app/websiteadmin.bicep new file mode 100644 index 000000000..8b4ecba54 --- /dev/null +++ b/infra/app/websiteadmin.bicep @@ -0,0 +1,70 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param allowedOrigins array = [] +param appServicePlanId string +param appCommandLine string = 'python -m streamlit run Admin.py --server.port 8000 --server.address 0.0.0.0 --server.enableXsrfProtection false' +param applicationInsightsName string = '' +param StorageAccountName string +param keyVaultName string = '' +param AzureOpenAIName string = '' +param AzureCognitiveSearchName string = '' +param FormRecognizerName string = '' +param ContentSafetyName string = '' + +param appSettings array = [] +param serviceName string = 'WebsiteName_admin' + +module websiteadmin '../core/host/appservice.bicep' = { + name: '${name}-app-module' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + allowedOrigins: allowedOrigins + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(toObject(appSettings, entry => entry.name, entry => entry.value), { + AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value + APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey + AZURE_OPENAI_KEY: openai.listKeys().key1 + AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey + AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 + AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 + }) + keyVaultName: keyVaultName + runtimeName: 'python' + runtimeVersion: '3.11' + scmDoBuildDuringDeployment: true + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: StorageAccountName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { + name: AzureOpenAIName +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { + name: AzureCognitiveSearchName +} + +resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { + name: FormRecognizerName +} + +resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { + name: ContentSafetyName +} + +output WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID string = websiteadmin.outputs.identityPrincipalId +output WEBSITE_ADMIN_NAME string = websiteadmin.outputs.name +output WEBSITE_ADMIN_URI string = websiteadmin.outputs.uri diff --git a/infra/app/websiteapi.bicep b/infra/app/websiteapi.bicep new file mode 100644 index 000000000..11d7cb2a8 --- /dev/null +++ b/infra/app/websiteapi.bicep @@ -0,0 +1,71 @@ +param name string +param location string = resourceGroup().location +param tags object = {} + +param allowedOrigins array = [] +param appCommandLine string = '' +param appServicePlanId string +param applicationInsightsName string = '' +param StorageAccountName string +param keyVaultName string = '' +param AzureOpenAIName string = '' +param AzureCognitiveSearchName string = '' +param FormRecognizerName string = '' +param ContentSafetyName string = '' + +param appSettings array = [] +param serviceName string = 'Website' + +module Website '../core/host/appservice.bicep' = { + name: '${name}-app-module' + params: { + name: name + location: location + tags: union(tags, { 'azd-service-name': serviceName }) + allowedOrigins: allowedOrigins + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(toObject(appSettings, entry => entry.name, entry => entry.value), { + AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value + APPINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString + AZURE_OPENAI_KEY: openai.listKeys().key1 + AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey + AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 + AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 + }) + + keyVaultName: keyVaultName + runtimeName: 'python' + runtimeVersion: '3.11' + scmDoBuildDuringDeployment: true + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: StorageAccountName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { + name: AzureOpenAIName +} + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { + name: AzureCognitiveSearchName +} + +resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { + name: FormRecognizerName +} + +resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { + name: ContentSafetyName +} + +output FRONTEND_API_IDENTITY_PRINCIPAL_ID string = Website.outputs.identityPrincipalId +output FRONTEND_API_NAME string = Website.outputs.name +output FRONTEND_API_URI string = Website.outputs.uri diff --git a/infra/core/ai/cognitiveservices.bicep b/infra/core/ai/cognitiveservices.bicep new file mode 100644 index 000000000..1eafef93f --- /dev/null +++ b/infra/core/ai/cognitiveservices.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure Cognitive Services instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +@description('The custom subdomain name used to access the API. Defaults to the value of the name parameter.') +param customSubDomainName string = name +param deployments array = [] +param kind string = 'OpenAI' +param publicNetworkAccess string = 'Enabled' +param sku object = { + name: 'S0' +} + +resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + customSubDomainName: customSubDomainName + publicNetworkAccess: publicNetworkAccess + } + sku: sku +} + +@batchSize(1) +resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = [for deployment in deployments: { + parent: account + name: deployment.name + properties: { + model: deployment.model + raiPolicyName: contains(deployment, 'raiPolicyName') ? deployment.raiPolicyName : null + } + sku: contains(deployment, 'sku') ? deployment.sku : { + name: 'Standard' + capacity: 20 + } +}] + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name diff --git a/infra/core/host/appservice-appsettings.bicep b/infra/core/host/appservice-appsettings.bicep new file mode 100644 index 000000000..f4b22f816 --- /dev/null +++ b/infra/core/host/appservice-appsettings.bicep @@ -0,0 +1,17 @@ +metadata description = 'Updates app settings for an Azure App Service.' +@description('The name of the app service resource within the current resource group scope') +param name string + +@description('The app settings to be applied to the app service') +@secure() +param appSettings object + +resource appService 'Microsoft.Web/sites@2022-03-01' existing = { + name: name +} + +resource settings 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'appsettings' + parent: appService + properties: appSettings +} diff --git a/infra/core/host/appservice.bicep b/infra/core/host/appservice.bicep new file mode 100644 index 000000000..4626bab5f --- /dev/null +++ b/infra/core/host/appservice.bicep @@ -0,0 +1,118 @@ +metadata description = 'Creates an Azure App Service in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Microsoft.Web/sites Properties +param kind string = 'app,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = false +param use32BitWorkerProcess bool = false +param ftpsState string = 'FtpsOnly' +param healthCheckPath string = '' + +resource appService 'Microsoft.Web/sites@2022-03-01' = { + name: name + location: location + tags: tags + kind: kind + properties: { + serverFarmId: appServicePlanId + siteConfig: { + linuxFxVersion: linuxFxVersion + alwaysOn: alwaysOn + ftpsState: ftpsState + minTlsVersion: '1.2' + appCommandLine: appCommandLine + numberOfWorkers: numberOfWorkers != -1 ? numberOfWorkers : null + minimumElasticInstanceCount: minimumElasticInstanceCount != -1 ? minimumElasticInstanceCount : null + use32BitWorkerProcess: use32BitWorkerProcess + functionAppScaleLimit: functionAppScaleLimit != -1 ? functionAppScaleLimit : null + healthCheckPath: healthCheckPath + cors: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } + clientAffinityEnabled: clientAffinityEnabled + httpsOnly: true + } + + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + + resource configLogs 'config' = { + name: 'logs' + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + } + + resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +module config 'appservice-appsettings.bicep' = if (!empty(appSettings)) { + name: '${name}-appSettings' + params: { + name: appService.name + appSettings: union(appSettings, + { + SCM_DO_BUILD_DURING_DEPLOYMENT: string(scmDoBuildDuringDeployment) + ENABLE_ORYX_BUILD: string(enableOryxBuild) + }, + runtimeName == 'python' && appCommandLine == '' ? { PYTHON_ENABLE_GUNICORN_MULTIWORKERS: 'true'} : {}, + !empty(applicationInsightsName) ? { APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString } : {}, + !empty(keyVaultName) ? { AZURE_KEY_VAULT_ENDPOINT: keyVault.properties.vaultUri } : {}) + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { + name: keyVaultName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output identityPrincipalId string = managedIdentity ? appService.identity.principalId : '' +output name string = appService.name +output uri string = 'https://${appService.properties.defaultHostName}' diff --git a/infra/core/host/appserviceplan.bicep b/infra/core/host/appserviceplan.bicep new file mode 100644 index 000000000..2e37e041f --- /dev/null +++ b/infra/core/host/appserviceplan.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param kind string = '' +param reserved bool = true +param sku object + +resource appServicePlan 'Microsoft.Web/serverfarms@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + kind: kind + properties: { + reserved: reserved + } +} + +output id string = appServicePlan.id +output name string = appServicePlan.name diff --git a/infra/core/host/functions.bicep b/infra/core/host/functions.bicep new file mode 100644 index 000000000..7070a2c66 --- /dev/null +++ b/infra/core/host/functions.bicep @@ -0,0 +1,86 @@ +metadata description = 'Creates an Azure Function in an existing Azure App Service plan.' +param name string +param location string = resourceGroup().location +param tags object = {} + +// Reference Properties +param applicationInsightsName string = '' +param appServicePlanId string +param keyVaultName string = '' +param managedIdentity bool = !empty(keyVaultName) +param storageAccountName string + +// Runtime Properties +@allowed([ + 'dotnet', 'dotnetcore', 'dotnet-isolated', 'node', 'python', 'java', 'powershell', 'custom' +]) +param runtimeName string +param runtimeNameAndVersion string = '${runtimeName}|${runtimeVersion}' +param runtimeVersion string + +// Function Settings +@allowed([ + '~4', '~3', '~2', '~1' +]) +param extensionVersion string = '~4' + +// Microsoft.Web/sites Properties +param kind string = 'functionapp,linux' + +// Microsoft.Web/sites/config +param allowedOrigins array = [] +param alwaysOn bool = true +param appCommandLine string = '' +@secure() +param appSettings object = {} +param clientAffinityEnabled bool = false +param enableOryxBuild bool = contains(kind, 'linux') +param functionAppScaleLimit int = -1 +param linuxFxVersion string = runtimeNameAndVersion +param minimumElasticInstanceCount int = -1 +param numberOfWorkers int = -1 +param scmDoBuildDuringDeployment bool = true +param use32BitWorkerProcess bool = false +param healthCheckPath string = '' + +module functions 'appservice.bicep' = { + name: '${name}-functions' + params: { + name: name + location: location + tags: tags + allowedOrigins: allowedOrigins + alwaysOn: alwaysOn + appCommandLine: appCommandLine + applicationInsightsName: applicationInsightsName + appServicePlanId: appServicePlanId + appSettings: union(appSettings, { + AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + FUNCTIONS_EXTENSION_VERSION: extensionVersion + FUNCTIONS_WORKER_RUNTIME: runtimeName + }) + clientAffinityEnabled: clientAffinityEnabled + enableOryxBuild: enableOryxBuild + functionAppScaleLimit: functionAppScaleLimit + healthCheckPath: healthCheckPath + keyVaultName: keyVaultName + kind: kind + linuxFxVersion: linuxFxVersion + managedIdentity: managedIdentity + minimumElasticInstanceCount: minimumElasticInstanceCount + numberOfWorkers: numberOfWorkers + runtimeName: runtimeName + runtimeVersion: runtimeVersion + runtimeNameAndVersion: runtimeNameAndVersion + scmDoBuildDuringDeployment: scmDoBuildDuringDeployment + use32BitWorkerProcess: use32BitWorkerProcess + } +} + +resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { + name: storageAccountName +} + +output identityPrincipalId string = managedIdentity ? functions.outputs.identityPrincipalId : '' +output name string = functions.outputs.name +output uri string = functions.outputs.uri diff --git a/infra/core/monitor/applicationinsights-dashboard.bicep b/infra/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 000000000..d082e668e --- /dev/null +++ b/infra/core/monitor/applicationinsights-dashboard.bicep @@ -0,0 +1,1236 @@ +metadata description = 'Creates a dashboard for an Application Insights instance.' +param name string +param applicationInsightsName string +param location string = resourceGroup().location +param tags object = {} + +// 2020-09-01-preview because that is the latest valid version +resource applicationInsightsDashboard 'Microsoft.Portal/dashboards@2020-09-01-preview' = { + name: name + location: location + tags: tags + properties: { + lenses: [ + { + order: 0 + parts: [ + { + position: { + x: 0 + y: 0 + colSpan: 2 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'id' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AspNetOverviewPinnedPart' + asset: { + idInputName: 'id' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'overview' + } + } + { + position: { + x: 2 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/ProactiveDetectionAsyncPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'ProactiveDetection' + } + } + { + position: { + x: 3 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/QuickPulseButtonSmallPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:20:33.345Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AvailabilityNavButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 5 + y: 0 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-08T18:47:35.237Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'ConfigurationId' + value: '78ce933e-e864-4b05-a27b-71fd55a6afad' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/AppMapButtonPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 0 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Usage' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 3 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + endTime: null + createdTime: '2018-05-04T01:22:35.782Z' + isInitialTime: true + grain: 1 + useDashboardTimeRange: false + } + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/UsageUsersOverviewPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + } + } + { + position: { + x: 4 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Reliability' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 7 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:42:40.072Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '8a02f7bf-ac0f-40e1-afe9-f0e72cfee77f' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladeFailuresPinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'failures' + } + } + { + position: { + x: 8 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Responsiveness\r\n' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 11 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ResourceId' + value: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + { + name: 'DataModel' + value: { + version: '1.0.0' + timeContext: { + durationMs: 86400000 + createdTime: '2018-05-04T23:43:37.804Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + isOptional: true + } + { + name: 'ConfigurationId' + value: '2a8ede4f-2bee-4b9c-aed9-2db0e8a01865' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/CuratedBladePerformancePinnedPart' + isAdapter: true + asset: { + idInputName: 'ResourceId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'performance' + } + } + { + position: { + x: 12 + y: 1 + colSpan: 3 + rowSpan: 1 + } + metadata: { + inputs: [] + type: 'Extension/HubsExtension/PartType/MarkdownPart' + settings: { + content: { + settings: { + content: '# Browser' + title: '' + subtitle: '' + } + } + } + } + } + { + position: { + x: 15 + y: 1 + colSpan: 1 + rowSpan: 1 + } + metadata: { + inputs: [ + { + name: 'ComponentId' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'MetricsExplorerJsonDefinitionId' + value: 'BrowserPerformanceTimelineMetrics' + } + { + name: 'TimeContext' + value: { + durationMs: 86400000 + createdTime: '2018-05-08T12:16:27.534Z' + isInitialTime: false + grain: 1 + useDashboardTimeRange: false + } + } + { + name: 'CurrentFilter' + value: { + eventTypes: [ + 4 + 1 + 3 + 5 + 2 + 6 + 13 + ] + typeFacets: {} + isPermissive: false + } + } + { + name: 'id' + value: { + Name: applicationInsights.name + SubscriptionId: subscription().subscriptionId + ResourceGroup: resourceGroup().name + } + } + { + name: 'Version' + value: '1.0' + } + ] + #disable-next-line BCP036 + type: 'Extension/AppInsightsExtension/PartType/MetricsExplorerBladePinnedPart' + asset: { + idInputName: 'ComponentId' + type: 'ApplicationInsights' + } + defaultMenuItemId: 'browser' + } + } + { + position: { + x: 0 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'sessions/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Sessions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'users/count' + aggregationType: 5 + namespace: 'microsoft.insights/components/kusto' + metricVisualization: { + displayName: 'Users' + color: '#7E58FF' + } + } + ] + title: 'Unique sessions and users' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'segmentationUsers' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Failed requests' + color: '#EC008C' + } + } + ] + title: 'Failed requests' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'failures' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'requests/duration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server response time' + color: '#00BCF2' + } + } + ] + title: 'Server response time' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'performance' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 2 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/networkDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Page load network connect time' + color: '#7E58FF' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/processingDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Client processing time' + color: '#44F1C8' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/sendDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Send request time' + color: '#EB9371' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'browserTimings/receiveDuration' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Receiving response time' + color: '#0672F1' + } + } + ] + title: 'Average page load time breakdown' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/availabilityPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability' + color: '#47BDF5' + } + } + ] + title: 'Average availability' + visualization: { + chartType: 3 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + openBladeOnClick: { + openBlade: true + destinationBlade: { + extensionName: 'HubsExtension' + bladeName: 'ResourceMenuBlade' + parameters: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + menuid: 'availability' + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/server' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Server exceptions' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'dependencies/failed' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Dependency failures' + color: '#7E58FF' + } + } + ] + title: 'Server exceptions and Dependency failures' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processorCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Processor time' + color: '#47BDF5' + } + } + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processCpuPercentage' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process CPU' + color: '#7E58FF' + } + } + ] + title: 'Average processor and process CPU utilization' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 12 + y: 5 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'exceptions/browser' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Browser exceptions' + color: '#47BDF5' + } + } + ] + title: 'Browser exceptions' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 0 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'availabilityResults/count' + aggregationType: 7 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Availability test results count' + color: '#47BDF5' + } + } + ] + title: 'Availability test results count' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 4 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/processIOBytesPerSecond' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Process IO rate' + color: '#47BDF5' + } + } + ] + title: 'Average process I/O rate' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + { + position: { + x: 8 + y: 8 + colSpan: 4 + rowSpan: 3 + } + metadata: { + inputs: [ + { + name: 'options' + value: { + chart: { + metrics: [ + { + resourceMetadata: { + id: '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/Microsoft.Insights/components/${applicationInsights.name}' + } + name: 'performanceCounters/memoryAvailableBytes' + aggregationType: 4 + namespace: 'microsoft.insights/components' + metricVisualization: { + displayName: 'Available memory' + color: '#47BDF5' + } + } + ] + title: 'Average available memory' + visualization: { + chartType: 2 + legendVisualization: { + isVisible: true + position: 2 + hideSubtitle: false + } + axisVisualization: { + x: { + isVisible: true + axisType: 2 + } + y: { + isVisible: true + axisType: 1 + } + } + } + } + } + } + { + name: 'sharedTimeRange' + isOptional: true + } + ] + #disable-next-line BCP036 + type: 'Extension/HubsExtension/PartType/MonitorChartPart' + settings: {} + } + } + ] + } + ] + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = { + name: applicationInsightsName +} diff --git a/infra/core/monitor/applicationinsights.bicep b/infra/core/monitor/applicationinsights.bicep new file mode 100644 index 000000000..4ee08255a --- /dev/null +++ b/infra/core/monitor/applicationinsights.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' +param name string +param dashboardName string +param location string = resourceGroup().location +param tags object = {} +param includeDashboard bool = true +param logAnalyticsWorkspaceId string + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { + name: name + location: location + tags: tags + kind: 'web' + properties: { + Application_Type: 'web' + WorkspaceResourceId: logAnalyticsWorkspaceId + } +} + +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (includeDashboard) { + name: 'application-insights-dashboard' + params: { + name: dashboardName + location: location + applicationInsightsName: applicationInsights.name + } +} + +output connectionString string = applicationInsights.properties.ConnectionString +output instrumentationKey string = applicationInsights.properties.InstrumentationKey +output name string = applicationInsights.name diff --git a/infra/core/monitor/loganalytics.bicep b/infra/core/monitor/loganalytics.bicep new file mode 100644 index 000000000..33f9dc294 --- /dev/null +++ b/infra/core/monitor/loganalytics.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates a Log Analytics workspace.' +param name string +param location string = resourceGroup().location +param tags object = {} + +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = { + name: name + location: location + tags: tags + properties: any({ + retentionInDays: 30 + features: { + searchVersion: 1 + } + sku: { + name: 'PerGB2018' + } + }) +} + +output id string = logAnalytics.id +output name string = logAnalytics.name diff --git a/infra/core/monitor/monitoring.bicep b/infra/core/monitor/monitoring.bicep new file mode 100644 index 000000000..165ada8b3 --- /dev/null +++ b/infra/core/monitor/monitoring.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' +param logAnalyticsName string +param applicationInsightsName string +param applicationInsightsDashboardName string +param location string = resourceGroup().location +param tags object = {} +param includeDashboard bool = true + +module logAnalytics 'loganalytics.bicep' = { + name: 'loganalytics' + params: { + name: logAnalyticsName + location: location + tags: tags + } +} + +module applicationInsights 'applicationinsights.bicep' = { + name: 'applicationinsights' + params: { + name: applicationInsightsName + location: location + tags: tags + dashboardName: applicationInsightsDashboardName + includeDashboard: includeDashboard + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +output applicationInsightsConnectionString string = applicationInsights.outputs.connectionString +output applicationInsightsInstrumentationKey string = applicationInsights.outputs.instrumentationKey +output applicationInsightsName string = applicationInsights.outputs.name +output logAnalyticsWorkspaceId string = logAnalytics.outputs.id +output logAnalyticsWorkspaceName string = logAnalytics.outputs.name diff --git a/infra/core/search/search-services.bicep b/infra/core/search/search-services.bicep new file mode 100644 index 000000000..be6e3b344 --- /dev/null +++ b/infra/core/search/search-services.bicep @@ -0,0 +1,63 @@ +metadata description = 'Creates an Azure Cognitive Search instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'standard' +} + +param authOptions object = {} +param disableLocalAuth bool = false +param disabledDataExfiltrationOptions array = [] +param encryptionWithCmk object = { + enforcement: 'Unspecified' +} +@allowed([ + 'default' + 'highDensity' +]) +param hostingMode string = 'default' +param networkRuleSet object = { + bypass: 'None' + ipRules: [] +} +param partitionCount int = 1 +@allowed([ + 'enabled' + 'disabled' +]) +param publicNetworkAccess string = 'enabled' +param replicaCount int = 1 +@allowed([ + 'disabled' + 'free' + 'standard' +]) +param semanticSearch string = 'disabled' + +resource search 'Microsoft.Search/searchServices@2021-04-01-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + authOptions: authOptions + disableLocalAuth: disableLocalAuth + disabledDataExfiltrationOptions: disabledDataExfiltrationOptions + encryptionWithCmk: encryptionWithCmk + hostingMode: hostingMode + networkRuleSet: networkRuleSet + partitionCount: partitionCount + publicNetworkAccess: publicNetworkAccess + replicaCount: replicaCount + semanticSearch: semanticSearch + } + sku: sku +} + +output id string = search.id +output endpoint string = 'https://${name}.search.windows.net/' +output name string = search.name diff --git a/infra/main.bicep b/infra/main.bicep new file mode 100644 index 000000000..1843d1deb --- /dev/null +++ b/infra/main.bicep @@ -0,0 +1,409 @@ +targetScope = 'subscription' + +@minLength(1) +@maxLength(64) +@description('Name of the the environment which is used to generate a short unique hash used in all resources.') +param environmentName string + +@description('provide a 2-13 character prefix for all resources.') +param ResourcePrefix string = environmentName + +@description('Location for all resources.') +param Location string + +@description('Name of App Service plan') +param HostingPlanName string = '${ResourcePrefix}-hosting-plan' + +@description('The pricing tier for the App Service plan') +@allowed([ + 'F1' + 'D1' + 'B1' + 'B2' + 'B3' + 'S1' + 'S2' + 'S3' + 'P1' + 'P2' + 'P3' + 'P4' +]) +param HostingPlanSku string = 'B3' + +@description('Name of Web App') +param WebsiteName string = '${ResourcePrefix}-website' + +@description('Name of Application Insights') +param ApplicationInsightsName string = '${ResourcePrefix}-appinsights' + +@description('Use semantic search') +param AzureSearchUseSemanticSearch string = 'false' + +@description('Semantic search config') +param AzureSearchSemanticSearchConfig string = 'default' + +@description('Is the index prechunked') +param AzureSearchIndexIsPrechunked string = 'false' + +@description('Top K results') +param AzureSearchTopK string = '5' + +@description('Enable in domain') +param AzureSearchEnableInDomain string = 'false' + +@description('Content columns') +param AzureSearchContentColumns string = 'content' + +@description('Filename column') +param AzureSearchFilenameColumn string = 'filename' + +@description('Title column') +param AzureSearchTitleColumn string = 'title' + +@description('Url column') +param AzureSearchUrlColumn string = 'url' + +@description('Name of Azure OpenAI Resource') +param AzureOpenAIResource string = '${ResourcePrefix}-openai' + +@description('Name of Azure OpenAI Resource SKU') +param AzureOpenAISkuName string = 'S0' + +@description('Azure OpenAI Model Deployment Name') +param AzureOpenAIModel string = 'gpt-35-turbo' + +@description('Azure OpenAI Model Name') +param AzureOpenAIModelName string = 'gpt-35-turbo' + +param AzureOpenAIModelVersion string = '0613' + +@description('Orchestration strategy: openai_function or langchain str. If you use a old version of turbo (0301), plese select langchain') +@allowed([ + 'openai_function' + 'langchain' +]) +param OrchestrationStrategy string = 'langchain' + +@description('Azure OpenAI Temperature') +param AzureOpenAITemperature string = '0' + +@description('Azure OpenAI Top P') +param AzureOpenAITopP string = '1' + +@description('Azure OpenAI Max Tokens') +param AzureOpenAIMaxTokens string = '1000' + +@description('Azure OpenAI Stop Sequence') +param AzureOpenAIStopSequence string = '\n' + +@description('Azure OpenAI System Message') +param AzureOpenAISystemMessage string = 'You are an AI assistant that helps people find information.' + +@description('Azure OpenAI Api Version') +param AzureOpenAIApiVersion string = '2023-07-01-preview' + +@description('Whether or not to stream responses from Azure OpenAI') +param AzureOpenAIStream string = 'true' + +@description('Azure OpenAI Embedding Model Deployment Name') +param AzureOpenAIEmbeddingModel string = 'text-embedding-ada-002' + +@description('Azure OpenAI Embedding Model Name') +param AzureOpenAIEmbeddingModelName string = 'text-embedding-ada-002' + +@description('Azure Cognitive Search Resource') +param AzureCognitiveSearch string = '${ResourcePrefix}-search' + +@description('The SKU of the search service you want to create. E.g. free or standard') +@allowed([ + 'free' + 'basic' + 'standard' + 'standard2' + 'standard3' +]) +param AzureCognitiveSearchSku string = 'standard' + +@description('Azure Cognitive Search Index') +param AzureSearchIndex string = '${ResourcePrefix}-index' + +@description('Azure Cognitive Search Conversation Log Index') +param AzureSearchConversationLogIndex string = 'conversations' + +@description('Name of Storage Account') +param StorageAccountName string = '${ResourcePrefix}str' + +@description('Name of Function App for Batch document processing') +param FunctionName string = '${ResourcePrefix}-backend' + +@description('Azure Form Recognizer Name') +param FormRecognizerName string = '${ResourcePrefix}-formrecog' + +@description('Azure Content Safety Name') +param ContentSafetyName string = '${ResourcePrefix}-contentsafety' +param newGuidString string = newGuid() + +var BlobContainerName = 'documents' +var QueueName = 'doc-processing' +var ClientKey = '${uniqueString(guid(subscription().id, deployment().name))}${newGuidString}' +var EventGridSystemTopicName = 'doc-processing' +var tags = { 'azd-env-name': environmentName } + +resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: 'rg-${ResourcePrefix}' + location: Location + tags: tags +} + +module OpenAI 'core/ai/cognitiveservices.bicep' = { + name: 'openai' + scope: rg + params: { + name: AzureOpenAIResource + location: Location + tags: tags + sku: { + name: AzureOpenAISkuName + } + deployments: [ + { + name: AzureOpenAIModel + model: { + format: 'OpenAI' + name: AzureOpenAIModelName + version: AzureOpenAIModelVersion + } + sku: { + name: 'Standard' + capacity: 30 + } + } + { + name: AzureOpenAIEmbeddingModel + model: { + format: 'OpenAI' + name: AzureOpenAIEmbeddingModelName + version: '2' + } + capacity: 30 + } + ] + } +} + +module AzureCognitiveSearch_resource './core/search/search-services.bicep' = { + name: AzureCognitiveSearch + scope: rg + params:{ + name: AzureCognitiveSearch + location: Location + tags: { + deployment : 'chatwithyourdata-sa' + } + sku: { + name: AzureCognitiveSearchSku + } + } +} + +module Other_Resources './app/resources.bicep' = { + name: 'AllResources' + scope: rg + params: { + FormRecognizerName: FormRecognizerName + ContentSafetyName: ContentSafetyName + Location: Location + EventGridSystemTopicName: EventGridSystemTopicName + StorageAccountId: StorageAccount.outputs.StorageAccountId + QueueName: StorageAccount.outputs.StorageAccountName_default_doc_processing_name + BlobContainerName: BlobContainerName + } +} + +module HostingPlan './core/host/appserviceplan.bicep' = { + name: HostingPlanName + scope: rg + params: { + name: HostingPlanName + location: Location + sku: { + name: HostingPlanSku + } + kind: 'linux' + reserved: true + } +} + +module Website './app/websiteapi.bicep' = { + name: WebsiteName + scope: rg + params: { + name: WebsiteName + location: Location + tags: { 'azd-service-name': 'Website' } + appServicePlanId: HostingPlanName + StorageAccountName: StorageAccountName + applicationInsightsName: monitoring.outputs.applicationInsightsName + AzureOpenAIName: OpenAI.outputs.name + AzureCognitiveSearchName: AzureCognitiveSearch + FormRecognizerName: FormRecognizerName + ContentSafetyName: ContentSafetyName + appSettings: [ + { name: 'AZURE_SEARCH_SERVICE', value: 'https://${AzureCognitiveSearch}.search.windows.net'} + { name: 'AZURE_SEARCH_INDEX', value: AzureSearchIndex} + { name: 'AZURE_SEARCH_CONVERSATIONS_LOG_INDEX', value: AzureSearchConversationLogIndex} + { name: 'AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG', value: AzureSearchSemanticSearchConfig} + { name: 'AZURE_SEARCH_INDEX_IS_PRECHUNKED', value: AzureSearchIndexIsPrechunked} + { name: 'AZURE_SEARCH_TOP_K', value: AzureSearchTopK} + { name: 'AZURE_SEARCH_ENABLE_IN_DOMAIN', value: AzureSearchEnableInDomain} + { name: 'AZURE_SEARCH_CONTENT_COLUMNS', value: AzureSearchContentColumns} + { name: 'AZURE_SEARCH_FILENAME_COLUMN', value: AzureSearchFilenameColumn} + { name: 'AZURE_SEARCH_TITLE_COLUMN', value: AzureSearchTitleColumn} + { name: 'AZURE_SEARCH_URL_COLUMN', value: AzureSearchUrlColumn} + { name: 'AZURE_OPENAI_RESOURCE', value: AzureOpenAIResource} + { name: 'AZURE_OPENAI_MODEL', value: AzureOpenAIModel} + { name: 'AZURE_OPENAI_MODEL_NAME', value: AzureOpenAIModelName} + { name: 'AZURE_OPENAI_TEMPERATURE', value: AzureOpenAITemperature} + { name: 'AZURE_OPENAI_TOP_P', value: AzureOpenAITopP} + { name: 'AZURE_OPENAI_MAX_TOKENS', value: AzureOpenAIMaxTokens} + { name: 'AZURE_OPENAI_STOP_SEQUENCE', value: AzureOpenAIStopSequence} + { name: 'AZURE_OPENAI_SYSTEM_MESSAGE', value: AzureOpenAISystemMessage} + { name: 'AZURE_OPENAI_API_VERSION', value: AzureOpenAIApiVersion} + { name: 'AZURE_OPENAI_STREAM', value: AzureOpenAIStream} + { name: 'AZURE_OPENAI_EMBEDDING_MODEL', value: AzureOpenAIEmbeddingModel} + { name: 'AZURE_FORM_RECOGNIZER_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} + { name: 'AZURE_BLOB_ACCOUNT_NAME', value: StorageAccountName} + { name: 'AZURE_BLOB_CONTAINER_NAME', value: BlobContainerName} + { name: 'ORCHESTRATION_STRATEGY', value: OrchestrationStrategy} + { name: 'AZURE_CONTENT_SAFETY_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} + ] + } + dependsOn:[ + HostingPlan + StorageAccount + monitoring + OpenAI + ] +} + +module WebsiteName_admin './app/websiteadmin.bicep' = { + name: '${WebsiteName}-admin' + scope: rg + params: { + name: '${WebsiteName}-admin' + location: Location + tags: { 'azd-service-name': 'WebsiteName_admin' } + appServicePlanId: HostingPlanName + StorageAccountName: StorageAccountName + applicationInsightsName: monitoring.outputs.applicationInsightsName + AzureOpenAIName: OpenAI.outputs.name + AzureCognitiveSearchName: AzureCognitiveSearch + FormRecognizerName: FormRecognizerName + ContentSafetyName: ContentSafetyName + appSettings: [ + { name: 'AZURE_SEARCH_SERVICE', value: 'https://${AzureCognitiveSearch}.search.windows.net' } + { name: 'AZURE_SEARCH_INDEX', value: AzureSearchIndex } + { name: 'AZURE_SEARCH_USE_SEMANTIC_SEARCH', value: AzureSearchUseSemanticSearch } + { name: 'AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG', value: AzureSearchSemanticSearchConfig } + { name: 'AZURE_SEARCH_INDEX_IS_PRECHUNKED', value: AzureSearchIndexIsPrechunked } + { name: 'AZURE_SEARCH_TOP_K', value: AzureSearchTopK } + { name: 'AZURE_SEARCH_ENABLE_IN_DOMAIN', value: AzureSearchEnableInDomain } + { name: 'AZURE_SEARCH_CONTENT_COLUMNS', value: AzureSearchContentColumns} + { name: 'AZURE_SEARCH_FILENAME_COLUMN', value: AzureSearchFilenameColumn } + { name: 'AZURE_SEARCH_TITLE_COLUMN', value: AzureSearchTitleColumn} + { name: 'AZURE_SEARCH_URL_COLUMN', value: AzureSearchUrlColumn } + { name: 'AZURE_OPENAI_RESOURCE', value: AzureOpenAIResource} + { name: 'AZURE_OPENAI_MODEL', value: AzureOpenAIModel } + { name: 'AZURE_OPENAI_MODEL_NAME', value: AzureOpenAIModelName } + { name: 'AZURE_OPENAI_TEMPERATURE', value: AzureOpenAITemperature } + { name: 'AZURE_OPENAI_TOP_P', value: AzureOpenAITopP } + { name: 'AZURE_OPENAI_MAX_TOKENS', value: AzureOpenAIMaxTokens } + { name: 'AZURE_OPENAI_STOP_SEQUENCE', value: AzureOpenAIStopSequence } + { name: 'AZURE_OPENAI_SYSTEM_MESSAGE', value: AzureOpenAISystemMessage } + { name: 'AZURE_OPENAI_API_VERSION', value: AzureOpenAIApiVersion } + { name: 'AZURE_OPENAI_STREAM', value: AzureOpenAIStream } + { name: 'AZURE_OPENAI_EMBEDDING_MODEL', value: AzureOpenAIEmbeddingModel } + { name: 'AZURE_FORM_RECOGNIZER_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/' } + { name: 'AZURE_BLOB_ACCOUNT_NAME', value: StorageAccountName } + { name: 'AZURE_BLOB_CONTAINER_NAME', value: BlobContainerName } + { name: 'DOCUMENT_PROCESSING_QUEUE_NAME', value: QueueName} + { name: 'BACKEND_URL', value: 'https://${FunctionName}.azurewebsites.net'} + { name: 'FUNCTION_KEY', value: ClientKey} + { name: 'ORCHESTRATION_STRATEGY', value: OrchestrationStrategy} + { name: 'AZURE_CONTENT_SAFETY_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} + ] + } + dependsOn:[ + HostingPlan + StorageAccount + monitoring + OpenAI + ] +} + +module StorageAccount './app/storage.bicep' = { + name: 'Storage_Account' + scope: rg + params: { + StorageAccountName: StorageAccountName + Location: Location + BlobContainerName: BlobContainerName + } +} + +module monitoring './core/monitor/monitoring.bicep' = { + name: 'monitoring' + scope: rg + params: { + applicationInsightsName: ApplicationInsightsName + location: Location + tags: { + 'hidden-link:${resourceId('Microsoft.Web/sites', ApplicationInsightsName)}': 'Resource' + } + logAnalyticsName: '${ResourcePrefix}-logAnalytics' + applicationInsightsDashboardName: 'dash-${ApplicationInsightsName}' + } +} + +module Function './app/function.bicep' = { + name: FunctionName + scope: rg + params:{ + name: FunctionName + location: Location + tags: { 'azd-service-name': 'Function' } + appServicePlanId: HostingPlanName + storageAccountName: StorageAccountName + applicationInsightsName: monitoring.outputs.applicationInsightsName + AzureOpenAIName: OpenAI.outputs.name + AzureCognitiveSearchName: AzureCognitiveSearch + FormRecognizerName: FormRecognizerName + ContentSafetyName: ContentSafetyName + runtimeName:'python' + runtimeVersion:'3.11' + ClientKey: ClientKey + appSettings: [ + { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4'} + { name: 'WEBSITES_ENABLE_APP_SERVICE_STORAGE', value: 'false'} + { name: 'AZURE_OPENAI_MODEL', value: AzureOpenAIModel} + { name: 'AZURE_OPENAI_EMBEDDING_MODEL', value: AzureOpenAIEmbeddingModel} + { name: 'AZURE_OPENAI_RESOURCE', value: AzureOpenAIResource} + { name: 'AZURE_BLOB_ACCOUNT_NAME', value: StorageAccountName} + { name: 'AZURE_BLOB_CONTAINER_NAME', value: BlobContainerName} + { name: 'AZURE_FORM_RECOGNIZER_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} + { name: 'AZURE_SEARCH_SERVICE', value: 'https://${AzureCognitiveSearch}.search.windows.net'} + { name: 'DOCUMENT_PROCESSING_QUEUE_NAME', value: QueueName} + { name: 'AZURE_OPENAI_API_VERSION', value: AzureOpenAIApiVersion} + { name: 'AZURE_SEARCH_INDEX', value: AzureSearchIndex} + { name: 'ORCHESTRATION_STRATEGY', value: OrchestrationStrategy} + { name: 'AZURE_CONTENT_SAFETY_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} + ] + } + dependsOn:[ + StorageAccount + HostingPlan + monitoring + OpenAI + ] +} diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 000000000..47a5e3f25 --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "Location": { + "value": "${AZURE_LOCATION}" + } + } +} From fbfd9b704bbf75c14617630b064306bdda3a4ad8 Mon Sep 17 00:00:00 2001 From: zedy Date: Sun, 5 Nov 2023 14:11:24 +0800 Subject: [PATCH 02/37] instead parameters.json file with .bicepparam --- infra/main.bicepparam | 5 +++++ infra/main.parameters.json | 12 ------------ 2 files changed, 5 insertions(+), 12 deletions(-) create mode 100644 infra/main.bicepparam delete mode 100644 infra/main.parameters.json diff --git a/infra/main.bicepparam b/infra/main.bicepparam new file mode 100644 index 000000000..f9bfb364a --- /dev/null +++ b/infra/main.bicepparam @@ -0,0 +1,5 @@ +using './main.bicep' + +param environmentName = readEnvironmentVariable('AZURE_ENV_NAME') + +param Location = readEnvironmentVariable('AZURE_LOCATION') diff --git a/infra/main.parameters.json b/infra/main.parameters.json deleted file mode 100644 index 47a5e3f25..000000000 --- a/infra/main.parameters.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "environmentName": { - "value": "${AZURE_ENV_NAME}" - }, - "Location": { - "value": "${AZURE_LOCATION}" - } - } -} From d32d977eedb7851e238b5a228ac1831b1b48bfaf Mon Sep 17 00:00:00 2001 From: zedy Date: Tue, 7 Nov 2023 16:30:10 +0800 Subject: [PATCH 03/37] processing format --- azure.yaml | 6 +- .../{websiteadmin.bicep => adminweb.bicep} | 15 +- infra/app/function.bicep | 13 +- infra/app/{websiteapi.bicep => web.bicep} | 15 +- infra/main.bicep | 166 +++++++++--------- 5 files changed, 109 insertions(+), 106 deletions(-) rename infra/app/{websiteadmin.bicep => adminweb.bicep} (82%) rename infra/app/{websiteapi.bicep => web.bicep} (83%) diff --git a/azure.yaml b/azure.yaml index 710d75f0c..525049475 100644 --- a/azure.yaml +++ b/azure.yaml @@ -3,7 +3,7 @@ name: chat-with-your-data-solution-accelerator services: - Website: + web: project: . language: py host: appservice @@ -15,12 +15,12 @@ services: interactive: true continueOnError: false - WebsiteName_admin: + adminweb: project: ./backend language: py host: appservice - Function: + function: project: ./backend language: py host: function diff --git a/infra/app/websiteadmin.bicep b/infra/app/adminweb.bicep similarity index 82% rename from infra/app/websiteadmin.bicep rename to infra/app/adminweb.bicep index 8b4ecba54..5672d9b1e 100644 --- a/infra/app/websiteadmin.bicep +++ b/infra/app/adminweb.bicep @@ -13,10 +13,11 @@ param AzureCognitiveSearchName string = '' param FormRecognizerName string = '' param ContentSafetyName string = '' -param appSettings array = [] -param serviceName string = 'WebsiteName_admin' +@secure() +param appSettings object = {} +param serviceName string = 'adminweb' -module websiteadmin '../core/host/appservice.bicep' = { +module adminweb '../core/host/appservice.bicep' = { name: '${name}-app-module' params: { name: name @@ -26,7 +27,7 @@ module websiteadmin '../core/host/appservice.bicep' = { appCommandLine: appCommandLine applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId - appSettings: union(toObject(appSettings, entry => entry.name, entry => entry.value), { + appSettings: union(appSettings, { AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey AZURE_OPENAI_KEY: openai.listKeys().key1 @@ -65,6 +66,6 @@ resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existin name: ContentSafetyName } -output WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID string = websiteadmin.outputs.identityPrincipalId -output WEBSITE_ADMIN_NAME string = websiteadmin.outputs.name -output WEBSITE_ADMIN_URI string = websiteadmin.outputs.uri +output WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID string = adminweb.outputs.identityPrincipalId +output WEBSITE_ADMIN_NAME string = adminweb.outputs.name +output WEBSITE_ADMIN_URI string = adminweb.outputs.uri diff --git a/infra/app/function.bicep b/infra/app/function.bicep index e4088b824..f9655d509 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -4,8 +4,9 @@ param appServicePlanId string param storageAccountName string = '' param tags object = {} -param appSettings array = [] -param serviceName string = 'Function' +@secure() +param appSettings object = {} +param serviceName string = 'function' param applicationInsightsName string = '' param runtimeName string param runtimeVersion string @@ -16,7 +17,7 @@ param FormRecognizerName string = '' param ContentSafetyName string = '' -module Function '../core/host/functions.bicep' = { +module function '../core/host/functions.bicep' = { name: '${name}-app-module' params: { name: name @@ -26,7 +27,7 @@ module Function '../core/host/functions.bicep' = { storageAccountName: storageAccountName runtimeName: runtimeName runtimeVersion: runtimeVersion - appSettings: union(toObject(appSettings, entry => entry.name, entry => entry.value), { + appSettings: union(appSettings, { AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey @@ -69,7 +70,7 @@ resource FunctionName_default_clientKey 'Microsoft.Web/sites/host/functionKeys@2 value: ClientKey } dependsOn: [ - Function + function WaitFunctionDeploymentSection ] } @@ -85,6 +86,6 @@ resource WaitFunctionDeploymentSection 'Microsoft.Resources/deploymentScripts@20 retentionInterval: 'PT1H' } dependsOn: [ - Function + function ] } diff --git a/infra/app/websiteapi.bicep b/infra/app/web.bicep similarity index 83% rename from infra/app/websiteapi.bicep rename to infra/app/web.bicep index 11d7cb2a8..1bd52c168 100644 --- a/infra/app/websiteapi.bicep +++ b/infra/app/web.bicep @@ -13,10 +13,11 @@ param AzureCognitiveSearchName string = '' param FormRecognizerName string = '' param ContentSafetyName string = '' -param appSettings array = [] -param serviceName string = 'Website' +@secure() +param appSettings object = {} +param serviceName string = 'web' -module Website '../core/host/appservice.bicep' = { +module web '../core/host/appservice.bicep' = { name: '${name}-app-module' params: { name: name @@ -26,7 +27,7 @@ module Website '../core/host/appservice.bicep' = { appCommandLine: appCommandLine applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId - appSettings: union(toObject(appSettings, entry => entry.name, entry => entry.value), { + appSettings: union(appSettings, { AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value APPINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString AZURE_OPENAI_KEY: openai.listKeys().key1 @@ -66,6 +67,6 @@ resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existin name: ContentSafetyName } -output FRONTEND_API_IDENTITY_PRINCIPAL_ID string = Website.outputs.identityPrincipalId -output FRONTEND_API_NAME string = Website.outputs.name -output FRONTEND_API_URI string = Website.outputs.uri +output FRONTEND_API_IDENTITY_PRINCIPAL_ID string = web.outputs.identityPrincipalId +output FRONTEND_API_NAME string = web.outputs.name +output FRONTEND_API_URI string = web.outputs.uri diff --git a/infra/main.bicep b/infra/main.bicep index 1843d1deb..092abbad7 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -235,13 +235,13 @@ module HostingPlan './core/host/appserviceplan.bicep' = { } } -module Website './app/websiteapi.bicep' = { +module web './app/web.bicep' = { name: WebsiteName scope: rg params: { name: WebsiteName location: Location - tags: { 'azd-service-name': 'Website' } + tags: { 'azd-service-name': 'web' } appServicePlanId: HostingPlanName StorageAccountName: StorageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName @@ -249,35 +249,35 @@ module Website './app/websiteapi.bicep' = { AzureCognitiveSearchName: AzureCognitiveSearch FormRecognizerName: FormRecognizerName ContentSafetyName: ContentSafetyName - appSettings: [ - { name: 'AZURE_SEARCH_SERVICE', value: 'https://${AzureCognitiveSearch}.search.windows.net'} - { name: 'AZURE_SEARCH_INDEX', value: AzureSearchIndex} - { name: 'AZURE_SEARCH_CONVERSATIONS_LOG_INDEX', value: AzureSearchConversationLogIndex} - { name: 'AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG', value: AzureSearchSemanticSearchConfig} - { name: 'AZURE_SEARCH_INDEX_IS_PRECHUNKED', value: AzureSearchIndexIsPrechunked} - { name: 'AZURE_SEARCH_TOP_K', value: AzureSearchTopK} - { name: 'AZURE_SEARCH_ENABLE_IN_DOMAIN', value: AzureSearchEnableInDomain} - { name: 'AZURE_SEARCH_CONTENT_COLUMNS', value: AzureSearchContentColumns} - { name: 'AZURE_SEARCH_FILENAME_COLUMN', value: AzureSearchFilenameColumn} - { name: 'AZURE_SEARCH_TITLE_COLUMN', value: AzureSearchTitleColumn} - { name: 'AZURE_SEARCH_URL_COLUMN', value: AzureSearchUrlColumn} - { name: 'AZURE_OPENAI_RESOURCE', value: AzureOpenAIResource} - { name: 'AZURE_OPENAI_MODEL', value: AzureOpenAIModel} - { name: 'AZURE_OPENAI_MODEL_NAME', value: AzureOpenAIModelName} - { name: 'AZURE_OPENAI_TEMPERATURE', value: AzureOpenAITemperature} - { name: 'AZURE_OPENAI_TOP_P', value: AzureOpenAITopP} - { name: 'AZURE_OPENAI_MAX_TOKENS', value: AzureOpenAIMaxTokens} - { name: 'AZURE_OPENAI_STOP_SEQUENCE', value: AzureOpenAIStopSequence} - { name: 'AZURE_OPENAI_SYSTEM_MESSAGE', value: AzureOpenAISystemMessage} - { name: 'AZURE_OPENAI_API_VERSION', value: AzureOpenAIApiVersion} - { name: 'AZURE_OPENAI_STREAM', value: AzureOpenAIStream} - { name: 'AZURE_OPENAI_EMBEDDING_MODEL', value: AzureOpenAIEmbeddingModel} - { name: 'AZURE_FORM_RECOGNIZER_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} - { name: 'AZURE_BLOB_ACCOUNT_NAME', value: StorageAccountName} - { name: 'AZURE_BLOB_CONTAINER_NAME', value: BlobContainerName} - { name: 'ORCHESTRATION_STRATEGY', value: OrchestrationStrategy} - { name: 'AZURE_CONTENT_SAFETY_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} - ] + appSettings: { + AZURE_SEARCH_SERVICE: 'https://${AzureCognitiveSearch}.search.windows.net' + AZURE_SEARCH_INDEX: AzureSearchIndex + AZURE_SEARCH_CONVERSATIONS_LOG_INDEX: AzureSearchConversationLogIndex + AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: AzureSearchSemanticSearchConfig + AZURE_SEARCH_INDEX_IS_PRECHUNKED: AzureSearchIndexIsPrechunked + AZURE_SEARCH_TOP_K: AzureSearchTopK + AZURE_SEARCH_ENABLE_IN_DOMAIN: AzureSearchEnableInDomain + AZURE_SEARCH_CONTENT_COLUMNS: AzureSearchContentColumns + AZURE_SEARCH_FILENAME_COLUMN: AzureSearchFilenameColumn + AZURE_SEARCH_TITLE_COLUMN: AzureSearchTitleColumn + AZURE_SEARCH_URL_COLUMN: AzureSearchUrlColumn + AZURE_OPENAI_RESOURCE: AzureOpenAIResource + AZURE_OPENAI_MODEL: AzureOpenAIModel + AZURE_OPENAI_MODEL_NAME: AzureOpenAIModelName + AZURE_OPENAI_TEMPERATURE: AzureOpenAITemperature + AZURE_OPENAI_TOP_P: AzureOpenAITopP + AZURE_OPENAI_MAX_TOKENS: AzureOpenAIMaxTokens + AZURE_OPENAI_STOP_SEQUENCE: AzureOpenAIStopSequence + AZURE_OPENAI_SYSTEM_MESSAGE: AzureOpenAISystemMessage + AZURE_OPENAI_API_VERSION: AzureOpenAIApiVersion + AZURE_OPENAI_STREAM: AzureOpenAIStream + AZURE_OPENAI_EMBEDDING_MODEL: AzureOpenAIEmbeddingModel + AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + AZURE_BLOB_ACCOUNT_NAME: StorageAccountName + AZURE_BLOB_CONTAINER_NAME: BlobContainerName + ORCHESTRATION_STRATEGY: OrchestrationStrategy + AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + } } dependsOn:[ HostingPlan @@ -287,13 +287,13 @@ module Website './app/websiteapi.bicep' = { ] } -module WebsiteName_admin './app/websiteadmin.bicep' = { +module adminweb './app/adminweb.bicep' = { name: '${WebsiteName}-admin' scope: rg params: { name: '${WebsiteName}-admin' location: Location - tags: { 'azd-service-name': 'WebsiteName_admin' } + tags: { 'azd-service-name': 'adminweb' } appServicePlanId: HostingPlanName StorageAccountName: StorageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName @@ -301,38 +301,38 @@ module WebsiteName_admin './app/websiteadmin.bicep' = { AzureCognitiveSearchName: AzureCognitiveSearch FormRecognizerName: FormRecognizerName ContentSafetyName: ContentSafetyName - appSettings: [ - { name: 'AZURE_SEARCH_SERVICE', value: 'https://${AzureCognitiveSearch}.search.windows.net' } - { name: 'AZURE_SEARCH_INDEX', value: AzureSearchIndex } - { name: 'AZURE_SEARCH_USE_SEMANTIC_SEARCH', value: AzureSearchUseSemanticSearch } - { name: 'AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG', value: AzureSearchSemanticSearchConfig } - { name: 'AZURE_SEARCH_INDEX_IS_PRECHUNKED', value: AzureSearchIndexIsPrechunked } - { name: 'AZURE_SEARCH_TOP_K', value: AzureSearchTopK } - { name: 'AZURE_SEARCH_ENABLE_IN_DOMAIN', value: AzureSearchEnableInDomain } - { name: 'AZURE_SEARCH_CONTENT_COLUMNS', value: AzureSearchContentColumns} - { name: 'AZURE_SEARCH_FILENAME_COLUMN', value: AzureSearchFilenameColumn } - { name: 'AZURE_SEARCH_TITLE_COLUMN', value: AzureSearchTitleColumn} - { name: 'AZURE_SEARCH_URL_COLUMN', value: AzureSearchUrlColumn } - { name: 'AZURE_OPENAI_RESOURCE', value: AzureOpenAIResource} - { name: 'AZURE_OPENAI_MODEL', value: AzureOpenAIModel } - { name: 'AZURE_OPENAI_MODEL_NAME', value: AzureOpenAIModelName } - { name: 'AZURE_OPENAI_TEMPERATURE', value: AzureOpenAITemperature } - { name: 'AZURE_OPENAI_TOP_P', value: AzureOpenAITopP } - { name: 'AZURE_OPENAI_MAX_TOKENS', value: AzureOpenAIMaxTokens } - { name: 'AZURE_OPENAI_STOP_SEQUENCE', value: AzureOpenAIStopSequence } - { name: 'AZURE_OPENAI_SYSTEM_MESSAGE', value: AzureOpenAISystemMessage } - { name: 'AZURE_OPENAI_API_VERSION', value: AzureOpenAIApiVersion } - { name: 'AZURE_OPENAI_STREAM', value: AzureOpenAIStream } - { name: 'AZURE_OPENAI_EMBEDDING_MODEL', value: AzureOpenAIEmbeddingModel } - { name: 'AZURE_FORM_RECOGNIZER_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/' } - { name: 'AZURE_BLOB_ACCOUNT_NAME', value: StorageAccountName } - { name: 'AZURE_BLOB_CONTAINER_NAME', value: BlobContainerName } - { name: 'DOCUMENT_PROCESSING_QUEUE_NAME', value: QueueName} - { name: 'BACKEND_URL', value: 'https://${FunctionName}.azurewebsites.net'} - { name: 'FUNCTION_KEY', value: ClientKey} - { name: 'ORCHESTRATION_STRATEGY', value: OrchestrationStrategy} - { name: 'AZURE_CONTENT_SAFETY_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} - ] + appSettings: { + AZURE_SEARCH_SERVICE: 'https://${AzureCognitiveSearch}.search.windows.net' + AZURE_SEARCH_INDEX: AzureSearchIndex + AZURE_SEARCH_USE_SEMANTIC_SEARCH: AzureSearchUseSemanticSearch + AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: AzureSearchSemanticSearchConfig + AZURE_SEARCH_INDEX_IS_PRECHUNKED: AzureSearchIndexIsPrechunked + AZURE_SEARCH_TOP_K: AzureSearchTopK + AZURE_SEARCH_ENABLE_IN_DOMAIN: AzureSearchEnableInDomain + AZURE_SEARCH_CONTENT_COLUMNS: AzureSearchContentColumns + AZURE_SEARCH_FILENAME_COLUMN: AzureSearchFilenameColumn + AZURE_SEARCH_TITLE_COLUMN: AzureSearchTitleColumn + AZURE_SEARCH_URL_COLUMN: AzureSearchUrlColumn + AZURE_OPENAI_RESOURCE: AzureOpenAIResource + AZURE_OPENAI_MODEL: AzureOpenAIModel + AZURE_OPENAI_MODEL_NAME: AzureOpenAIModelName + AZURE_OPENAI_TEMPERATURE: AzureOpenAITemperature + AZURE_OPENAI_TOP_P: AzureOpenAITopP + AZURE_OPENAI_MAX_TOKENS: AzureOpenAIMaxTokens + AZURE_OPENAI_STOP_SEQUENCE: AzureOpenAIStopSequence + AZURE_OPENAI_SYSTEM_MESSAGE: AzureOpenAISystemMessage + AZURE_OPENAI_API_VERSION: AzureOpenAIApiVersion + AZURE_OPENAI_STREAM: AzureOpenAIStream + AZURE_OPENAI_EMBEDDING_MODEL: AzureOpenAIEmbeddingModel + AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + AZURE_BLOB_ACCOUNT_NAME: StorageAccountName + AZURE_BLOB_CONTAINER_NAME: BlobContainerName + DOCUMENT_PROCESSING_QUEUE_NAME: QueueName + BACKEND_URL: 'https://${FunctionName}.azurewebsites.net' + FUNCTION_KEY: ClientKey + ORCHESTRATION_STRATEGY: OrchestrationStrategy + AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + } } dependsOn:[ HostingPlan @@ -366,13 +366,13 @@ module monitoring './core/monitor/monitoring.bicep' = { } } -module Function './app/function.bicep' = { +module function './app/function.bicep' = { name: FunctionName scope: rg params:{ name: FunctionName location: Location - tags: { 'azd-service-name': 'Function' } + tags: { 'azd-service-name': 'function' } appServicePlanId: HostingPlanName storageAccountName: StorageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName @@ -383,22 +383,22 @@ module Function './app/function.bicep' = { runtimeName:'python' runtimeVersion:'3.11' ClientKey: ClientKey - appSettings: [ - { name: 'FUNCTIONS_EXTENSION_VERSION', value: '~4'} - { name: 'WEBSITES_ENABLE_APP_SERVICE_STORAGE', value: 'false'} - { name: 'AZURE_OPENAI_MODEL', value: AzureOpenAIModel} - { name: 'AZURE_OPENAI_EMBEDDING_MODEL', value: AzureOpenAIEmbeddingModel} - { name: 'AZURE_OPENAI_RESOURCE', value: AzureOpenAIResource} - { name: 'AZURE_BLOB_ACCOUNT_NAME', value: StorageAccountName} - { name: 'AZURE_BLOB_CONTAINER_NAME', value: BlobContainerName} - { name: 'AZURE_FORM_RECOGNIZER_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} - { name: 'AZURE_SEARCH_SERVICE', value: 'https://${AzureCognitiveSearch}.search.windows.net'} - { name: 'DOCUMENT_PROCESSING_QUEUE_NAME', value: QueueName} - { name: 'AZURE_OPENAI_API_VERSION', value: AzureOpenAIApiVersion} - { name: 'AZURE_SEARCH_INDEX', value: AzureSearchIndex} - { name: 'ORCHESTRATION_STRATEGY', value: OrchestrationStrategy} - { name: 'AZURE_CONTENT_SAFETY_ENDPOINT', value: 'https://${Location}.api.cognitive.microsoft.com/'} - ] + appSettings: { + FUNCTIONS_EXTENSION_VERSION: '~4' + WEBSITES_ENABLE_APP_SERVICE_STORAGE: 'false' + AZURE_OPENAI_MODEL: AzureOpenAIModel + AZURE_OPENAI_EMBEDDING_MODEL: AzureOpenAIEmbeddingModel + AZURE_OPENAI_RESOURCE: AzureOpenAIResource + AZURE_BLOB_ACCOUNT_NAME: StorageAccountName + AZURE_BLOB_CONTAINER_NAME: BlobContainerName + AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + AZURE_SEARCH_SERVICE: 'https://${AzureCognitiveSearch}.search.windows.net' + DOCUMENT_PROCESSING_QUEUE_NAME: QueueName + AZURE_OPENAI_API_VERSION: AzureOpenAIApiVersion + AZURE_SEARCH_INDEX: AzureSearchIndex + ORCHESTRATION_STRATEGY: OrchestrationStrategy + AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + } } dependsOn:[ StorageAccount From 552f2541bc9c6c0ef4a17f35b5488ae52cb24f3a Mon Sep 17 00:00:00 2001 From: zedy Date: Tue, 7 Nov 2023 20:12:46 +0800 Subject: [PATCH 04/37] keep consistent casing for params --- infra/app/adminweb.bicep | 20 +-- infra/app/function.bicep | 20 +-- infra/app/resources.bicep | 34 ++-- infra/app/storage.bicep | 14 +- infra/app/web.bicep | 20 +-- infra/main.bicep | 344 +++++++++++++++++++------------------- infra/main.bicepparam | 2 +- 7 files changed, 227 insertions(+), 227 deletions(-) diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 5672d9b1e..3d33e3646 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -6,12 +6,12 @@ param allowedOrigins array = [] param appServicePlanId string param appCommandLine string = 'python -m streamlit run Admin.py --server.port 8000 --server.address 0.0.0.0 --server.enableXsrfProtection false' param applicationInsightsName string = '' -param StorageAccountName string +param storageAccountName string param keyVaultName string = '' -param AzureOpenAIName string = '' -param AzureCognitiveSearchName string = '' -param FormRecognizerName string = '' -param ContentSafetyName string = '' +param azureOpenAIName string = '' +param azureCognitiveSearchName string = '' +param formRecognizerName string = '' +param contentSafetyName string = '' @secure() param appSettings object = {} @@ -43,7 +43,7 @@ module adminweb '../core/host/appservice.bicep' = { } resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { - name: StorageAccountName + name: storageAccountName } resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { @@ -51,19 +51,19 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing } resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { - name: AzureOpenAIName + name: azureOpenAIName } resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { - name: AzureCognitiveSearchName + name: azureCognitiveSearchName } resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { - name: FormRecognizerName + name: formRecognizerName } resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { - name: ContentSafetyName + name: contentSafetyName } output WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID string = adminweb.outputs.identityPrincipalId diff --git a/infra/app/function.bicep b/infra/app/function.bicep index f9655d509..d0771fb1f 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -10,11 +10,11 @@ param serviceName string = 'function' param applicationInsightsName string = '' param runtimeName string param runtimeVersion string -param ClientKey string -param AzureOpenAIName string = '' -param AzureCognitiveSearchName string = '' -param FormRecognizerName string = '' -param ContentSafetyName string = '' +param clientKey string +param azureOpenAIName string = '' +param azureCognitiveSearchName string = '' +param formRecognizerName string = '' +param contentSafetyName string = '' module function '../core/host/functions.bicep' = { @@ -48,26 +48,26 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing } resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { - name: AzureOpenAIName + name: azureOpenAIName } resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { - name: AzureCognitiveSearchName + name: azureCognitiveSearchName } resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { - name: FormRecognizerName + name: formRecognizerName } resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { - name: ContentSafetyName + name: contentSafetyName } resource FunctionName_default_clientKey 'Microsoft.Web/sites/host/functionKeys@2018-11-01' = { name: '${name}/default/clientKey' properties: { name: 'ClientKey' - value: ClientKey + value: clientKey } dependsOn: [ function diff --git a/infra/app/resources.bicep b/infra/app/resources.bicep index c2fd7c1b0..cd909bafe 100644 --- a/infra/app/resources.bicep +++ b/infra/app/resources.bicep @@ -1,14 +1,14 @@ -param ContentSafetyName string -param FormRecognizerName string -param EventGridSystemTopicName string -param Location string -param StorageAccountId string -param QueueName string -param BlobContainerName string +param contentSafetyName string +param formRecognizerName string +param eventGridSystemTopicName string +param location string +param storageAccountId string +param queueName string +param blobContainerName string resource FormRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { - name: FormRecognizerName - location: Location + name: formRecognizerName + location: location sku: { name: 'S0' } @@ -27,8 +27,8 @@ resource FormRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { } resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { - name: ContentSafetyName - location: Location + name: contentSafetyName + location: location sku: { name: 'S0' } @@ -46,10 +46,10 @@ resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { } resource EventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { - name: EventGridSystemTopicName - location: Location + name: eventGridSystemTopicName + location: location properties: { - source: StorageAccountId + source: storageAccountId topicType: 'Microsoft.Storage.StorageAccounts' } } @@ -62,8 +62,8 @@ resource EventGridSystemTopicName_BlobEvents 'Microsoft.EventGrid/systemTopics/e endpointType: 'StorageQueue' properties: { queueMessageTimeToLiveInSeconds: -1 - queueName: QueueName - resourceId: StorageAccountId + queueName: queueName + resourceId: storageAccountId } } filter: { @@ -72,7 +72,7 @@ resource EventGridSystemTopicName_BlobEvents 'Microsoft.EventGrid/systemTopics/e 'Microsoft.Storage.BlobDeleted' ] enableAdvancedFilteringOnArrays: true - subjectBeginsWith: '/blobServices/default/containers/${BlobContainerName}/blobs/' + subjectBeginsWith: '/blobServices/default/containers/${blobContainerName}/blobs/' } labels: [] eventDeliverySchema: 'EventGridSchema' diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep index ed6803437..7828364e9 100644 --- a/infra/app/storage.bicep +++ b/infra/app/storage.bicep @@ -1,10 +1,10 @@ -param StorageAccountName string = 'Enabled' -param Location string = '' -param BlobContainerName string = 'Enabled' +param storageAccountName string = 'Enabled' +param location string = '' +param blobContainerName string = 'Enabled' resource StorageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { - name: StorageAccountName - location: Location + name: storageAccountName + location: location kind: 'StorageV2' sku: { name: 'Standard_GRS' @@ -12,7 +12,7 @@ resource StorageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { } resource StorageAccountName_default_BlobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { - name: '${StorageAccountName}/default/${BlobContainerName}' + name: '${storageAccountName}/default/${blobContainerName}' properties: { publicAccess: 'None' } @@ -22,7 +22,7 @@ resource StorageAccountName_default_BlobContainer 'Microsoft.Storage/storageAcco } resource StorageAccountName_default_config 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { - name: '${StorageAccountName}/default/config' + name: '${storageAccountName}/default/config' properties: { publicAccess: 'None' } diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 1bd52c168..a411a5ce3 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -6,12 +6,12 @@ param allowedOrigins array = [] param appCommandLine string = '' param appServicePlanId string param applicationInsightsName string = '' -param StorageAccountName string +param storageAccountName string param keyVaultName string = '' -param AzureOpenAIName string = '' -param AzureCognitiveSearchName string = '' -param FormRecognizerName string = '' -param ContentSafetyName string = '' +param azureOpenAIName string = '' +param azureCognitiveSearchName string = '' +param formRecognizerName string = '' +param contentSafetyName string = '' @secure() param appSettings object = {} @@ -44,7 +44,7 @@ module web '../core/host/appservice.bicep' = { } resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { - name: StorageAccountName + name: storageAccountName } resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { @@ -52,19 +52,19 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing } resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { - name: AzureOpenAIName + name: azureOpenAIName } resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { - name: AzureCognitiveSearchName + name: azureCognitiveSearchName } resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { - name: FormRecognizerName + name: formRecognizerName } resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { - name: ContentSafetyName + name: contentSafetyName } output FRONTEND_API_IDENTITY_PRINCIPAL_ID string = web.outputs.identityPrincipalId diff --git a/infra/main.bicep b/infra/main.bicep index 092abbad7..bde6c983c 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -6,13 +6,13 @@ targetScope = 'subscription' param environmentName string @description('provide a 2-13 character prefix for all resources.') -param ResourcePrefix string = environmentName +param resourcePrefix string = environmentName @description('Location for all resources.') -param Location string +param location string @description('Name of App Service plan') -param HostingPlanName string = '${ResourcePrefix}-hosting-plan' +param hostingPlanName string = '${resourcePrefix}-hosting-plan' @description('The pricing tier for the App Service plan') @allowed([ @@ -29,91 +29,91 @@ param HostingPlanName string = '${ResourcePrefix}-hosting-plan' 'P3' 'P4' ]) -param HostingPlanSku string = 'B3' +param hostingPlanSku string = 'B3' @description('Name of Web App') -param WebsiteName string = '${ResourcePrefix}-website' +param websiteName string = '${resourcePrefix}-website' @description('Name of Application Insights') -param ApplicationInsightsName string = '${ResourcePrefix}-appinsights' +param applicationInsightsName string = '${resourcePrefix}-appinsights' @description('Use semantic search') -param AzureSearchUseSemanticSearch string = 'false' +param azureSearchUseSemanticSearch string = 'false' @description('Semantic search config') -param AzureSearchSemanticSearchConfig string = 'default' +param azureSearchSemanticSearchConfig string = 'default' @description('Is the index prechunked') -param AzureSearchIndexIsPrechunked string = 'false' +param azureSearchIndexIsPrechunked string = 'false' @description('Top K results') -param AzureSearchTopK string = '5' +param azureSearchTopK string = '5' @description('Enable in domain') -param AzureSearchEnableInDomain string = 'false' +param azureSearchEnableInDomain string = 'false' @description('Content columns') -param AzureSearchContentColumns string = 'content' +param azureSearchContentColumns string = 'content' @description('Filename column') -param AzureSearchFilenameColumn string = 'filename' +param azureSearchFilenameColumn string = 'filename' @description('Title column') -param AzureSearchTitleColumn string = 'title' +param azureSearchTitleColumn string = 'title' @description('Url column') -param AzureSearchUrlColumn string = 'url' +param azureSearchUrlColumn string = 'url' @description('Name of Azure OpenAI Resource') -param AzureOpenAIResource string = '${ResourcePrefix}-openai' +param azureOpenAIResource string = '${resourcePrefix}-openai' @description('Name of Azure OpenAI Resource SKU') -param AzureOpenAISkuName string = 'S0' +param azureOpenAISkuName string = 'S0' @description('Azure OpenAI Model Deployment Name') -param AzureOpenAIModel string = 'gpt-35-turbo' +param azureOpenAIModel string = 'gpt-35-turbo' @description('Azure OpenAI Model Name') -param AzureOpenAIModelName string = 'gpt-35-turbo' +param azureOpenAIModelName string = 'gpt-35-turbo' -param AzureOpenAIModelVersion string = '0613' +param azureOpenAIModelVersion string = '0613' @description('Orchestration strategy: openai_function or langchain str. If you use a old version of turbo (0301), plese select langchain') @allowed([ 'openai_function' 'langchain' ]) -param OrchestrationStrategy string = 'langchain' +param orchestrationStrategy string = 'langchain' @description('Azure OpenAI Temperature') -param AzureOpenAITemperature string = '0' +param azureOpenAITemperature string = '0' @description('Azure OpenAI Top P') -param AzureOpenAITopP string = '1' +param azureOpenAITopP string = '1' @description('Azure OpenAI Max Tokens') -param AzureOpenAIMaxTokens string = '1000' +param azureOpenAIMaxTokens string = '1000' @description('Azure OpenAI Stop Sequence') -param AzureOpenAIStopSequence string = '\n' +param azureOpenAIStopSequence string = '\n' @description('Azure OpenAI System Message') -param AzureOpenAISystemMessage string = 'You are an AI assistant that helps people find information.' +param azureOpenAISystemMessage string = 'You are an AI assistant that helps people find information.' @description('Azure OpenAI Api Version') -param AzureOpenAIApiVersion string = '2023-07-01-preview' +param azureOpenAIApiVersion string = '2023-07-01-preview' @description('Whether or not to stream responses from Azure OpenAI') -param AzureOpenAIStream string = 'true' +param azureOpenAIStream string = 'true' @description('Azure OpenAI Embedding Model Deployment Name') -param AzureOpenAIEmbeddingModel string = 'text-embedding-ada-002' +param azureOpenAIEmbeddingModel string = 'text-embedding-ada-002' @description('Azure OpenAI Embedding Model Name') -param AzureOpenAIEmbeddingModelName string = 'text-embedding-ada-002' +param azureOpenAIEmbeddingModelName string = 'text-embedding-ada-002' @description('Azure Cognitive Search Resource') -param AzureCognitiveSearch string = '${ResourcePrefix}-search' +param azureCognitiveSearch string = '${resourcePrefix}-search' @description('The SKU of the search service you want to create. E.g. free or standard') @allowed([ @@ -123,36 +123,36 @@ param AzureCognitiveSearch string = '${ResourcePrefix}-search' 'standard2' 'standard3' ]) -param AzureCognitiveSearchSku string = 'standard' +param azureCognitiveSearchSku string = 'standard' @description('Azure Cognitive Search Index') -param AzureSearchIndex string = '${ResourcePrefix}-index' +param azureSearchIndex string = '${resourcePrefix}-index' @description('Azure Cognitive Search Conversation Log Index') -param AzureSearchConversationLogIndex string = 'conversations' +param azureSearchConversationLogIndex string = 'conversations' @description('Name of Storage Account') -param StorageAccountName string = '${ResourcePrefix}str' +param storageAccountName string = '${resourcePrefix}str' @description('Name of Function App for Batch document processing') -param FunctionName string = '${ResourcePrefix}-backend' +param functionName string = '${resourcePrefix}-backend' @description('Azure Form Recognizer Name') -param FormRecognizerName string = '${ResourcePrefix}-formrecog' +param formRecognizerName string = '${resourcePrefix}-formrecog' @description('Azure Content Safety Name') -param ContentSafetyName string = '${ResourcePrefix}-contentsafety' +param contentSafetyName string = '${resourcePrefix}-contentsafety' param newGuidString string = newGuid() -var BlobContainerName = 'documents' -var QueueName = 'doc-processing' -var ClientKey = '${uniqueString(guid(subscription().id, deployment().name))}${newGuidString}' -var EventGridSystemTopicName = 'doc-processing' +var blobContainerName = 'documents' +var queueName = 'doc-processing' +var clientKey = '${uniqueString(guid(subscription().id, deployment().name))}${newGuidString}' +var eventGridSystemTopicName = 'doc-processing' var tags = { 'azd-env-name': environmentName } resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { - name: 'rg-${ResourcePrefix}' - location: Location + name: 'rg-${resourcePrefix}' + location: location tags: tags } @@ -160,19 +160,19 @@ module OpenAI 'core/ai/cognitiveservices.bicep' = { name: 'openai' scope: rg params: { - name: AzureOpenAIResource - location: Location + name: azureOpenAIResource + location: location tags: tags sku: { - name: AzureOpenAISkuName + name: azureOpenAISkuName } deployments: [ { - name: AzureOpenAIModel + name: azureOpenAIModel model: { format: 'OpenAI' - name: AzureOpenAIModelName - version: AzureOpenAIModelVersion + name: azureOpenAIModelName + version: azureOpenAIModelVersion } sku: { name: 'Standard' @@ -180,10 +180,10 @@ module OpenAI 'core/ai/cognitiveservices.bicep' = { } } { - name: AzureOpenAIEmbeddingModel + name: azureOpenAIEmbeddingModel model: { format: 'OpenAI' - name: AzureOpenAIEmbeddingModelName + name: azureOpenAIEmbeddingModelName version: '2' } capacity: 30 @@ -193,16 +193,16 @@ module OpenAI 'core/ai/cognitiveservices.bicep' = { } module AzureCognitiveSearch_resource './core/search/search-services.bicep' = { - name: AzureCognitiveSearch + name: azureCognitiveSearch scope: rg params:{ - name: AzureCognitiveSearch - location: Location + name: azureCognitiveSearch + location: location tags: { deployment : 'chatwithyourdata-sa' } sku: { - name: AzureCognitiveSearchSku + name: azureCognitiveSearchSku } } } @@ -211,24 +211,24 @@ module Other_Resources './app/resources.bicep' = { name: 'AllResources' scope: rg params: { - FormRecognizerName: FormRecognizerName - ContentSafetyName: ContentSafetyName - Location: Location - EventGridSystemTopicName: EventGridSystemTopicName - StorageAccountId: StorageAccount.outputs.StorageAccountId - QueueName: StorageAccount.outputs.StorageAccountName_default_doc_processing_name - BlobContainerName: BlobContainerName + formRecognizerName: formRecognizerName + contentSafetyName: contentSafetyName + location: location + eventGridSystemTopicName: eventGridSystemTopicName + storageAccountId: StorageAccount.outputs.StorageAccountId + queueName: StorageAccount.outputs.StorageAccountName_default_doc_processing_name + blobContainerName: blobContainerName } } module HostingPlan './core/host/appserviceplan.bicep' = { - name: HostingPlanName + name: hostingPlanName scope: rg params: { - name: HostingPlanName - location: Location + name: hostingPlanName + location: location sku: { - name: HostingPlanSku + name: hostingPlanSku } kind: 'linux' reserved: true @@ -236,47 +236,47 @@ module HostingPlan './core/host/appserviceplan.bicep' = { } module web './app/web.bicep' = { - name: WebsiteName + name: websiteName scope: rg params: { - name: WebsiteName - location: Location + name: websiteName + location: location tags: { 'azd-service-name': 'web' } - appServicePlanId: HostingPlanName - StorageAccountName: StorageAccountName + appServicePlanId: hostingPlanName + storageAccountName: storageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName - AzureOpenAIName: OpenAI.outputs.name - AzureCognitiveSearchName: AzureCognitiveSearch - FormRecognizerName: FormRecognizerName - ContentSafetyName: ContentSafetyName + azureOpenAIName: OpenAI.outputs.name + azureCognitiveSearchName: azureCognitiveSearch + formRecognizerName: formRecognizerName + contentSafetyName: contentSafetyName appSettings: { - AZURE_SEARCH_SERVICE: 'https://${AzureCognitiveSearch}.search.windows.net' - AZURE_SEARCH_INDEX: AzureSearchIndex - AZURE_SEARCH_CONVERSATIONS_LOG_INDEX: AzureSearchConversationLogIndex - AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: AzureSearchSemanticSearchConfig - AZURE_SEARCH_INDEX_IS_PRECHUNKED: AzureSearchIndexIsPrechunked - AZURE_SEARCH_TOP_K: AzureSearchTopK - AZURE_SEARCH_ENABLE_IN_DOMAIN: AzureSearchEnableInDomain - AZURE_SEARCH_CONTENT_COLUMNS: AzureSearchContentColumns - AZURE_SEARCH_FILENAME_COLUMN: AzureSearchFilenameColumn - AZURE_SEARCH_TITLE_COLUMN: AzureSearchTitleColumn - AZURE_SEARCH_URL_COLUMN: AzureSearchUrlColumn - AZURE_OPENAI_RESOURCE: AzureOpenAIResource - AZURE_OPENAI_MODEL: AzureOpenAIModel - AZURE_OPENAI_MODEL_NAME: AzureOpenAIModelName - AZURE_OPENAI_TEMPERATURE: AzureOpenAITemperature - AZURE_OPENAI_TOP_P: AzureOpenAITopP - AZURE_OPENAI_MAX_TOKENS: AzureOpenAIMaxTokens - AZURE_OPENAI_STOP_SEQUENCE: AzureOpenAIStopSequence - AZURE_OPENAI_SYSTEM_MESSAGE: AzureOpenAISystemMessage - AZURE_OPENAI_API_VERSION: AzureOpenAIApiVersion - AZURE_OPENAI_STREAM: AzureOpenAIStream - AZURE_OPENAI_EMBEDDING_MODEL: AzureOpenAIEmbeddingModel - AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' - AZURE_BLOB_ACCOUNT_NAME: StorageAccountName - AZURE_BLOB_CONTAINER_NAME: BlobContainerName - ORCHESTRATION_STRATEGY: OrchestrationStrategy - AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearch}.search.windows.net' + AZURE_SEARCH_INDEX: azureSearchIndex + AZURE_SEARCH_CONVERSATIONS_LOG_INDEX: azureSearchConversationLogIndex + AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: azureSearchSemanticSearchConfig + AZURE_SEARCH_INDEX_IS_PRECHUNKED: azureSearchIndexIsPrechunked + AZURE_SEARCH_TOP_K: azureSearchTopK + AZURE_SEARCH_ENABLE_IN_DOMAIN: azureSearchEnableInDomain + AZURE_SEARCH_CONTENT_COLUMNS: azureSearchContentColumns + AZURE_SEARCH_FILENAME_COLUMN: azureSearchFilenameColumn + AZURE_SEARCH_TITLE_COLUMN: azureSearchTitleColumn + AZURE_SEARCH_URL_COLUMN: azureSearchUrlColumn + AZURE_OPENAI_RESOURCE: azureOpenAIResource + AZURE_OPENAI_MODEL: azureOpenAIModel + AZURE_OPENAI_MODEL_NAME: azureOpenAIModelName + AZURE_OPENAI_TEMPERATURE: azureOpenAITemperature + AZURE_OPENAI_TOP_P: azureOpenAITopP + AZURE_OPENAI_MAX_TOKENS: azureOpenAIMaxTokens + AZURE_OPENAI_STOP_SEQUENCE: azureOpenAIStopSequence + AZURE_OPENAI_SYSTEM_MESSAGE: azureOpenAISystemMessage + AZURE_OPENAI_API_VERSION: azureOpenAIApiVersion + AZURE_OPENAI_STREAM: azureOpenAIStream + AZURE_OPENAI_EMBEDDING_MODEL: azureOpenAIEmbeddingModel + AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' + AZURE_BLOB_ACCOUNT_NAME: storageAccountName + AZURE_BLOB_CONTAINER_NAME: blobContainerName + ORCHESTRATION_STRATEGY: orchestrationStrategy + AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' } } dependsOn:[ @@ -288,50 +288,50 @@ module web './app/web.bicep' = { } module adminweb './app/adminweb.bicep' = { - name: '${WebsiteName}-admin' + name: '${websiteName}-admin' scope: rg params: { - name: '${WebsiteName}-admin' - location: Location + name: '${websiteName}-admin' + location: location tags: { 'azd-service-name': 'adminweb' } - appServicePlanId: HostingPlanName - StorageAccountName: StorageAccountName + appServicePlanId: hostingPlanName + storageAccountName: storageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName - AzureOpenAIName: OpenAI.outputs.name - AzureCognitiveSearchName: AzureCognitiveSearch - FormRecognizerName: FormRecognizerName - ContentSafetyName: ContentSafetyName + azureOpenAIName: OpenAI.outputs.name + azureCognitiveSearchName: azureCognitiveSearch + formRecognizerName: formRecognizerName + contentSafetyName: contentSafetyName appSettings: { - AZURE_SEARCH_SERVICE: 'https://${AzureCognitiveSearch}.search.windows.net' - AZURE_SEARCH_INDEX: AzureSearchIndex - AZURE_SEARCH_USE_SEMANTIC_SEARCH: AzureSearchUseSemanticSearch - AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: AzureSearchSemanticSearchConfig - AZURE_SEARCH_INDEX_IS_PRECHUNKED: AzureSearchIndexIsPrechunked - AZURE_SEARCH_TOP_K: AzureSearchTopK - AZURE_SEARCH_ENABLE_IN_DOMAIN: AzureSearchEnableInDomain - AZURE_SEARCH_CONTENT_COLUMNS: AzureSearchContentColumns - AZURE_SEARCH_FILENAME_COLUMN: AzureSearchFilenameColumn - AZURE_SEARCH_TITLE_COLUMN: AzureSearchTitleColumn - AZURE_SEARCH_URL_COLUMN: AzureSearchUrlColumn - AZURE_OPENAI_RESOURCE: AzureOpenAIResource - AZURE_OPENAI_MODEL: AzureOpenAIModel - AZURE_OPENAI_MODEL_NAME: AzureOpenAIModelName - AZURE_OPENAI_TEMPERATURE: AzureOpenAITemperature - AZURE_OPENAI_TOP_P: AzureOpenAITopP - AZURE_OPENAI_MAX_TOKENS: AzureOpenAIMaxTokens - AZURE_OPENAI_STOP_SEQUENCE: AzureOpenAIStopSequence - AZURE_OPENAI_SYSTEM_MESSAGE: AzureOpenAISystemMessage - AZURE_OPENAI_API_VERSION: AzureOpenAIApiVersion - AZURE_OPENAI_STREAM: AzureOpenAIStream - AZURE_OPENAI_EMBEDDING_MODEL: AzureOpenAIEmbeddingModel - AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' - AZURE_BLOB_ACCOUNT_NAME: StorageAccountName - AZURE_BLOB_CONTAINER_NAME: BlobContainerName - DOCUMENT_PROCESSING_QUEUE_NAME: QueueName - BACKEND_URL: 'https://${FunctionName}.azurewebsites.net' - FUNCTION_KEY: ClientKey - ORCHESTRATION_STRATEGY: OrchestrationStrategy - AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearch}.search.windows.net' + AZURE_SEARCH_INDEX: azureSearchIndex + AZURE_SEARCH_USE_SEMANTIC_SEARCH: azureSearchUseSemanticSearch + AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: azureSearchSemanticSearchConfig + AZURE_SEARCH_INDEX_IS_PRECHUNKED: azureSearchIndexIsPrechunked + AZURE_SEARCH_TOP_K: azureSearchTopK + AZURE_SEARCH_ENABLE_IN_DOMAIN: azureSearchEnableInDomain + AZURE_SEARCH_CONTENT_COLUMNS: azureSearchContentColumns + AZURE_SEARCH_FILENAME_COLUMN: azureSearchFilenameColumn + AZURE_SEARCH_TITLE_COLUMN: azureSearchTitleColumn + AZURE_SEARCH_URL_COLUMN: azureSearchUrlColumn + AZURE_OPENAI_RESOURCE: azureOpenAIResource + AZURE_OPENAI_MODEL: azureOpenAIModel + AZURE_OPENAI_MODEL_NAME: azureOpenAIModelName + AZURE_OPENAI_TEMPERATURE: azureOpenAITemperature + AZURE_OPENAI_TOP_P: azureOpenAITopP + AZURE_OPENAI_MAX_TOKENS: azureOpenAIMaxTokens + AZURE_OPENAI_STOP_SEQUENCE: azureOpenAIStopSequence + AZURE_OPENAI_SYSTEM_MESSAGE: azureOpenAISystemMessage + AZURE_OPENAI_API_VERSION: azureOpenAIApiVersion + AZURE_OPENAI_STREAM: azureOpenAIStream + AZURE_OPENAI_EMBEDDING_MODEL: azureOpenAIEmbeddingModel + AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' + AZURE_BLOB_ACCOUNT_NAME: storageAccountName + AZURE_BLOB_CONTAINER_NAME: blobContainerName + DOCUMENT_PROCESSING_QUEUE_NAME: queueName + BACKEND_URL: 'https://${functionName}.azurewebsites.net' + FUNCTION_KEY: clientKey + ORCHESTRATION_STRATEGY: orchestrationStrategy + AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' } } dependsOn:[ @@ -346,9 +346,9 @@ module StorageAccount './app/storage.bicep' = { name: 'Storage_Account' scope: rg params: { - StorageAccountName: StorageAccountName - Location: Location - BlobContainerName: BlobContainerName + storageAccountName: storageAccountName + location: location + blobContainerName: blobContainerName } } @@ -356,48 +356,48 @@ module monitoring './core/monitor/monitoring.bicep' = { name: 'monitoring' scope: rg params: { - applicationInsightsName: ApplicationInsightsName - location: Location + applicationInsightsName: applicationInsightsName + location: location tags: { - 'hidden-link:${resourceId('Microsoft.Web/sites', ApplicationInsightsName)}': 'Resource' + 'hidden-link:${resourceId('Microsoft.Web/sites', applicationInsightsName)}': 'Resource' } - logAnalyticsName: '${ResourcePrefix}-logAnalytics' - applicationInsightsDashboardName: 'dash-${ApplicationInsightsName}' + logAnalyticsName: '${resourcePrefix}-logAnalytics' + applicationInsightsDashboardName: 'dash-${applicationInsightsName}' } } module function './app/function.bicep' = { - name: FunctionName + name: functionName scope: rg params:{ - name: FunctionName - location: Location + name: functionName + location: location tags: { 'azd-service-name': 'function' } - appServicePlanId: HostingPlanName - storageAccountName: StorageAccountName + appServicePlanId: hostingPlanName + storageAccountName: storageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName - AzureOpenAIName: OpenAI.outputs.name - AzureCognitiveSearchName: AzureCognitiveSearch - FormRecognizerName: FormRecognizerName - ContentSafetyName: ContentSafetyName + azureOpenAIName: OpenAI.outputs.name + azureCognitiveSearchName: azureCognitiveSearch + formRecognizerName: formRecognizerName + contentSafetyName: contentSafetyName runtimeName:'python' runtimeVersion:'3.11' - ClientKey: ClientKey + clientKey: clientKey appSettings: { FUNCTIONS_EXTENSION_VERSION: '~4' WEBSITES_ENABLE_APP_SERVICE_STORAGE: 'false' - AZURE_OPENAI_MODEL: AzureOpenAIModel - AZURE_OPENAI_EMBEDDING_MODEL: AzureOpenAIEmbeddingModel - AZURE_OPENAI_RESOURCE: AzureOpenAIResource - AZURE_BLOB_ACCOUNT_NAME: StorageAccountName - AZURE_BLOB_CONTAINER_NAME: BlobContainerName - AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' - AZURE_SEARCH_SERVICE: 'https://${AzureCognitiveSearch}.search.windows.net' - DOCUMENT_PROCESSING_QUEUE_NAME: QueueName - AZURE_OPENAI_API_VERSION: AzureOpenAIApiVersion - AZURE_SEARCH_INDEX: AzureSearchIndex - ORCHESTRATION_STRATEGY: OrchestrationStrategy - AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${Location}.api.cognitive.microsoft.com/' + AZURE_OPENAI_MODEL: azureOpenAIModel + AZURE_OPENAI_EMBEDDING_MODEL: azureOpenAIEmbeddingModel + AZURE_OPENAI_RESOURCE: azureOpenAIResource + AZURE_BLOB_ACCOUNT_NAME: storageAccountName + AZURE_BLOB_CONTAINER_NAME: blobContainerName + AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' + AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearch}.search.windows.net' + DOCUMENT_PROCESSING_QUEUE_NAME: queueName + AZURE_OPENAI_API_VERSION: azureOpenAIApiVersion + AZURE_SEARCH_INDEX: azureSearchIndex + ORCHESTRATION_STRATEGY: orchestrationStrategy + AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' } } dependsOn:[ diff --git a/infra/main.bicepparam b/infra/main.bicepparam index f9bfb364a..96a8c2486 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -2,4 +2,4 @@ using './main.bicep' param environmentName = readEnvironmentVariable('AZURE_ENV_NAME') -param Location = readEnvironmentVariable('AZURE_LOCATION') +param location = readEnvironmentVariable('AZURE_LOCATION') From c00f55a8f5f9c9b4f1aa6318fe493ef3a31cf995 Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 8 Nov 2023 00:21:52 +0800 Subject: [PATCH 05/37] move appsettings --- infra/app/adminweb.bicep | 42 ++++++++++++------------ infra/app/function.bicep | 50 ++++++++++++++-------------- infra/app/resources.bicep | 13 +++++--- infra/app/storage.bicep | 27 ++++++++-------- infra/app/web.bicep | 42 ++++++++++++------------ infra/main.bicep | 68 +++++++++++++++++++++++---------------- 6 files changed, 129 insertions(+), 113 deletions(-) diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 3d33e3646..db95ab56c 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -6,12 +6,12 @@ param allowedOrigins array = [] param appServicePlanId string param appCommandLine string = 'python -m streamlit run Admin.py --server.port 8000 --server.address 0.0.0.0 --server.enableXsrfProtection false' param applicationInsightsName string = '' -param storageAccountName string +// param storageAccountName string param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' -param formRecognizerName string = '' -param contentSafetyName string = '' +// param formRecognizerName string = '' +// param contentSafetyName string = '' @secure() param appSettings object = {} @@ -28,12 +28,12 @@ module adminweb '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value - APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey - AZURE_OPENAI_KEY: openai.listKeys().key1 + // AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value + // APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey + AZURE_OPENAI_KEY: openAI.listKeys().key1 AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey - AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 - AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 + // AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 + // AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 }) keyVaultName: keyVaultName runtimeName: 'python' @@ -42,15 +42,15 @@ module adminweb '../core/host/appservice.bicep' = { } } -resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { - name: storageAccountName -} +// resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { +// name: storageAccountName +// } -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { - name: applicationInsightsName -} +// resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { +// name: applicationInsightsName +// } -resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { +resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } @@ -58,13 +58,13 @@ resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = name: azureCognitiveSearchName } -resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { - name: formRecognizerName -} +// resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { +// name: formRecognizerName +// } -resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { - name: contentSafetyName -} +// resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { +// name: contentSafetyName +// } output WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID string = adminweb.outputs.identityPrincipalId output WEBSITE_ADMIN_NAME string = adminweb.outputs.name diff --git a/infra/app/function.bicep b/infra/app/function.bicep index d0771fb1f..bf0aacc3c 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -7,14 +7,14 @@ param tags object = {} @secure() param appSettings object = {} param serviceName string = 'function' -param applicationInsightsName string = '' +// param applicationInsightsName string = '' param runtimeName string param runtimeVersion string param clientKey string param azureOpenAIName string = '' param azureCognitiveSearchName string = '' -param formRecognizerName string = '' -param contentSafetyName string = '' +// param formRecognizerName string = '' +// param contentSafetyName string = '' module function '../core/host/functions.bicep' = { @@ -28,26 +28,26 @@ module function '../core/host/functions.bicep' = { runtimeName: runtimeName runtimeVersion: runtimeVersion appSettings: union(appSettings, { - AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' - AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value - APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey - AZURE_OPENAI_KEY: openai.listKeys().key1 + // AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' + // AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value + // APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey + AZURE_OPENAI_KEY: openAI.listKeys().key1 AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey - AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 - AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 + // AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 + // AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 }) } } -resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { - name: storageAccountName -} +// resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { +// name: storageAccountName +// } -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { - name: applicationInsightsName -} +// resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { +// name: applicationInsightsName +// } -resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { +resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } @@ -55,15 +55,15 @@ resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = name: azureCognitiveSearchName } -resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { - name: formRecognizerName -} +// resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { +// name: formRecognizerName +// } -resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { - name: contentSafetyName -} +// resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { +// name: contentSafetyName +// } -resource FunctionName_default_clientKey 'Microsoft.Web/sites/host/functionKeys@2018-11-01' = { +resource functionNameDefaultClientKey 'Microsoft.Web/sites/host/functionKeys@2018-11-01' = { name: '${name}/default/clientKey' properties: { name: 'ClientKey' @@ -71,11 +71,11 @@ resource FunctionName_default_clientKey 'Microsoft.Web/sites/host/functionKeys@2 } dependsOn: [ function - WaitFunctionDeploymentSection + waitFunctionDeploymentSection ] } -resource WaitFunctionDeploymentSection 'Microsoft.Resources/deploymentScripts@2020-10-01' = { +resource waitFunctionDeploymentSection 'Microsoft.Resources/deploymentScripts@2020-10-01' = { kind: 'AzurePowerShell' name: 'WaitFunctionDeploymentSection' location: location diff --git a/infra/app/resources.bicep b/infra/app/resources.bicep index cd909bafe..a50ce7449 100644 --- a/infra/app/resources.bicep +++ b/infra/app/resources.bicep @@ -6,7 +6,7 @@ param storageAccountId string param queueName string param blobContainerName string -resource FormRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { +resource formRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { name: formRecognizerName location: location sku: { @@ -26,7 +26,7 @@ resource FormRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { } } -resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { +resource contentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { name: contentSafetyName location: location sku: { @@ -45,7 +45,7 @@ resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { } } -resource EventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { +resource eventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { name: eventGridSystemTopicName location: location properties: { @@ -54,8 +54,8 @@ resource EventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { } } -resource EventGridSystemTopicName_BlobEvents 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2021-12-01' = { - parent: EventGridSystemTopic +resource eventGridSystemTopicNameBlobEvents 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2021-12-01' = { + parent: eventGridSystemTopic name: 'BlobEvents' properties: { destination: { @@ -82,3 +82,6 @@ resource EventGridSystemTopicName_BlobEvents 'Microsoft.EventGrid/systemTopics/e } } } + +output AZURE_FORM_RECOGNIZER_KEY string = formRecognizer.listKeys().key1 +output AZURE_CONTENT_SAFETY_KEY string = contentSafety.listKeys().key1 diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep index 7828364e9..339d3b626 100644 --- a/infra/app/storage.bicep +++ b/infra/app/storage.bicep @@ -2,7 +2,7 @@ param storageAccountName string = 'Enabled' param location string = '' param blobContainerName string = 'Enabled' -resource StorageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { name: storageAccountName location: location kind: 'StorageV2' @@ -11,28 +11,28 @@ resource StorageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { } } -resource StorageAccountName_default_BlobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { +resource storageAccountNameDefaultBlobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { name: '${storageAccountName}/default/${blobContainerName}' properties: { publicAccess: 'None' } dependsOn: [ - StorageAccount + storageAccount ] } -resource StorageAccountName_default_config 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { +resource storageAccountNameDefaultConfig 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { name: '${storageAccountName}/default/config' properties: { publicAccess: 'None' } dependsOn: [ - StorageAccount + storageAccount ] } -resource StorageAccountName_default 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { - parent: StorageAccount +resource storageAccountNameDefault 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { + parent: storageAccount name: 'default' properties: { cors: { @@ -41,8 +41,8 @@ resource StorageAccountName_default 'Microsoft.Storage/storageAccounts/queueServ } } -resource StorageAccountName_default_doc_processing 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { - parent: StorageAccountName_default +resource storageAccountNameDefaultDocProcessing 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { + parent: storageAccountNameDefault name: 'doc-processing' properties: { metadata: {} @@ -50,8 +50,8 @@ resource StorageAccountName_default_doc_processing 'Microsoft.Storage/storageAcc dependsOn: [] } -resource StorageAccountName_default_doc_processing_poison 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { - parent: StorageAccountName_default +resource storageAccountNameDefaultDocProcessingPoison 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { + parent: storageAccountNameDefault name: 'doc-processing-poison' properties: { metadata: {} @@ -59,5 +59,6 @@ resource StorageAccountName_default_doc_processing_poison 'Microsoft.Storage/sto dependsOn: [] } -output StorageAccountId string = StorageAccount.id -output StorageAccountName_default_doc_processing_name string = StorageAccountName_default_doc_processing.name +output StorageAccountId string = storageAccount.id +output StorageAccountName_default_doc_processing_name string = storageAccountNameDefaultDocProcessing.name +output AZURE_BLOB_ACCOUNT_KEY string = storageAccount.listKeys().keys[0].value diff --git a/infra/app/web.bicep b/infra/app/web.bicep index a411a5ce3..a126bd5d3 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -6,12 +6,12 @@ param allowedOrigins array = [] param appCommandLine string = '' param appServicePlanId string param applicationInsightsName string = '' -param storageAccountName string +// param storageAccountName string param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' -param formRecognizerName string = '' -param contentSafetyName string = '' +// param formRecognizerName string = '' +// param contentSafetyName string = '' @secure() param appSettings object = {} @@ -28,12 +28,12 @@ module web '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value - APPINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString - AZURE_OPENAI_KEY: openai.listKeys().key1 + // AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value + // APPINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString + AZURE_OPENAI_KEY: openAI.listKeys().key1 AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey - AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 - AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 + // AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 + // AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 }) keyVaultName: keyVaultName @@ -43,15 +43,15 @@ module web '../core/host/appservice.bicep' = { } } -resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { - name: storageAccountName -} +// resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { +// name: storageAccountName +// } -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { - name: applicationInsightsName -} +// resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { +// name: applicationInsightsName +// } -resource openai 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { +resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } @@ -59,13 +59,13 @@ resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = name: azureCognitiveSearchName } -resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { - name: formRecognizerName -} +// resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { +// name: formRecognizerName +// } -resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { - name: contentSafetyName -} +// resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { +// name: contentSafetyName +// } output FRONTEND_API_IDENTITY_PRINCIPAL_ID string = web.outputs.identityPrincipalId output FRONTEND_API_NAME string = web.outputs.name diff --git a/infra/main.bicep b/infra/main.bicep index bde6c983c..a3e69774b 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -156,7 +156,7 @@ resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { tags: tags } -module OpenAI 'core/ai/cognitiveservices.bicep' = { +module openai 'core/ai/cognitiveservices.bicep' = { name: 'openai' scope: rg params: { @@ -192,7 +192,7 @@ module OpenAI 'core/ai/cognitiveservices.bicep' = { } } -module AzureCognitiveSearch_resource './core/search/search-services.bicep' = { +module search './core/search/search-services.bicep' = { name: azureCognitiveSearch scope: rg params:{ @@ -207,7 +207,7 @@ module AzureCognitiveSearch_resource './core/search/search-services.bicep' = { } } -module Other_Resources './app/resources.bicep' = { +module resources './app/resources.bicep' = { name: 'AllResources' scope: rg params: { @@ -215,13 +215,13 @@ module Other_Resources './app/resources.bicep' = { contentSafetyName: contentSafetyName location: location eventGridSystemTopicName: eventGridSystemTopicName - storageAccountId: StorageAccount.outputs.StorageAccountId - queueName: StorageAccount.outputs.StorageAccountName_default_doc_processing_name + storageAccountId: storage.outputs.StorageAccountId + queueName: storage.outputs.StorageAccountName_default_doc_processing_name blobContainerName: blobContainerName } } -module HostingPlan './core/host/appserviceplan.bicep' = { +module hostingplan './core/host/appserviceplan.bicep' = { name: hostingPlanName scope: rg params: { @@ -243,12 +243,12 @@ module web './app/web.bicep' = { location: location tags: { 'azd-service-name': 'web' } appServicePlanId: hostingPlanName - storageAccountName: storageAccountName + // storageAccountName: storageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName - azureOpenAIName: OpenAI.outputs.name + azureOpenAIName: openai.outputs.name azureCognitiveSearchName: azureCognitiveSearch - formRecognizerName: formRecognizerName - contentSafetyName: contentSafetyName + // formRecognizerName: formRecognizerName + // contentSafetyName: contentSafetyName appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearch}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -277,13 +277,17 @@ module web './app/web.bicep' = { AZURE_BLOB_CONTAINER_NAME: blobContainerName ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' + AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY + APPINSIGHTS_CONNECTION_STRING: monitoring.outputs.applicationInsightsConnectionString + AZURE_FORM_RECOGNIZER_KEY: resources.outputs.AZURE_FORM_RECOGNIZER_KEY + AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY } } dependsOn:[ - HostingPlan - StorageAccount + hostingplan + storage monitoring - OpenAI + openai ] } @@ -295,12 +299,12 @@ module adminweb './app/adminweb.bicep' = { location: location tags: { 'azd-service-name': 'adminweb' } appServicePlanId: hostingPlanName - storageAccountName: storageAccountName + // storageAccountName: storageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName - azureOpenAIName: OpenAI.outputs.name + azureOpenAIName: openai.outputs.name azureCognitiveSearchName: azureCognitiveSearch - formRecognizerName: formRecognizerName - contentSafetyName: contentSafetyName + // formRecognizerName: formRecognizerName + // contentSafetyName: contentSafetyName appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearch}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -332,17 +336,21 @@ module adminweb './app/adminweb.bicep' = { FUNCTION_KEY: clientKey ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' + AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY + APPINSIGHTS_INSTRUMENTATIONKEY: monitoring.outputs.applicationInsightsInstrumentationKey + AZURE_FORM_RECOGNIZER_KEY: resources.outputs.AZURE_FORM_RECOGNIZER_KEY + AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY } } dependsOn:[ - HostingPlan - StorageAccount + hostingplan + storage monitoring - OpenAI + openai ] } -module StorageAccount './app/storage.bicep' = { +module storage './app/storage.bicep' = { name: 'Storage_Account' scope: rg params: { @@ -375,11 +383,11 @@ module function './app/function.bicep' = { tags: { 'azd-service-name': 'function' } appServicePlanId: hostingPlanName storageAccountName: storageAccountName - applicationInsightsName: monitoring.outputs.applicationInsightsName - azureOpenAIName: OpenAI.outputs.name + // applicationInsightsName: monitoring.outputs.applicationInsightsName + azureOpenAIName: openai.outputs.name azureCognitiveSearchName: azureCognitiveSearch - formRecognizerName: formRecognizerName - contentSafetyName: contentSafetyName + // formRecognizerName: formRecognizerName + // contentSafetyName: contentSafetyName runtimeName:'python' runtimeVersion:'3.11' clientKey: clientKey @@ -398,12 +406,16 @@ module function './app/function.bicep' = { AZURE_SEARCH_INDEX: azureSearchIndex ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' + AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY + APPINSIGHTS_INSTRUMENTATIONKEY: monitoring.outputs.applicationInsightsInstrumentationKey + AZURE_FORM_RECOGNIZER_KEY: resources.outputs.AZURE_FORM_RECOGNIZER_KEY + AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY } } dependsOn:[ - StorageAccount - HostingPlan + storage + hostingplan monitoring - OpenAI + openai ] } From b2e62b2cfe2041f02494836ae39b08b0b33434b4 Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 9 Nov 2023 10:14:23 +0800 Subject: [PATCH 06/37] remove some invalid changes --- .azure/config.json | 1 + .azure/zed12/config.json | 1 + infra/app/adminweb.bicep | 23 ----------------------- infra/app/function.bicep | 26 ++------------------------ infra/app/storage.bicep | 4 ++-- infra/app/web.bicep | 23 ----------------------- infra/main.bicep | 34 +++++++++++++--------------------- 7 files changed, 19 insertions(+), 93 deletions(-) create mode 100644 .azure/config.json create mode 100644 .azure/zed12/config.json diff --git a/.azure/config.json b/.azure/config.json new file mode 100644 index 000000000..8c55a9a97 --- /dev/null +++ b/.azure/config.json @@ -0,0 +1 @@ +{"version":1,"defaultEnvironment":"zed12"} \ No newline at end of file diff --git a/.azure/zed12/config.json b/.azure/zed12/config.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/.azure/zed12/config.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index db95ab56c..4b80a4887 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -6,12 +6,9 @@ param allowedOrigins array = [] param appServicePlanId string param appCommandLine string = 'python -m streamlit run Admin.py --server.port 8000 --server.address 0.0.0.0 --server.enableXsrfProtection false' param applicationInsightsName string = '' -// param storageAccountName string param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' -// param formRecognizerName string = '' -// param contentSafetyName string = '' @secure() param appSettings object = {} @@ -28,12 +25,8 @@ module adminweb '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - // AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value - // APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey AZURE_OPENAI_KEY: openAI.listKeys().key1 AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey - // AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 - // AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 }) keyVaultName: keyVaultName runtimeName: 'python' @@ -42,14 +35,6 @@ module adminweb '../core/host/appservice.bicep' = { } } -// resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { -// name: storageAccountName -// } - -// resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { -// name: applicationInsightsName -// } - resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } @@ -58,14 +43,6 @@ resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = name: azureCognitiveSearchName } -// resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { -// name: formRecognizerName -// } - -// resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { -// name: contentSafetyName -// } - output WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID string = adminweb.outputs.identityPrincipalId output WEBSITE_ADMIN_NAME string = adminweb.outputs.name output WEBSITE_ADMIN_URI string = adminweb.outputs.uri diff --git a/infra/app/function.bicep b/infra/app/function.bicep index bf0aacc3c..ed26ba665 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -7,14 +7,13 @@ param tags object = {} @secure() param appSettings object = {} param serviceName string = 'function' -// param applicationInsightsName string = '' param runtimeName string param runtimeVersion string + +@secure() param clientKey string param azureOpenAIName string = '' param azureCognitiveSearchName string = '' -// param formRecognizerName string = '' -// param contentSafetyName string = '' module function '../core/host/functions.bicep' = { @@ -28,25 +27,12 @@ module function '../core/host/functions.bicep' = { runtimeName: runtimeName runtimeVersion: runtimeVersion appSettings: union(appSettings, { - // AzureWebJobsStorage: 'DefaultEndpointsProtocol=https;AccountName=${storage.name};AccountKey=${storage.listKeys().keys[0].value};EndpointSuffix=${environment().suffixes.storage}' - // AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value - // APPINSIGHTS_INSTRUMENTATIONKEY: applicationInsights.properties.InstrumentationKey AZURE_OPENAI_KEY: openAI.listKeys().key1 AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey - // AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 - // AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 }) } } -// resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { -// name: storageAccountName -// } - -// resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { -// name: applicationInsightsName -// } - resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } @@ -55,14 +41,6 @@ resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = name: azureCognitiveSearchName } -// resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { -// name: formRecognizerName -// } - -// resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { -// name: contentSafetyName -// } - resource functionNameDefaultClientKey 'Microsoft.Web/sites/host/functionKeys@2018-11-01' = { name: '${name}/default/clientKey' properties: { diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep index 339d3b626..43d521c01 100644 --- a/infra/app/storage.bicep +++ b/infra/app/storage.bicep @@ -1,6 +1,6 @@ -param storageAccountName string = 'Enabled' +param storageAccountName string = '' param location string = '' -param blobContainerName string = 'Enabled' +param blobContainerName string = '' resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { name: storageAccountName diff --git a/infra/app/web.bicep b/infra/app/web.bicep index a126bd5d3..e74ae34cc 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -6,12 +6,9 @@ param allowedOrigins array = [] param appCommandLine string = '' param appServicePlanId string param applicationInsightsName string = '' -// param storageAccountName string param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' -// param formRecognizerName string = '' -// param contentSafetyName string = '' @secure() param appSettings object = {} @@ -28,12 +25,8 @@ module web '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - // AZURE_BLOB_ACCOUNT_KEY: storage.listKeys().keys[0].value - // APPINSIGHTS_CONNECTION_STRING: applicationInsights.properties.ConnectionString AZURE_OPENAI_KEY: openAI.listKeys().key1 AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey - // AZURE_FORM_RECOGNIZER_KEY: formrecognizer.listKeys().key1 - // AZURE_CONTENT_SAFETY_KEY: ContentSafety.listKeys().key1 }) keyVaultName: keyVaultName @@ -43,14 +36,6 @@ module web '../core/host/appservice.bicep' = { } } -// resource storage 'Microsoft.Storage/storageAccounts@2021-09-01' existing = { -// name: storageAccountName -// } - -// resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { -// name: applicationInsightsName -// } - resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } @@ -59,14 +44,6 @@ resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = name: azureCognitiveSearchName } -// resource formrecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' existing = { -// name: formRecognizerName -// } - -// resource ContentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' existing = { -// name: contentSafetyName -// } - output FRONTEND_API_IDENTITY_PRINCIPAL_ID string = web.outputs.identityPrincipalId output FRONTEND_API_NAME string = web.outputs.name output FRONTEND_API_URI string = web.outputs.uri diff --git a/infra/main.bicep b/infra/main.bicep index a3e69774b..d53b8456e 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -113,7 +113,7 @@ param azureOpenAIEmbeddingModel string = 'text-embedding-ada-002' param azureOpenAIEmbeddingModelName string = 'text-embedding-ada-002' @description('Azure Cognitive Search Resource') -param azureCognitiveSearch string = '${resourcePrefix}-search' +param azureCognitiveSearchName string = '${resourcePrefix}-search' @description('The SKU of the search service you want to create. E.g. free or standard') @allowed([ @@ -143,6 +143,7 @@ param formRecognizerName string = '${resourcePrefix}-formrecog' @description('Azure Content Safety Name') param contentSafetyName string = '${resourcePrefix}-contentsafety' param newGuidString string = newGuid() +param searchTag string = 'chatwithyourdata-sa' var blobContainerName = 'documents' var queueName = 'doc-processing' @@ -193,13 +194,13 @@ module openai 'core/ai/cognitiveservices.bicep' = { } module search './core/search/search-services.bicep' = { - name: azureCognitiveSearch + name: azureCognitiveSearchName scope: rg params:{ - name: azureCognitiveSearch + name: azureCognitiveSearchName location: location - tags: { - deployment : 'chatwithyourdata-sa' + tags: { + deployment : searchTag } sku: { name: azureCognitiveSearchSku @@ -208,7 +209,7 @@ module search './core/search/search-services.bicep' = { } module resources './app/resources.bicep' = { - name: 'AllResources' + name: 'resource' scope: rg params: { formRecognizerName: formRecognizerName @@ -243,14 +244,11 @@ module web './app/web.bicep' = { location: location tags: { 'azd-service-name': 'web' } appServicePlanId: hostingPlanName - // storageAccountName: storageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name - azureCognitiveSearchName: azureCognitiveSearch - // formRecognizerName: formRecognizerName - // contentSafetyName: contentSafetyName + azureCognitiveSearchName: azureCognitiveSearchName appSettings: { - AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearch}.search.windows.net' + AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex AZURE_SEARCH_CONVERSATIONS_LOG_INDEX: azureSearchConversationLogIndex AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: azureSearchSemanticSearchConfig @@ -299,14 +297,11 @@ module adminweb './app/adminweb.bicep' = { location: location tags: { 'azd-service-name': 'adminweb' } appServicePlanId: hostingPlanName - // storageAccountName: storageAccountName applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name - azureCognitiveSearchName: azureCognitiveSearch - // formRecognizerName: formRecognizerName - // contentSafetyName: contentSafetyName + azureCognitiveSearchName: azureCognitiveSearchName appSettings: { - AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearch}.search.windows.net' + AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex AZURE_SEARCH_USE_SEMANTIC_SEARCH: azureSearchUseSemanticSearch AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG: azureSearchSemanticSearchConfig @@ -383,11 +378,8 @@ module function './app/function.bicep' = { tags: { 'azd-service-name': 'function' } appServicePlanId: hostingPlanName storageAccountName: storageAccountName - // applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name - azureCognitiveSearchName: azureCognitiveSearch - // formRecognizerName: formRecognizerName - // contentSafetyName: contentSafetyName + azureCognitiveSearchName: azureCognitiveSearchName runtimeName:'python' runtimeVersion:'3.11' clientKey: clientKey @@ -400,7 +392,7 @@ module function './app/function.bicep' = { AZURE_BLOB_ACCOUNT_NAME: storageAccountName AZURE_BLOB_CONTAINER_NAME: blobContainerName AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' - AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearch}.search.windows.net' + AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' DOCUMENT_PROCESSING_QUEUE_NAME: queueName AZURE_OPENAI_API_VERSION: azureOpenAIApiVersion AZURE_SEARCH_INDEX: azureSearchIndex From b1a97b7b43f9087c55e4372a7db3a9deb366e483 Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 9 Nov 2023 12:43:24 +0800 Subject: [PATCH 07/37] fix format issues --- .azure/config.json | 1 - .azure/zed12/config.json | 1 - infra/app/function.bicep | 4 ++-- infra/app/storage.bicep | 4 ++-- infra/main.bicep | 35 ++++++++++++++++------------------- 5 files changed, 20 insertions(+), 25 deletions(-) delete mode 100644 .azure/config.json delete mode 100644 .azure/zed12/config.json diff --git a/.azure/config.json b/.azure/config.json deleted file mode 100644 index 8c55a9a97..000000000 --- a/.azure/config.json +++ /dev/null @@ -1 +0,0 @@ -{"version":1,"defaultEnvironment":"zed12"} \ No newline at end of file diff --git a/.azure/zed12/config.json b/.azure/zed12/config.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/.azure/zed12/config.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/infra/app/function.bicep b/infra/app/function.bicep index ed26ba665..d220f2b99 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -7,8 +7,8 @@ param tags object = {} @secure() param appSettings object = {} param serviceName string = 'function' -param runtimeName string -param runtimeVersion string +param runtimeName string = 'python' +param runtimeVersion string = '3.11' @secure() param clientKey string diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep index 43d521c01..30b08ee53 100644 --- a/infra/app/storage.bicep +++ b/infra/app/storage.bicep @@ -59,6 +59,6 @@ resource storageAccountNameDefaultDocProcessingPoison 'Microsoft.Storage/storage dependsOn: [] } -output StorageAccountId string = storageAccount.id -output StorageAccountName_default_doc_processing_name string = storageAccountNameDefaultDocProcessing.name +output STORAGE_ACCOUNT_ID string = storageAccount.id +output STORAGE_ACCOUNT_NAME_DEFAULT_DOC_PROCESSING_NAME string = storageAccountNameDefaultDocProcessing.name output AZURE_BLOB_ACCOUNT_KEY string = storageAccount.listKeys().keys[0].value diff --git a/infra/main.bicep b/infra/main.bicep index d53b8456e..beaa7968e 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -5,14 +5,13 @@ targetScope = 'subscription' @description('Name of the the environment which is used to generate a short unique hash used in all resources.') param environmentName string -@description('provide a 2-13 character prefix for all resources.') -param resourcePrefix string = environmentName +param resourceToken string = toLower(uniqueString(subscription().id, environmentName, location)) @description('Location for all resources.') param location string @description('Name of App Service plan') -param hostingPlanName string = '${resourcePrefix}-hosting-plan' +param hostingPlanName string = '${environmentName}-hosting-plan-${resourceToken}' @description('The pricing tier for the App Service plan') @allowed([ @@ -32,10 +31,10 @@ param hostingPlanName string = '${resourcePrefix}-hosting-plan' param hostingPlanSku string = 'B3' @description('Name of Web App') -param websiteName string = '${resourcePrefix}-website' +param websiteName string = '${environmentName}-website-${resourceToken}' @description('Name of Application Insights') -param applicationInsightsName string = '${resourcePrefix}-appinsights' +param applicationInsightsName string = '${environmentName}-appinsights-${resourceToken}' @description('Use semantic search') param azureSearchUseSemanticSearch string = 'false' @@ -65,7 +64,7 @@ param azureSearchTitleColumn string = 'title' param azureSearchUrlColumn string = 'url' @description('Name of Azure OpenAI Resource') -param azureOpenAIResource string = '${resourcePrefix}-openai' +param azureOpenAIResource string = '${environmentName}-openai-${resourceToken}' @description('Name of Azure OpenAI Resource SKU') param azureOpenAISkuName string = 'S0' @@ -113,7 +112,7 @@ param azureOpenAIEmbeddingModel string = 'text-embedding-ada-002' param azureOpenAIEmbeddingModelName string = 'text-embedding-ada-002' @description('Azure Cognitive Search Resource') -param azureCognitiveSearchName string = '${resourcePrefix}-search' +param azureCognitiveSearchName string = '${environmentName}-search-${resourceToken}' @description('The SKU of the search service you want to create. E.g. free or standard') @allowed([ @@ -126,22 +125,22 @@ param azureCognitiveSearchName string = '${resourcePrefix}-search' param azureCognitiveSearchSku string = 'standard' @description('Azure Cognitive Search Index') -param azureSearchIndex string = '${resourcePrefix}-index' +param azureSearchIndex string = '${environmentName}-index-${resourceToken}' @description('Azure Cognitive Search Conversation Log Index') param azureSearchConversationLogIndex string = 'conversations' @description('Name of Storage Account') -param storageAccountName string = '${resourcePrefix}str' +param storageAccountName string = '${environmentName}str' @description('Name of Function App for Batch document processing') -param functionName string = '${resourcePrefix}-backend' +param functionName string = '${environmentName}-backend-${resourceToken}' @description('Azure Form Recognizer Name') -param formRecognizerName string = '${resourcePrefix}-formrecog' +param formRecognizerName string = '${environmentName}-formrecog-${resourceToken}' @description('Azure Content Safety Name') -param contentSafetyName string = '${resourcePrefix}-contentsafety' +param contentSafetyName string = '${environmentName}-contentsafety-${resourceToken}' param newGuidString string = newGuid() param searchTag string = 'chatwithyourdata-sa' @@ -151,8 +150,9 @@ var clientKey = '${uniqueString(guid(subscription().id, deployment().name))}${ne var eventGridSystemTopicName = 'doc-processing' var tags = { 'azd-env-name': environmentName } + resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { - name: 'rg-${resourcePrefix}' + name: 'rg-${environmentName}' location: location tags: tags } @@ -216,8 +216,8 @@ module resources './app/resources.bicep' = { contentSafetyName: contentSafetyName location: location eventGridSystemTopicName: eventGridSystemTopicName - storageAccountId: storage.outputs.StorageAccountId - queueName: storage.outputs.StorageAccountName_default_doc_processing_name + storageAccountId: storage.outputs.STORAGE_ACCOUNT_ID + queueName: storage.outputs.STORAGE_ACCOUNT_NAME_DEFAULT_DOC_PROCESSING_NAME blobContainerName: blobContainerName } } @@ -231,7 +231,6 @@ module hostingplan './core/host/appserviceplan.bicep' = { sku: { name: hostingPlanSku } - kind: 'linux' reserved: true } } @@ -364,7 +363,7 @@ module monitoring './core/monitor/monitoring.bicep' = { tags: { 'hidden-link:${resourceId('Microsoft.Web/sites', applicationInsightsName)}': 'Resource' } - logAnalyticsName: '${resourcePrefix}-logAnalytics' + logAnalyticsName: '${environmentName}-logAnalytics-${resourceToken}' applicationInsightsDashboardName: 'dash-${applicationInsightsName}' } } @@ -380,8 +379,6 @@ module function './app/function.bicep' = { storageAccountName: storageAccountName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: azureCognitiveSearchName - runtimeName:'python' - runtimeVersion:'3.11' clientKey: clientKey appSettings: { FUNCTIONS_EXTENSION_VERSION: '~4' From 625487e720df007845b16a5a4f1190d60b8c4e33 Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 9 Nov 2023 13:35:59 +0800 Subject: [PATCH 08/37] Remove depandsOn --- infra/app/storage.bicep | 33 ++++++++++++++------------------- infra/main.bicep | 24 +++--------------------- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep index 30b08ee53..a88e272ba 100644 --- a/infra/app/storage.bicep +++ b/infra/app/storage.bicep @@ -9,26 +9,21 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { sku: { name: 'Standard_GRS' } -} - -resource storageAccountNameDefaultBlobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { - name: '${storageAccountName}/default/${blobContainerName}' - properties: { - publicAccess: 'None' - } - dependsOn: [ - storageAccount - ] -} - -resource storageAccountNameDefaultConfig 'Microsoft.Storage/storageAccounts/blobServices/containers@2021-08-01' = { - name: '${storageAccountName}/default/config' - properties: { - publicAccess: 'None' + resource storageAccountNameDefaultBlob 'blobServices' = { + name: 'default' + resource storageAccountNameDefaultBlobContainer 'containers' = { + name: blobContainerName + properties: { + publicAccess: 'None' + } + } + resource storageAccountNameDefaultConfig 'containers' = { + name: 'config' + properties: { + publicAccess: 'None' + } + } } - dependsOn: [ - storageAccount - ] } resource storageAccountNameDefault 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { diff --git a/infra/main.bicep b/infra/main.bicep index beaa7968e..2b91bf5ea 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -242,7 +242,7 @@ module web './app/web.bicep' = { name: websiteName location: location tags: { 'azd-service-name': 'web' } - appServicePlanId: hostingPlanName + appServicePlanId: hostingplan.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: azureCognitiveSearchName @@ -280,12 +280,6 @@ module web './app/web.bicep' = { AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY } } - dependsOn:[ - hostingplan - storage - monitoring - openai - ] } module adminweb './app/adminweb.bicep' = { @@ -295,7 +289,7 @@ module adminweb './app/adminweb.bicep' = { name: '${websiteName}-admin' location: location tags: { 'azd-service-name': 'adminweb' } - appServicePlanId: hostingPlanName + appServicePlanId: hostingplan.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: azureCognitiveSearchName @@ -336,12 +330,6 @@ module adminweb './app/adminweb.bicep' = { AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY } } - dependsOn:[ - hostingplan - storage - monitoring - openai - ] } module storage './app/storage.bicep' = { @@ -375,7 +363,7 @@ module function './app/function.bicep' = { name: functionName location: location tags: { 'azd-service-name': 'function' } - appServicePlanId: hostingPlanName + appServicePlanId: hostingplan.outputs.name storageAccountName: storageAccountName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: azureCognitiveSearchName @@ -401,10 +389,4 @@ module function './app/function.bicep' = { AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY } } - dependsOn:[ - storage - hostingplan - monitoring - openai - ] } From 65ac3c5c1f08132f4d72d950ecc13a2950e1d7c1 Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 10 Nov 2023 11:12:08 +0800 Subject: [PATCH 09/37] remove resources file --- infra/app/resources.bicep | 87 ------------------------- infra/main.bicep | 130 ++++++++++++++++++++++++++------------ 2 files changed, 89 insertions(+), 128 deletions(-) delete mode 100644 infra/app/resources.bicep diff --git a/infra/app/resources.bicep b/infra/app/resources.bicep deleted file mode 100644 index a50ce7449..000000000 --- a/infra/app/resources.bicep +++ /dev/null @@ -1,87 +0,0 @@ -param contentSafetyName string -param formRecognizerName string -param eventGridSystemTopicName string -param location string -param storageAccountId string -param queueName string -param blobContainerName string - -resource formRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { - name: formRecognizerName - location: location - sku: { - name: 'S0' - } - kind: 'FormRecognizer' - identity: { - type: 'None' - } - properties: { - networkAcls: { - defaultAction: 'Allow' - virtualNetworkRules: [] - ipRules: [] - } - publicNetworkAccess: 'Enabled' - } -} - -resource contentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { - name: contentSafetyName - location: location - sku: { - name: 'S0' - } - kind: 'ContentSafety' - identity: { - type: 'None' - } - properties: { - networkAcls: { - defaultAction: 'Allow' - virtualNetworkRules: [] - ipRules: [] - } - } -} - -resource eventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { - name: eventGridSystemTopicName - location: location - properties: { - source: storageAccountId - topicType: 'Microsoft.Storage.StorageAccounts' - } -} - -resource eventGridSystemTopicNameBlobEvents 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2021-12-01' = { - parent: eventGridSystemTopic - name: 'BlobEvents' - properties: { - destination: { - endpointType: 'StorageQueue' - properties: { - queueMessageTimeToLiveInSeconds: -1 - queueName: queueName - resourceId: storageAccountId - } - } - filter: { - includedEventTypes: [ - 'Microsoft.Storage.BlobCreated' - 'Microsoft.Storage.BlobDeleted' - ] - enableAdvancedFilteringOnArrays: true - subjectBeginsWith: '/blobServices/default/containers/${blobContainerName}/blobs/' - } - labels: [] - eventDeliverySchema: 'EventGridSchema' - retryPolicy: { - maxDeliveryAttempts: 30 - eventTimeToLiveInMinutes: 1440 - } - } -} - -output AZURE_FORM_RECOGNIZER_KEY string = formRecognizer.listKeys().key1 -output AZURE_CONTENT_SAFETY_KEY string = contentSafety.listKeys().key1 diff --git a/infra/main.bicep b/infra/main.bicep index 2b91bf5ea..229a6e469 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1,4 +1,4 @@ -targetScope = 'subscription' +targetScope = 'resourceGroup' @minLength(1) @maxLength(64) @@ -64,7 +64,7 @@ param azureSearchTitleColumn string = 'title' param azureSearchUrlColumn string = 'url' @description('Name of Azure OpenAI Resource') -param azureOpenAIResource string = '${environmentName}-openai-${resourceToken}' +param azureOpenAIResourceName string = '${environmentName}-openai-${resourceToken}' @description('Name of Azure OpenAI Resource SKU') param azureOpenAISkuName string = 'S0' @@ -150,18 +150,10 @@ var clientKey = '${uniqueString(guid(subscription().id, deployment().name))}${ne var eventGridSystemTopicName = 'doc-processing' var tags = { 'azd-env-name': environmentName } - -resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { - name: 'rg-${environmentName}' - location: location - tags: tags -} - module openai 'core/ai/cognitiveservices.bicep' = { name: 'openai' - scope: rg params: { - name: azureOpenAIResource + name: azureOpenAIResourceName location: location tags: tags sku: { @@ -195,7 +187,6 @@ module openai 'core/ai/cognitiveservices.bicep' = { module search './core/search/search-services.bicep' = { name: azureCognitiveSearchName - scope: rg params:{ name: azureCognitiveSearchName location: location @@ -208,23 +199,8 @@ module search './core/search/search-services.bicep' = { } } -module resources './app/resources.bicep' = { - name: 'resource' - scope: rg - params: { - formRecognizerName: formRecognizerName - contentSafetyName: contentSafetyName - location: location - eventGridSystemTopicName: eventGridSystemTopicName - storageAccountId: storage.outputs.STORAGE_ACCOUNT_ID - queueName: storage.outputs.STORAGE_ACCOUNT_NAME_DEFAULT_DOC_PROCESSING_NAME - blobContainerName: blobContainerName - } -} - module hostingplan './core/host/appserviceplan.bicep' = { name: hostingPlanName - scope: rg params: { name: hostingPlanName location: location @@ -237,7 +213,6 @@ module hostingplan './core/host/appserviceplan.bicep' = { module web './app/web.bicep' = { name: websiteName - scope: rg params: { name: websiteName location: location @@ -258,7 +233,7 @@ module web './app/web.bicep' = { AZURE_SEARCH_FILENAME_COLUMN: azureSearchFilenameColumn AZURE_SEARCH_TITLE_COLUMN: azureSearchTitleColumn AZURE_SEARCH_URL_COLUMN: azureSearchUrlColumn - AZURE_OPENAI_RESOURCE: azureOpenAIResource + AZURE_OPENAI_RESOURCE: azureOpenAIResourceName AZURE_OPENAI_MODEL: azureOpenAIModel AZURE_OPENAI_MODEL_NAME: azureOpenAIModelName AZURE_OPENAI_TEMPERATURE: azureOpenAITemperature @@ -276,15 +251,14 @@ module web './app/web.bicep' = { AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY APPINSIGHTS_CONNECTION_STRING: monitoring.outputs.applicationInsightsConnectionString - AZURE_FORM_RECOGNIZER_KEY: resources.outputs.AZURE_FORM_RECOGNIZER_KEY - AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY + AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 + AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 } } } module adminweb './app/adminweb.bicep' = { name: '${websiteName}-admin' - scope: rg params: { name: '${websiteName}-admin' location: location @@ -305,7 +279,7 @@ module adminweb './app/adminweb.bicep' = { AZURE_SEARCH_FILENAME_COLUMN: azureSearchFilenameColumn AZURE_SEARCH_TITLE_COLUMN: azureSearchTitleColumn AZURE_SEARCH_URL_COLUMN: azureSearchUrlColumn - AZURE_OPENAI_RESOURCE: azureOpenAIResource + AZURE_OPENAI_RESOURCE: azureOpenAIResourceName AZURE_OPENAI_MODEL: azureOpenAIModel AZURE_OPENAI_MODEL_NAME: azureOpenAIModelName AZURE_OPENAI_TEMPERATURE: azureOpenAITemperature @@ -326,15 +300,14 @@ module adminweb './app/adminweb.bicep' = { AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY APPINSIGHTS_INSTRUMENTATIONKEY: monitoring.outputs.applicationInsightsInstrumentationKey - AZURE_FORM_RECOGNIZER_KEY: resources.outputs.AZURE_FORM_RECOGNIZER_KEY - AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY + AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 + AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 } } } module storage './app/storage.bicep' = { name: 'Storage_Account' - scope: rg params: { storageAccountName: storageAccountName location: location @@ -344,7 +317,6 @@ module storage './app/storage.bicep' = { module monitoring './core/monitor/monitoring.bicep' = { name: 'monitoring' - scope: rg params: { applicationInsightsName: applicationInsightsName location: location @@ -358,7 +330,6 @@ module monitoring './core/monitor/monitoring.bicep' = { module function './app/function.bicep' = { name: functionName - scope: rg params:{ name: functionName location: location @@ -373,7 +344,7 @@ module function './app/function.bicep' = { WEBSITES_ENABLE_APP_SERVICE_STORAGE: 'false' AZURE_OPENAI_MODEL: azureOpenAIModel AZURE_OPENAI_EMBEDDING_MODEL: azureOpenAIEmbeddingModel - AZURE_OPENAI_RESOURCE: azureOpenAIResource + AZURE_OPENAI_RESOURCE: azureOpenAIResourceName AZURE_BLOB_ACCOUNT_NAME: storageAccountName AZURE_BLOB_CONTAINER_NAME: blobContainerName AZURE_FORM_RECOGNIZER_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' @@ -385,8 +356,85 @@ module function './app/function.bicep' = { AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY APPINSIGHTS_INSTRUMENTATIONKEY: monitoring.outputs.applicationInsightsInstrumentationKey - AZURE_FORM_RECOGNIZER_KEY: resources.outputs.AZURE_FORM_RECOGNIZER_KEY - AZURE_CONTENT_SAFETY_KEY: resources.outputs.AZURE_CONTENT_SAFETY_KEY + AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 + AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 + } + } +} + +resource formRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { + name: formRecognizerName + location: location + sku: { + name: 'S0' + } + kind: 'FormRecognizer' + identity: { + type: 'None' + } + properties: { + networkAcls: { + defaultAction: 'Allow' + virtualNetworkRules: [] + ipRules: [] + } + publicNetworkAccess: 'Enabled' + } +} + +resource contentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { + name: contentSafetyName + location: location + sku: { + name: 'S0' + } + kind: 'ContentSafety' + identity: { + type: 'None' + } + properties: { + networkAcls: { + defaultAction: 'Allow' + virtualNetworkRules: [] + ipRules: [] + } + } +} + +resource eventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { + name: eventGridSystemTopicName + location: location + properties: { + source: storage.outputs.STORAGE_ACCOUNT_ID + topicType: 'Microsoft.Storage.StorageAccounts' + } +} + +resource eventGridSystemTopicNameBlobEvents 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2021-12-01' = { + parent: eventGridSystemTopic + name: 'BlobEvents' + properties: { + destination: { + endpointType: 'StorageQueue' + properties: { + queueMessageTimeToLiveInSeconds: -1 + queueName: queueName + resourceId: storage.outputs.STORAGE_ACCOUNT_ID + } + } + filter: { + includedEventTypes: [ + 'Microsoft.Storage.BlobCreated' + 'Microsoft.Storage.BlobDeleted' + ] + enableAdvancedFilteringOnArrays: true + subjectBeginsWith: '/blobServices/default/containers/${blobContainerName}/blobs/' + } + labels: [] + eventDeliverySchema: 'EventGridSchema' + retryPolicy: { + maxDeliveryAttempts: 30 + eventTimeToLiveInMinutes: 1440 } } } From 43b09e7e95c5129d86e85a3f439d0b5bbf47c239 Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 10 Nov 2023 14:40:21 +0800 Subject: [PATCH 10/37] remov keys from output --- README.md | 2 +- infra/app/adminweb.bicep | 6 +-- infra/app/function.bicep | 6 +-- infra/app/storage.bicep | 59 ------------------------------ infra/app/web.bicep | 6 +-- infra/main.bicep | 79 +++++++++++++++++++++++++++++++--------- 6 files changed, 65 insertions(+), 93 deletions(-) delete mode 100644 infra/app/storage.bicep diff --git a/README.md b/README.md index faea432db..c78dc8088 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Run `azd up` to provision all the resources to Azure and deploy the code to thos azd up ``` -According to the prompt, select `subscription` and `location`, these are the necessary parameters when you create resources. Wait a moment for the resource deployment to complete, click the Website endpoint and you will see the web app page. +According to the prompt, select `subscription` and `location`, these are the necessary parameters when you create resources. After that, choose a resource group created or create a new resource group. Wait a moment for the resource deployment to complete, click the Website endpoint and you will see the web app page. You can also run the sample directly locally (See below). diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 4b80a4887..5e2d3b69a 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -26,7 +26,7 @@ module adminweb '../core/host/appservice.bicep' = { appServicePlanId: appServicePlanId appSettings: union(appSettings, { AZURE_OPENAI_KEY: openAI.listKeys().key1 - AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey + AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey }) keyVaultName: keyVaultName runtimeName: 'python' @@ -39,10 +39,6 @@ resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } -resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { - name: azureCognitiveSearchName -} - output WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID string = adminweb.outputs.identityPrincipalId output WEBSITE_ADMIN_NAME string = adminweb.outputs.name output WEBSITE_ADMIN_URI string = adminweb.outputs.uri diff --git a/infra/app/function.bicep b/infra/app/function.bicep index d220f2b99..108a01fb9 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -28,7 +28,7 @@ module function '../core/host/functions.bicep' = { runtimeVersion: runtimeVersion appSettings: union(appSettings, { AZURE_OPENAI_KEY: openAI.listKeys().key1 - AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey + AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey }) } } @@ -37,10 +37,6 @@ resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } -resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { - name: azureCognitiveSearchName -} - resource functionNameDefaultClientKey 'Microsoft.Web/sites/host/functionKeys@2018-11-01' = { name: '${name}/default/clientKey' properties: { diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep deleted file mode 100644 index a88e272ba..000000000 --- a/infra/app/storage.bicep +++ /dev/null @@ -1,59 +0,0 @@ -param storageAccountName string = '' -param location string = '' -param blobContainerName string = '' - -resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { - name: storageAccountName - location: location - kind: 'StorageV2' - sku: { - name: 'Standard_GRS' - } - resource storageAccountNameDefaultBlob 'blobServices' = { - name: 'default' - resource storageAccountNameDefaultBlobContainer 'containers' = { - name: blobContainerName - properties: { - publicAccess: 'None' - } - } - resource storageAccountNameDefaultConfig 'containers' = { - name: 'config' - properties: { - publicAccess: 'None' - } - } - } -} - -resource storageAccountNameDefault 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { - parent: storageAccount - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} - -resource storageAccountNameDefaultDocProcessing 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { - parent: storageAccountNameDefault - name: 'doc-processing' - properties: { - metadata: {} - } - dependsOn: [] -} - -resource storageAccountNameDefaultDocProcessingPoison 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { - parent: storageAccountNameDefault - name: 'doc-processing-poison' - properties: { - metadata: {} - } - dependsOn: [] -} - -output STORAGE_ACCOUNT_ID string = storageAccount.id -output STORAGE_ACCOUNT_NAME_DEFAULT_DOC_PROCESSING_NAME string = storageAccountNameDefaultDocProcessing.name -output AZURE_BLOB_ACCOUNT_KEY string = storageAccount.listKeys().keys[0].value diff --git a/infra/app/web.bicep b/infra/app/web.bicep index e74ae34cc..9567704c5 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -26,7 +26,7 @@ module web '../core/host/appservice.bicep' = { appServicePlanId: appServicePlanId appSettings: union(appSettings, { AZURE_OPENAI_KEY: openAI.listKeys().key1 - AZURE_SEARCH_KEY: search.listAdminKeys().primaryKey + AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey }) keyVaultName: keyVaultName @@ -40,10 +40,6 @@ resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { name: azureOpenAIName } -resource search 'Microsoft.Search/searchServices@2021-04-01-preview' existing = { - name: azureCognitiveSearchName -} - output FRONTEND_API_IDENTITY_PRINCIPAL_ID string = web.outputs.identityPrincipalId output FRONTEND_API_NAME string = web.outputs.name output FRONTEND_API_URI string = web.outputs.uri diff --git a/infra/main.bicep b/infra/main.bicep index 229a6e469..7e3437e0a 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -151,7 +151,7 @@ var eventGridSystemTopicName = 'doc-processing' var tags = { 'azd-env-name': environmentName } module openai 'core/ai/cognitiveservices.bicep' = { - name: 'openai' + name: azureOpenAIResourceName params: { name: azureOpenAIResourceName location: location @@ -220,7 +220,7 @@ module web './app/web.bicep' = { appServicePlanId: hostingplan.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name - azureCognitiveSearchName: azureCognitiveSearchName + azureCognitiveSearchName: search.outputs.name appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -249,7 +249,7 @@ module web './app/web.bicep' = { AZURE_BLOB_CONTAINER_NAME: blobContainerName ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' - AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY + AZURE_BLOB_ACCOUNT_KEY: storageAccount.listKeys().keys[0].value APPINSIGHTS_CONNECTION_STRING: monitoring.outputs.applicationInsightsConnectionString AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 @@ -266,7 +266,7 @@ module adminweb './app/adminweb.bicep' = { appServicePlanId: hostingplan.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name - azureCognitiveSearchName: azureCognitiveSearchName + azureCognitiveSearchName: search.outputs.name appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -298,7 +298,7 @@ module adminweb './app/adminweb.bicep' = { FUNCTION_KEY: clientKey ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' - AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY + AZURE_BLOB_ACCOUNT_KEY: storageAccount.listKeys().keys[0].value APPINSIGHTS_INSTRUMENTATIONKEY: monitoring.outputs.applicationInsightsInstrumentationKey AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 @@ -306,15 +306,6 @@ module adminweb './app/adminweb.bicep' = { } } -module storage './app/storage.bicep' = { - name: 'Storage_Account' - params: { - storageAccountName: storageAccountName - location: location - blobContainerName: blobContainerName - } -} - module monitoring './core/monitor/monitoring.bicep' = { name: 'monitoring' params: { @@ -337,7 +328,7 @@ module function './app/function.bicep' = { appServicePlanId: hostingplan.outputs.name storageAccountName: storageAccountName azureOpenAIName: openai.outputs.name - azureCognitiveSearchName: azureCognitiveSearchName + azureCognitiveSearchName: search.outputs.name clientKey: clientKey appSettings: { FUNCTIONS_EXTENSION_VERSION: '~4' @@ -354,7 +345,7 @@ module function './app/function.bicep' = { AZURE_SEARCH_INDEX: azureSearchIndex ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' - AZURE_BLOB_ACCOUNT_KEY: storage.outputs.AZURE_BLOB_ACCOUNT_KEY + AZURE_BLOB_ACCOUNT_KEY: storageAccount.listKeys().keys[0].value APPINSIGHTS_INSTRUMENTATIONKEY: monitoring.outputs.applicationInsightsInstrumentationKey AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 @@ -405,7 +396,7 @@ resource eventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { name: eventGridSystemTopicName location: location properties: { - source: storage.outputs.STORAGE_ACCOUNT_ID + source: storageAccount.id topicType: 'Microsoft.Storage.StorageAccounts' } } @@ -419,7 +410,7 @@ resource eventGridSystemTopicNameBlobEvents 'Microsoft.EventGrid/systemTopics/ev properties: { queueMessageTimeToLiveInSeconds: -1 queueName: queueName - resourceId: storage.outputs.STORAGE_ACCOUNT_ID + resourceId: storageAccount.id } } filter: { @@ -438,3 +429,55 @@ resource eventGridSystemTopicNameBlobEvents 'Microsoft.EventGrid/systemTopics/ev } } } + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_GRS' + } + resource storageAccountNameDefaultBlob 'blobServices' = { + name: 'default' + resource storageAccountNameDefaultBlobContainer 'containers' = { + name: blobContainerName + properties: { + publicAccess: 'None' + } + } + resource storageAccountNameDefaultConfig 'containers' = { + name: 'config' + properties: { + publicAccess: 'None' + } + } + } +} + +resource storageAccountNameDefault 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { + parent: storageAccount + name: 'default' + properties: { + cors: { + corsRules: [] + } + } +} + +resource storageAccountNameDefaultDocProcessing 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { + parent: storageAccountNameDefault + name: 'doc-processing' + properties: { + metadata: {} + } + dependsOn: [] +} + +resource storageAccountNameDefaultDocProcessingPoison 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { + parent: storageAccountNameDefault + name: 'doc-processing-poison' + properties: { + metadata: {} + } + dependsOn: [] +} From bd29d31c90d2a2dec875928711427374fd8814eb Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 10 Nov 2023 16:56:16 +0800 Subject: [PATCH 11/37] remove existing reference in module --- infra/app/adminweb.bicep | 6 +----- infra/app/function.bicep | 6 +----- infra/app/web.bicep | 6 +----- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 5e2d3b69a..ad0b5951d 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -25,7 +25,7 @@ module adminweb '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AZURE_OPENAI_KEY: openAI.listKeys().key1 + AZURE_OPENAI_KEY: listKeys('Microsoft.CognitiveServices/accounts/${azureOpenAIName}', '2023-05-01').key1 AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey }) keyVaultName: keyVaultName @@ -35,10 +35,6 @@ module adminweb '../core/host/appservice.bicep' = { } } -resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { - name: azureOpenAIName -} - output WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID string = adminweb.outputs.identityPrincipalId output WEBSITE_ADMIN_NAME string = adminweb.outputs.name output WEBSITE_ADMIN_URI string = adminweb.outputs.uri diff --git a/infra/app/function.bicep b/infra/app/function.bicep index 108a01fb9..02f00f4ad 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -27,16 +27,12 @@ module function '../core/host/functions.bicep' = { runtimeName: runtimeName runtimeVersion: runtimeVersion appSettings: union(appSettings, { - AZURE_OPENAI_KEY: openAI.listKeys().key1 + AZURE_OPENAI_KEY: listKeys('Microsoft.CognitiveServices/accounts/${azureOpenAIName}', '2023-05-01').key1 AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey }) } } -resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { - name: azureOpenAIName -} - resource functionNameDefaultClientKey 'Microsoft.Web/sites/host/functionKeys@2018-11-01' = { name: '${name}/default/clientKey' properties: { diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 9567704c5..8b6f5ee64 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -25,7 +25,7 @@ module web '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AZURE_OPENAI_KEY: openAI.listKeys().key1 + AZURE_OPENAI_KEY: listKeys('Microsoft.CognitiveServices/accounts/${azureOpenAIName}', '2023-05-01').key1 AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey }) @@ -36,10 +36,6 @@ module web '../core/host/appservice.bicep' = { } } -resource openAI 'Microsoft.CognitiveServices/accounts@2023-05-01' existing = { - name: azureOpenAIName -} - output FRONTEND_API_IDENTITY_PRINCIPAL_ID string = web.outputs.identityPrincipalId output FRONTEND_API_NAME string = web.outputs.name output FRONTEND_API_URI string = web.outputs.uri From 8587450ce9a8ab055c5d51346d8dd4da091ffdbd Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 15 Nov 2023 11:08:16 +0800 Subject: [PATCH 12/37] update ReadMe --- README.md | 12 ++++++------ azure.yaml | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c78dc8088..176ae235d 100644 --- a/README.md +++ b/README.md @@ -104,11 +104,11 @@ Out-of-the-box, you can upload the following file types: ![A screenshot of the chat app.](./media/chat-app.png) -## Running the sample using Azd template +## Running the sample using the Azure Developer CLI (azd) template The Azure Developer CLI (`azd`) is a developer-centric command-line interface (CLI) tool for creating Azure applications. -You need to install it before running and deploying with Azure Developer CLI. +You need to install it before running and deploying with the Azure Developer CLI. ### Windows @@ -122,7 +122,7 @@ powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' curl -fsSL https://aka.ms/install-azd.sh | bash ``` -After logging in with the following command, you will be able to use the azd cli to quickly provision and deploy the application. +After logging in with the following command, you will be able to use azd cli to quickly provision and deploy the application. ``` azd auth login @@ -132,18 +132,18 @@ Then, execute the `azd init` command to initialize the environment. ``` azd init -t jongio/chat-with-your-data-solution-accelerator ``` -According to the prompt, enter an `env name`. +According to the prompt, enter an environment name. Run `azd up` to provision all the resources to Azure and deploy the code to those resources. ``` azd up ``` -According to the prompt, select `subscription` and `location`, these are the necessary parameters when you create resources. After that, choose a resource group created or create a new resource group. Wait a moment for the resource deployment to complete, click the Website endpoint and you will see the web app page. +According to the prompt, select `subscription` and `location`, these are the necessary parameters when you create resources. After that, choose a resource group or create a new resource group. Wait a moment for the resource deployment to complete, click the Website endpoint and you will see the web app page. You can also run the sample directly locally (See below). -## Development and run the accelerator locally +## Develop and run the accelerator locally To customize the accelerator or run it locally, first, copy the `.env.sample` file to your development environment's `.env` file, and edit it according to [environment variable values table](#environment-variables) below. diff --git a/azure.yaml b/azure.yaml index 525049475..63287d07e 100644 --- a/azure.yaml +++ b/azure.yaml @@ -1,7 +1,8 @@ # yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json name: chat-with-your-data-solution-accelerator - +metadata: + template: chat-with-your-data-solution-accelerator services: web: project: . From afd3ca80846ec9975c7d26c1597aea69a8971e9c Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 22 Nov 2023 14:31:06 +0800 Subject: [PATCH 13/37] add rgName in listkeys --- infra/app/adminweb.bicep | 13 +- infra/app/eventgrid.bicep | 43 ++++++ infra/app/function.bicep | 13 +- infra/app/storage.bicep | 56 +++++++ infra/app/web.bicep | 13 +- infra/core/storage/storage-account.bicep | 64 ++++++++ infra/main.bicep | 180 +++++++---------------- 7 files changed, 245 insertions(+), 137 deletions(-) create mode 100644 infra/app/eventgrid.bicep create mode 100644 infra/app/storage.bicep create mode 100644 infra/core/storage/storage-account.bicep diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index ad0b5951d..e465fd945 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -1,7 +1,10 @@ param name string param location string = resourceGroup().location param tags object = {} - +param rgName string = '' +param storageAccountName string = '' +param formRecognizerName string = '' +param contentSafetyName string = '' param allowedOrigins array = [] param appServicePlanId string param appCommandLine string = 'python -m streamlit run Admin.py --server.port 8000 --server.address 0.0.0.0 --server.enableXsrfProtection false' @@ -9,7 +12,6 @@ param applicationInsightsName string = '' param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' - @secure() param appSettings object = {} param serviceName string = 'adminweb' @@ -25,8 +27,11 @@ module adminweb '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AZURE_OPENAI_KEY: listKeys('Microsoft.CognitiveServices/accounts/${azureOpenAIName}', '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/app/eventgrid.bicep b/infra/app/eventgrid.bicep new file mode 100644 index 000000000..78a091a84 --- /dev/null +++ b/infra/app/eventgrid.bicep @@ -0,0 +1,43 @@ +param name string +param location string +param storageAccountId string +param queueName string +param blobContainerName string + +resource eventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { + name: name + location: location + properties: { + source: storageAccountId + topicType: 'Microsoft.Storage.StorageAccounts' + } +} + +resource eventGridSystemTopicNameBlobEvents 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2021-12-01' = { + parent: eventGridSystemTopic + name: 'BlobEvents' + properties: { + destination: { + endpointType: 'StorageQueue' + properties: { + queueMessageTimeToLiveInSeconds: -1 + queueName: queueName + resourceId: storageAccountId + } + } + filter: { + includedEventTypes: [ + 'Microsoft.Storage.BlobCreated' + 'Microsoft.Storage.BlobDeleted' + ] + enableAdvancedFilteringOnArrays: true + subjectBeginsWith: '/blobServices/default/containers/${blobContainerName}/blobs/' + } + labels: [] + eventDeliverySchema: 'EventGridSchema' + retryPolicy: { + maxDeliveryAttempts: 30 + eventTimeToLiveInMinutes: 1440 + } + } +} diff --git a/infra/app/function.bicep b/infra/app/function.bicep index 02f00f4ad..ab04d1bcc 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -3,18 +3,18 @@ param location string = '' param appServicePlanId string param storageAccountName string = '' param tags object = {} - @secure() param appSettings object = {} param serviceName string = 'function' param runtimeName string = 'python' param runtimeVersion string = '3.11' - @secure() param clientKey string param azureOpenAIName string = '' param azureCognitiveSearchName string = '' - +param rgName string = '' +param formRecognizerName string = '' +param contentSafetyName string = '' module function '../core/host/functions.bicep' = { name: '${name}-app-module' @@ -27,8 +27,11 @@ module function '../core/host/functions.bicep' = { runtimeName: runtimeName runtimeVersion: runtimeVersion appSettings: union(appSettings, { - AZURE_OPENAI_KEY: listKeys('Microsoft.CognitiveServices/accounts/${azureOpenAIName}', '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) } } diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep new file mode 100644 index 000000000..f0221ed4c --- /dev/null +++ b/infra/app/storage.bicep @@ -0,0 +1,56 @@ +param name string +param location string +param blobContainerName string + +resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { + name: name + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_GRS' + } + resource storageAccountNameDefaultBlob 'blobServices' = { + name: 'default' + resource storageAccountNameDefaultBlobContainer 'containers' = { + name: blobContainerName + properties: { + publicAccess: 'None' + } + } + resource storageAccountNameDefaultConfig 'containers' = { + name: 'config' + properties: { + publicAccess: 'None' + } + } + } +} + +resource storageAccountNameDefault 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { + parent: storageAccount + name: 'default' + properties: { + cors: { + corsRules: [] + } + } +} + +resource storageAccountNameDefaultDocProcessing 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { + parent: storageAccountNameDefault + name: 'doc-processing' + properties: { + metadata: {} + } +} + +resource storageAccountNameDefaultDocProcessingPoison 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { + parent: storageAccountNameDefault + name: 'doc-processing-poison' + properties: { + metadata: {} + } +} + +output STORAGE_ACCOUNT_ID string = storageAccount.id +output STORAGE_ACCOUNT_NAME string = storageAccount.name diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 8b6f5ee64..6c77f8b53 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -1,7 +1,6 @@ param name string param location string = resourceGroup().location param tags object = {} - param allowedOrigins array = [] param appCommandLine string = '' param appServicePlanId string @@ -9,7 +8,10 @@ param applicationInsightsName string = '' param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' - +param rgName string = '' +param storageAccountName string = '' +param formRecognizerName string = '' +param contentSafetyName string = '' @secure() param appSettings object = {} param serviceName string = 'web' @@ -25,8 +27,11 @@ module web '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AZURE_OPENAI_KEY: listKeys('Microsoft.CognitiveServices/accounts/${azureOpenAIName}', '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys('Microsoft.Search/searchServices/${azureCognitiveSearchName}', '2021-04-01-preview').primaryKey + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName diff --git a/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep new file mode 100644 index 000000000..4b6febbea --- /dev/null +++ b/infra/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/infra/main.bicep b/infra/main.bicep index 7e3437e0a..3670f2d69 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1,4 +1,4 @@ -targetScope = 'resourceGroup' +targetScope = 'subscription' @minLength(1) @maxLength(64) @@ -149,9 +149,18 @@ var queueName = 'doc-processing' var clientKey = '${uniqueString(guid(subscription().id, deployment().name))}${newGuidString}' var eventGridSystemTopicName = 'doc-processing' var tags = { 'azd-env-name': environmentName } +var rgName = 'rg-${environmentName}' + +// Organize resources in a resource group +resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { + name: rgName + location: location + tags: tags +} module openai 'core/ai/cognitiveservices.bicep' = { name: azureOpenAIResourceName + scope: rg params: { name: azureOpenAIResourceName location: location @@ -187,6 +196,7 @@ module openai 'core/ai/cognitiveservices.bicep' = { module search './core/search/search-services.bicep' = { name: azureCognitiveSearchName + scope: rg params:{ name: azureCognitiveSearchName location: location @@ -201,6 +211,7 @@ module search './core/search/search-services.bicep' = { module hostingplan './core/host/appserviceplan.bicep' = { name: hostingPlanName + scope: rg params: { name: hostingPlanName location: location @@ -213,14 +224,19 @@ module hostingplan './core/host/appserviceplan.bicep' = { module web './app/web.bicep' = { name: websiteName + scope: rg params: { name: websiteName location: location tags: { 'azd-service-name': 'web' } + rgName: rgName appServicePlanId: hostingplan.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name + storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + formRecognizerName: formrecognizer.outputs.name + contentSafetyName: contentsafety.outputs.name appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -249,24 +265,26 @@ module web './app/web.bicep' = { AZURE_BLOB_CONTAINER_NAME: blobContainerName ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' - AZURE_BLOB_ACCOUNT_KEY: storageAccount.listKeys().keys[0].value APPINSIGHTS_CONNECTION_STRING: monitoring.outputs.applicationInsightsConnectionString - AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 - AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 } } } module adminweb './app/adminweb.bicep' = { name: '${websiteName}-admin' + scope: rg params: { name: '${websiteName}-admin' location: location tags: { 'azd-service-name': 'adminweb' } + rgName: rgName appServicePlanId: hostingplan.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name + storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + formRecognizerName: formrecognizer.outputs.name + contentSafetyName: contentsafety.outputs.name appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -298,16 +316,14 @@ module adminweb './app/adminweb.bicep' = { FUNCTION_KEY: clientKey ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' - AZURE_BLOB_ACCOUNT_KEY: storageAccount.listKeys().keys[0].value APPINSIGHTS_INSTRUMENTATIONKEY: monitoring.outputs.applicationInsightsInstrumentationKey - AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 - AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 } } } module monitoring './core/monitor/monitoring.bicep' = { name: 'monitoring' + scope: rg params: { applicationInsightsName: applicationInsightsName location: location @@ -321,14 +337,18 @@ module monitoring './core/monitor/monitoring.bicep' = { module function './app/function.bicep' = { name: functionName + scope: rg params:{ name: functionName location: location tags: { 'azd-service-name': 'function' } + rgName: rgName appServicePlanId: hostingplan.outputs.name - storageAccountName: storageAccountName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name + storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + formRecognizerName: formrecognizer.outputs.name + contentSafetyName: contentsafety.outputs.name clientKey: clientKey appSettings: { FUNCTIONS_EXTENSION_VERSION: '~4' @@ -345,139 +365,51 @@ module function './app/function.bicep' = { AZURE_SEARCH_INDEX: azureSearchIndex ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' - AZURE_BLOB_ACCOUNT_KEY: storageAccount.listKeys().keys[0].value APPINSIGHTS_INSTRUMENTATIONKEY: monitoring.outputs.applicationInsightsInstrumentationKey - AZURE_FORM_RECOGNIZER_KEY: formRecognizer.listKeys().key1 - AZURE_CONTENT_SAFETY_KEY: contentSafety.listKeys().key1 } } } -resource formRecognizer 'Microsoft.CognitiveServices/accounts@2022-12-01' = { +module formrecognizer 'core/ai/cognitiveservices.bicep' = { name: formRecognizerName - location: location - sku: { - name: 'S0' - } - kind: 'FormRecognizer' - identity: { - type: 'None' - } - properties: { - networkAcls: { - defaultAction: 'Allow' - virtualNetworkRules: [] - ipRules: [] - } - publicNetworkAccess: 'Enabled' + scope: rg + params: { + name: formRecognizerName + location: location + tags: tags + kind: 'FormRecognizer' } } -resource contentSafety 'Microsoft.CognitiveServices/accounts@2022-03-01' = { +module contentsafety 'core/ai/cognitiveservices.bicep' = { name: contentSafetyName - location: location - sku: { - name: 'S0' - } - kind: 'ContentSafety' - identity: { - type: 'None' - } - properties: { - networkAcls: { - defaultAction: 'Allow' - virtualNetworkRules: [] - ipRules: [] - } + scope: rg + params: { + name: contentSafetyName + location: location + tags: tags + kind: 'ContentSafety' } } -resource eventGridSystemTopic 'Microsoft.EventGrid/systemTopics@2021-12-01' = { +module eventgrid 'app/eventgrid.bicep' = { name: eventGridSystemTopicName - location: location - properties: { - source: storageAccount.id - topicType: 'Microsoft.Storage.StorageAccounts' - } -} - -resource eventGridSystemTopicNameBlobEvents 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2021-12-01' = { - parent: eventGridSystemTopic - name: 'BlobEvents' - properties: { - destination: { - endpointType: 'StorageQueue' - properties: { - queueMessageTimeToLiveInSeconds: -1 - queueName: queueName - resourceId: storageAccount.id - } - } - filter: { - includedEventTypes: [ - 'Microsoft.Storage.BlobCreated' - 'Microsoft.Storage.BlobDeleted' - ] - enableAdvancedFilteringOnArrays: true - subjectBeginsWith: '/blobServices/default/containers/${blobContainerName}/blobs/' - } - labels: [] - eventDeliverySchema: 'EventGridSchema' - retryPolicy: { - maxDeliveryAttempts: 30 - eventTimeToLiveInMinutes: 1440 - } + scope: rg + params: { + name: eventGridSystemTopicName + location: location + storageAccountId: storage.outputs.STORAGE_ACCOUNT_ID + queueName: queueName + blobContainerName: blobContainerName } } -resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { +module storage 'app/storage.bicep' = { name: storageAccountName - location: location - kind: 'StorageV2' - sku: { - name: 'Standard_GRS' - } - resource storageAccountNameDefaultBlob 'blobServices' = { - name: 'default' - resource storageAccountNameDefaultBlobContainer 'containers' = { - name: blobContainerName - properties: { - publicAccess: 'None' - } - } - resource storageAccountNameDefaultConfig 'containers' = { - name: 'config' - properties: { - publicAccess: 'None' - } - } - } -} - -resource storageAccountNameDefault 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { - parent: storageAccount - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} - -resource storageAccountNameDefaultDocProcessing 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { - parent: storageAccountNameDefault - name: 'doc-processing' - properties: { - metadata: {} - } - dependsOn: [] -} - -resource storageAccountNameDefaultDocProcessingPoison 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { - parent: storageAccountNameDefault - name: 'doc-processing-poison' - properties: { - metadata: {} + scope: rg + params: { + name: storageAccountName + location: location + blobContainerName: blobContainerName } - dependsOn: [] } From 654762e5d158cb5d539a60c2e8bdb999ef9fc2b9 Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 23 Nov 2023 10:52:18 +0800 Subject: [PATCH 14/37] add use keyvault option add keyvault option to get secret keys remove invalid changes add param useKeyVault --- app.py | 8 +- backend/pages/01_Ingest_Data.py | 6 +- backend/requirements.txt | 3 +- backend/utilities/helpers/EnvHelper.py | 14 ++-- infra/app/adminweb.bicep | 19 +++-- infra/app/function.bicep | 23 ++++-- infra/app/storekeys.bicep | 57 +++++++++++++ infra/app/web.bicep | 20 +++-- infra/core/security/keyvault-access.bicep | 22 +++++ infra/core/security/keyvault-secret.bicep | 31 +++++++ infra/core/security/keyvault.bicep | 26 ++++++ infra/core/security/registry-access.bicep | 19 +++++ infra/core/security/role.bicep | 21 +++++ infra/core/storage/storage-account.bicep | 64 --------------- infra/main.bicep | 98 +++++++++++++++++++++++ infra/main.bicepparam | 4 + requirements.txt | 3 +- 17 files changed, 349 insertions(+), 89 deletions(-) create mode 100644 infra/app/storekeys.bicep create mode 100644 infra/core/security/keyvault-access.bicep create mode 100644 infra/core/security/keyvault-secret.bicep create mode 100644 infra/core/security/keyvault.bicep create mode 100644 infra/core/security/registry-access.bicep create mode 100644 infra/core/security/role.bicep delete mode 100644 infra/core/storage/storage-account.bicep diff --git a/app.py b/app.py index 2d3773891..49728a9e4 100644 --- a/app.py +++ b/app.py @@ -13,6 +13,8 @@ from flask import Flask, Response, request, jsonify from dotenv import load_dotenv from backend.utilities.QuestionHandler import QuestionHandler +from azure.identity import DefaultAzureCredential +from azure.keyvault.secrets import SecretClient load_dotenv() @@ -23,10 +25,12 @@ def static_file(path): return app.send_static_file(path) +credential = DefaultAzureCredential() +secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) # ACS Integration Settings AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") -AZURE_SEARCH_KEY = os.environ.get("AZURE_SEARCH_KEY") +AZURE_SEARCH_KEY = secret_client.get_secret(os.environ.get("AZURE_SEARCH_KEY")).value if os.environ.get("USE_KEY_VAULT") else os.environ.get("AZURE_SEARCH_KEY") AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.environ.get("AZURE_SEARCH_USE_SEMANTIC_SEARCH", False) AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.environ.get("AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG", "default") AZURE_SEARCH_TOP_K = os.environ.get("AZURE_SEARCH_TOP_K", 5) @@ -39,7 +43,7 @@ def static_file(path): # AOAI Integration Settings AZURE_OPENAI_RESOURCE = os.environ.get("AZURE_OPENAI_RESOURCE") AZURE_OPENAI_MODEL = os.environ.get("AZURE_OPENAI_MODEL") -AZURE_OPENAI_KEY = os.environ.get("AZURE_OPENAI_KEY") +AZURE_OPENAI_KEY = secret_client.get_secret(os.environ.get("AZURE_OPENAI_KEY")).value if os.environ.get("USE_KEY_VAULT") else os.environ.get("AZURE_OPENAI_KEY") AZURE_OPENAI_TEMPERATURE = os.environ.get("AZURE_OPENAI_TEMPERATURE", 0) AZURE_OPENAI_TOP_P = os.environ.get("AZURE_OPENAI_TOP_P", 1.0) AZURE_OPENAI_MAX_TOKENS = os.environ.get("AZURE_OPENAI_MAX_TOKENS", 1000) diff --git a/backend/pages/01_Ingest_Data.py b/backend/pages/01_Ingest_Data.py index 971b796ca..eee40bb89 100644 --- a/backend/pages/01_Ingest_Data.py +++ b/backend/pages/01_Ingest_Data.py @@ -11,6 +11,8 @@ import urllib.parse from utilities.helpers.ConfigHelper import ConfigHelper from dotenv import load_dotenv +from azure.identity import DefaultAzureCredential +from azure.keyvault.secrets import SecretClient load_dotenv() logger = logging.getLogger('azure.core.pipeline.policies.http_logging_policy').setLevel(logging.WARNING) @@ -65,7 +67,9 @@ def upload_file(bytes_data: bytes, file_name: str, content_type: Optional[str] = charset = f"; charset={chardet.detect(bytes_data)['encoding']}" if content_type == 'text/plain' else '' content_type = content_type if content_type != None else 'text/plain' account_name = os.getenv('AZURE_BLOB_ACCOUNT_NAME') - account_key = os.getenv('AZURE_BLOB_ACCOUNT_KEY') + credential = DefaultAzureCredential() + secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + account_key = secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY")).value if os.getenv("USE_KEY_VAULT") else os.getenv("AZURE_BLOB_ACCOUNT_KEY") container_name = os.getenv('AZURE_BLOB_CONTAINER_NAME') if account_name == None or account_key == None or container_name == None: raise ValueError("Please provide values for AZURE_BLOB_ACCOUNT_NAME, AZURE_BLOB_ACCOUNT_KEY and AZURE_BLOB_CONTAINER_NAME") diff --git a/backend/requirements.txt b/backend/requirements.txt index a398e7c69..f1c9499b2 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -23,4 +23,5 @@ chardet==5.1.0 azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 pandas==1.5.1 -python-docx==0.8.11 \ No newline at end of file +python-docx==0.8.11 +azure-keyvault-secrets == 4.4.* \ No newline at end of file diff --git a/backend/utilities/helpers/EnvHelper.py b/backend/utilities/helpers/EnvHelper.py index d9ff68c5b..771a86152 100644 --- a/backend/utilities/helpers/EnvHelper.py +++ b/backend/utilities/helpers/EnvHelper.py @@ -1,16 +1,20 @@ import os import logging from dotenv import load_dotenv +from azure.identity import DefaultAzureCredential +from azure.keyvault.secrets import SecretClient logger = logging.getLogger(__name__) class EnvHelper: def __init__(self, **kwargs) -> None: load_dotenv() + credential = DefaultAzureCredential() + secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) # Azure Search self.AZURE_SEARCH_SERVICE = os.getenv('AZURE_SEARCH_SERVICE', '') self.AZURE_SEARCH_INDEX = os.getenv('AZURE_SEARCH_INDEX', '') - self.AZURE_SEARCH_KEY = os.getenv('AZURE_SEARCH_KEY', '') + self.AZURE_SEARCH_KEY = secret_client.get_secret(os.getenv("AZURE_SEARCH_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_SEARCH_KEY", '') self.AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.getenv('AZURE_SEARCH_USE_SEMANTIC_SEARCH', '') self.AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.getenv('AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG', '') self.AZURE_SEARCH_INDEX_IS_PRECHUNKED = os.getenv('AZURE_SEARCH_INDEX_IS_PRECHUNKED', '') @@ -29,7 +33,7 @@ def __init__(self, **kwargs) -> None: # Azure OpenAI self.AZURE_OPENAI_RESOURCE = os.getenv('AZURE_OPENAI_RESOURCE', '') self.AZURE_OPENAI_MODEL = os.getenv('AZURE_OPENAI_MODEL', '') - self.AZURE_OPENAI_KEY = os.getenv('AZURE_OPENAI_KEY', '') + self.AZURE_OPENAI_KEY = secret_client.get_secret(os.getenv("AZURE_OPENAI_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_OPENAI_KEY", '') self.AZURE_OPENAI_MODEL_NAME = os.getenv('AZURE_OPENAI_MODEL_NAME', '') self.AZURE_OPENAI_TEMPERATURE = os.getenv('AZURE_OPENAI_TEMPERATURE', '') self.AZURE_OPENAI_TOP_P = os.getenv('AZURE_OPENAI_TOP_P', '') @@ -54,16 +58,16 @@ def __init__(self, **kwargs) -> None: self.DOCUMENT_PROCESSING_QUEUE_NAME = os.getenv('DOCUMENT_PROCESSING_QUEUE_NAME', '') # Azure Blob Storage self.AZURE_BLOB_ACCOUNT_NAME = os.getenv('AZURE_BLOB_ACCOUNT_NAME', '') - self.AZURE_BLOB_ACCOUNT_KEY = os.getenv('AZURE_BLOB_ACCOUNT_KEY', '') + self.AZURE_BLOB_ACCOUNT_KEY = secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_BLOB_ACCOUNT_KEY", '') self.AZURE_BLOB_CONTAINER_NAME = os.getenv('AZURE_BLOB_CONTAINER_NAME', '') # Azure Form Recognizer self.AZURE_FORM_RECOGNIZER_ENDPOINT = os.getenv('AZURE_FORM_RECOGNIZER_ENDPOINT', '') - self.AZURE_FORM_RECOGNIZER_KEY = os.getenv('AZURE_FORM_RECOGNIZER_KEY', '') + self.AZURE_FORM_RECOGNIZER_KEY = secret_client.get_secret(os.getenv("AZURE_FORM_RECOGNIZER_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_FORM_RECOGNIZER_KEY", '') # Azure App Insights self.APPINSIGHTS_CONNECTION_STRING = os.getenv('APPINSIGHTS_CONNECTION_STRING', '') # Azure AI Content Safety self.AZURE_CONTENT_SAFETY_ENDPOINT = os.getenv('AZURE_CONTENT_SAFETY_ENDPOINT', '') - self.AZURE_CONTENT_SAFETY_KEY = os.getenv('AZURE_CONTENT_SAFETY_KEY', '') + self.AZURE_CONTENT_SAFETY_KEY = secret_client.get_secret(os.getenv("AZURE_CONTENT_SAFETY_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_CONTENT_SAFETY_KEY", '') # Orchestration Settings self.ORCHESTRATION_STRATEGY = os.getenv('ORCHESTRATION_STRATEGY', 'openai_function') diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index e465fd945..232fe34e9 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -15,6 +15,13 @@ param azureCognitiveSearchName string = '' @secure() param appSettings object = {} param serviceName string = 'adminweb' +param useKeyVault bool = true +param openAIKey string = '' +param storageAccountKey string = '' +param formRecognizerKey string = '' +param searchKey string = '' +param contentSafetyKey string = '' +param keyVaultEndpoint string = '' module adminweb '../core/host/appservice.bicep' = { name: '${name}-app-module' @@ -27,11 +34,13 @@ module adminweb '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + USE_KEY_VAULT: useKeyVault ? useKeyVault : '' + AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' + AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/app/function.bicep b/infra/app/function.bicep index ab04d1bcc..7f6df0e6c 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -10,11 +10,19 @@ param runtimeName string = 'python' param runtimeVersion string = '3.11' @secure() param clientKey string +param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' param rgName string = '' param formRecognizerName string = '' param contentSafetyName string = '' +param useKeyVault bool = true +param openAIKey string = '' +param storageAccountKey string = '' +param formRecognizerKey string = '' +param searchKey string = '' +param contentSafetyKey string = '' +param keyVaultEndpoint string = '' module function '../core/host/functions.bicep' = { name: '${name}-app-module' @@ -24,14 +32,17 @@ module function '../core/host/functions.bicep' = { tags: union(tags, { 'azd-service-name': serviceName }) appServicePlanId: appServicePlanId storageAccountName: storageAccountName + keyVaultName: keyVaultName runtimeName: runtimeName runtimeVersion: runtimeVersion appSettings: union(appSettings, { - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + USE_KEY_VAULT: useKeyVault ? useKeyVault : '' + AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' + AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) } } @@ -62,3 +73,5 @@ resource waitFunctionDeploymentSection 'Microsoft.Resources/deploymentScripts@20 function ] } + +output FUNCTION_IDENTITY_PRINCIPAL_ID string = function.outputs.identityPrincipalId diff --git a/infra/app/storekeys.bicep b/infra/app/storekeys.bicep new file mode 100644 index 000000000..fff777100 --- /dev/null +++ b/infra/app/storekeys.bicep @@ -0,0 +1,57 @@ +param keyVaultName string = '' +param storageAccountName string = '' +param azureOpenAIName string = '' +param azureCognitiveSearchName string = '' +param rgName string = '' +param formRecognizerName string = '' +param contentSafetyName string = '' + +resource storageAccountKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'storageAccountKey' + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + } +} + +resource openAIKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'openAIKey' + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + } +} + +resource searchKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'searchKey' + properties: { + value: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + } +} + +resource formRecognizerKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'formRecognizerKey' + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + } +} + +resource contentSafetyKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'contentSafetyKey' + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output CONTENT_SAFETY_KEY string = contentSafetyKeySecret.name +output FORM_RECOGNIZER_KEY string = formRecognizerKeySecret.name +output SEARCH_KEY string = searchKeySecret.name +output OPENAI_KEY string = openAIKeySecret.name +output STORAGE_ACCOUNT_KEY string = storageAccountKeySecret.name diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 6c77f8b53..74c9fa546 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -15,6 +15,14 @@ param contentSafetyName string = '' @secure() param appSettings object = {} param serviceName string = 'web' +param useKeyVault bool = true +param openAIKey string = '' +param storageAccountKey string = '' +param formRecognizerKey string = '' +param searchKey string = '' +param contentSafetyKey string = '' +@secure() +param keyVaultEndpoint string = '' module web '../core/host/appservice.bicep' = { name: '${name}-app-module' @@ -27,11 +35,13 @@ module web '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + USE_KEY_VAULT: useKeyVault ? useKeyVault : '' + AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' + AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName diff --git a/infra/core/security/keyvault-access.bicep b/infra/core/security/keyvault-access.bicep new file mode 100644 index 000000000..316775f21 --- /dev/null +++ b/infra/core/security/keyvault-access.bicep @@ -0,0 +1,22 @@ +metadata description = 'Assigns an Azure Key Vault access policy.' +param name string = 'add' + +param keyVaultName string +param permissions object = { secrets: [ 'get', 'list' ] } +param principalId string + +resource keyVaultAccessPolicies 'Microsoft.KeyVault/vaults/accessPolicies@2022-07-01' = { + parent: keyVault + name: name + properties: { + accessPolicies: [ { + objectId: principalId + tenantId: subscription().tenantId + permissions: permissions + } ] + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/infra/core/security/keyvault-secret.bicep b/infra/core/security/keyvault-secret.bicep new file mode 100644 index 000000000..7441b2961 --- /dev/null +++ b/infra/core/security/keyvault-secret.bicep @@ -0,0 +1,31 @@ +metadata description = 'Creates or updates a secret in an Azure Key Vault.' +param name string +param tags object = {} +param keyVaultName string +param contentType string = 'string' +@description('The value of the secret. Provide only derived values like blob storage access, but do not hard code any secrets in your templates') +@secure() +param secretValue string + +param enabled bool = true +param exp int = 0 +param nbf int = 0 + +resource keyVaultSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + name: name + tags: tags + parent: keyVault + properties: { + attributes: { + enabled: enabled + exp: exp + nbf: nbf + } + contentType: contentType + value: secretValue + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} diff --git a/infra/core/security/keyvault.bicep b/infra/core/security/keyvault.bicep new file mode 100644 index 000000000..314a1db61 --- /dev/null +++ b/infra/core/security/keyvault.bicep @@ -0,0 +1,26 @@ +metadata description = 'Creates an Azure Key Vault.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param principalId string = '' + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = { + name: name + location: location + tags: tags + properties: { + tenantId: subscription().tenantId + sku: { family: 'A', name: 'standard' } + accessPolicies: !empty(principalId) ? [ + { + objectId: principalId + permissions: { secrets: [ 'get', 'list' ] } + tenantId: subscription().tenantId + } + ] : [] + } +} + +output endpoint string = keyVault.properties.vaultUri +output name string = keyVault.name diff --git a/infra/core/security/registry-access.bicep b/infra/core/security/registry-access.bicep new file mode 100644 index 000000000..5335efabc --- /dev/null +++ b/infra/core/security/registry-access.bicep @@ -0,0 +1,19 @@ +metadata description = 'Assigns ACR Pull permissions to access an Azure Container Registry.' +param containerRegistryName string +param principalId string + +var acrPullRole = subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + +resource aksAcrPull 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: containerRegistry // Use when specifying a scope that is different than the deployment scope + name: guid(subscription().id, resourceGroup().id, principalId, acrPullRole) + properties: { + roleDefinitionId: acrPullRole + principalType: 'ServicePrincipal' + principalId: principalId + } +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' existing = { + name: containerRegistryName +} diff --git a/infra/core/security/role.bicep b/infra/core/security/role.bicep new file mode 100644 index 000000000..0b30cfd34 --- /dev/null +++ b/infra/core/security/role.bicep @@ -0,0 +1,21 @@ +metadata description = 'Creates a role assignment for a service principal.' +param principalId string + +@allowed([ + 'Device' + 'ForeignGroup' + 'Group' + 'ServicePrincipal' + 'User' +]) +param principalType string = 'ServicePrincipal' +param roleDefinitionId string + +resource role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(subscription().id, resourceGroup().id, principalId, roleDefinitionId) + properties: { + principalId: principalId + principalType: principalType + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitionId) + } +} diff --git a/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep deleted file mode 100644 index 4b6febbea..000000000 --- a/infra/core/storage/storage-account.bicep +++ /dev/null @@ -1,64 +0,0 @@ -metadata description = 'Creates an Azure storage account.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@allowed([ - 'Cool' - 'Hot' - 'Premium' ]) -param accessTier string = 'Hot' -param allowBlobPublicAccess bool = true -param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true -param containers array = [] -param defaultToOAuthAuthentication bool = false -param deleteRetentionPolicy object = {} -@allowed([ 'AzureDnsZone', 'Standard' ]) -param dnsEndpointType string = 'Standard' -param kind string = 'StorageV2' -param minimumTlsVersion string = 'TLS1_2' -param supportsHttpsTrafficOnly bool = true -param networkAcls object = { - bypass: 'AzureServices' - defaultAction: 'Allow' -} -@allowed([ 'Enabled', 'Disabled' ]) -param publicNetworkAccess string = 'Enabled' -param sku object = { name: 'Standard_LRS' } - -resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { - name: name - location: location - tags: tags - kind: kind - sku: sku - properties: { - accessTier: accessTier - allowBlobPublicAccess: allowBlobPublicAccess - allowCrossTenantReplication: allowCrossTenantReplication - allowSharedKeyAccess: allowSharedKeyAccess - defaultToOAuthAuthentication: defaultToOAuthAuthentication - dnsEndpointType: dnsEndpointType - minimumTlsVersion: minimumTlsVersion - networkAcls: networkAcls - publicNetworkAccess: publicNetworkAccess - supportsHttpsTrafficOnly: supportsHttpsTrafficOnly - } - - resource blobServices 'blobServices' = if (!empty(containers)) { - name: 'default' - properties: { - deleteRetentionPolicy: deleteRetentionPolicy - } - resource container 'containers' = [for container in containers: { - name: container.name - properties: { - publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' - } - }] - } -} - -output name string = storage.name -output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/infra/main.bicep b/infra/main.bicep index 3670f2d69..a3aa94b98 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -141,8 +141,13 @@ param formRecognizerName string = '${environmentName}-formrecog-${resourceToken} @description('Azure Content Safety Name') param contentSafetyName string = '${environmentName}-contentsafety-${resourceToken}' + param newGuidString string = newGuid() param searchTag string = 'chatwithyourdata-sa' +param useKeyVault bool = true + +@description('Id of the user or app to assign application roles') +param principalId string = '' var blobContainerName = 'documents' var queueName = 'doc-processing' @@ -222,6 +227,59 @@ module hostingplan './core/host/appserviceplan.bicep' = { } } +module storekeys './app/storekeys.bicep' = if(useKeyVault){ + name: 'storekeys' + scope: rg + params: { + keyVaultName: keyvault.outputs.name + azureOpenAIName: openai.outputs.name + azureCognitiveSearchName: search.outputs.name + storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + formRecognizerName: formrecognizer.outputs.name + contentSafetyName: contentsafety.outputs.name + rgName: rgName + } +} + +// Store secrets in a keyvault +module keyvault './core/security/keyvault.bicep' = if(useKeyVault){ + name: 'keyvault' + scope: rg + params: { + name: 'kv-${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +module webaccess './core/security/keyvault-access.bicep' = if(useKeyVault){ + name: 'web-keyvault-access' + scope: rg + params: { + keyVaultName: keyvault.outputs.name + principalId: web.outputs.FRONTEND_API_IDENTITY_PRINCIPAL_ID + } +} + +module adminwebaccess './core/security/keyvault-access.bicep' = if(useKeyVault){ + name: 'adminweb-keyvault-access' + scope: rg + params: { + keyVaultName: keyvault.outputs.name + principalId: adminweb.outputs.WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID + } +} + +module functionaccess './core/security/keyvault-access.bicep' = if(useKeyVault){ + name: 'function-keyvault-access' + scope: rg + params: { + keyVaultName: keyvault.outputs.name + principalId: function.outputs.FUNCTION_IDENTITY_PRINCIPAL_ID + } +} + module web './app/web.bicep' = { name: websiteName scope: rg @@ -237,6 +295,14 @@ module web './app/web.bicep' = { storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name + openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' + storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' + formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' + searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' + contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + useKeyVault: useKeyVault + keyVaultName: keyvault.outputs.name + keyVaultEndpoint: keyvault.outputs.endpoint appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -285,6 +351,14 @@ module adminweb './app/adminweb.bicep' = { storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name + openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' + storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' + formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' + searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' + contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + useKeyVault: useKeyVault + keyVaultName: keyvault.outputs.name + keyVaultEndpoint: keyvault.outputs.endpoint appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -350,6 +424,14 @@ module function './app/function.bicep' = { formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name clientKey: clientKey + openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' + storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' + formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' + searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' + contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + useKeyVault: useKeyVault + keyVaultName: keyvault.outputs.name + keyVaultEndpoint: keyvault.outputs.endpoint appSettings: { FUNCTIONS_EXTENSION_VERSION: '~4' WEBSITES_ENABLE_APP_SERVICE_STORAGE: 'false' @@ -413,3 +495,19 @@ module storage 'app/storage.bicep' = { blobContainerName: blobContainerName } } + +output AZURE_KEY_VAULT_ENDPOINT string = keyvault.outputs.endpoint +output AZURE_KEY_VAULT_NAME string = keyvault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output USE_KEY_VAULT bool = useKeyVault +output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString +output APPINSIGHTS_INSTRUMENTATIONKEY string = monitoring.outputs.applicationInsightsInstrumentationKey +output AZURE_CONTENT_SAFETY_ENDPOINT string = contentsafety.outputs.endpoint +output AZURE_SEARCH_SERVICE string = search.outputs.endpoint +output AZURE_FORM_RECOGNIZER_ENDPOINT string = formrecognizer.outputs.endpoint +output OPENAI_KEY string = useKeyVault ? storekeys.outputs.OPENAI_KEY : '' +output STORAGE_ACCOUNT_KEY string = useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' +output FORM_RECOGNIZER_KEY string = useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' +output SEARCH_KEY string = useKeyVault ? storekeys.outputs.SEARCH_KEY: '' +output CONTENT_SAFETY_KEY string = useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 96a8c2486..9a48ea283 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -3,3 +3,7 @@ using './main.bicep' param environmentName = readEnvironmentVariable('AZURE_ENV_NAME') param location = readEnvironmentVariable('AZURE_LOCATION') + +param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID') + +param useKeyVault = readEnvironmentVariable('USE_KEY_VAULT') diff --git a/requirements.txt b/requirements.txt index c4c4e8940..67dddebb3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,5 @@ chardet==5.1.0 azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 azure-ai-contentsafety==1.0.0b1 -python-docx==0.8.11 \ No newline at end of file +python-docx==0.8.11 +azure-keyvault-secrets == 4.4.* \ No newline at end of file From cc3e957ab7cb1e30375a049b97fa8b9427dd076a Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 24 Nov 2023 00:25:25 +0800 Subject: [PATCH 15/37] set default value in .bicepparam --- infra/main.bicepparam | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 9a48ea283..3c9f3f14e 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -1,9 +1,9 @@ using './main.bicep' -param environmentName = readEnvironmentVariable('AZURE_ENV_NAME') +param environmentName = readEnvironmentVariable('AZURE_ENV_NAME', 'env_name') -param location = readEnvironmentVariable('AZURE_LOCATION') +param location = readEnvironmentVariable('AZURE_LOCATION', 'location') -param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID') +param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id') -param useKeyVault = readEnvironmentVariable('USE_KEY_VAULT') +param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) From 9dc6a781e22d40f743c175ef774c462772f1b058 Mon Sep 17 00:00:00 2001 From: zedy Date: Tue, 28 Nov 2023 10:12:04 +0800 Subject: [PATCH 16/37] fix some bugs --- app.py | 6 +- backend/pages/01_Ingest_Data.py | 5 +- backend/requirements.txt | 2 +- backend/utilities/helpers/EnvHelper.py | 5 +- infra/app/adminweb.bicep | 2 +- infra/app/function.bicep | 2 +- infra/app/storekeys.bicep | 16 ++++-- infra/app/web.bicep | 2 +- infra/main.bicep | 77 +++++++++++++++++--------- infra/main.bicepparam | 2 +- requirements.txt | 2 +- 11 files changed, 78 insertions(+), 43 deletions(-) diff --git a/app.py b/app.py index 49728a9e4..62537cd4c 100644 --- a/app.py +++ b/app.py @@ -25,8 +25,10 @@ def static_file(path): return app.send_static_file(path) -credential = DefaultAzureCredential() -secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) +if os.environ.get("USE_KEY_VAULT"): + credential = DefaultAzureCredential() + secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + # ACS Integration Settings AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") diff --git a/backend/pages/01_Ingest_Data.py b/backend/pages/01_Ingest_Data.py index eee40bb89..78b97b71d 100644 --- a/backend/pages/01_Ingest_Data.py +++ b/backend/pages/01_Ingest_Data.py @@ -67,8 +67,9 @@ def upload_file(bytes_data: bytes, file_name: str, content_type: Optional[str] = charset = f"; charset={chardet.detect(bytes_data)['encoding']}" if content_type == 'text/plain' else '' content_type = content_type if content_type != None else 'text/plain' account_name = os.getenv('AZURE_BLOB_ACCOUNT_NAME') - credential = DefaultAzureCredential() - secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + if os.environ.get("USE_KEY_VAULT"): + credential = DefaultAzureCredential() + secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) account_key = secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY")).value if os.getenv("USE_KEY_VAULT") else os.getenv("AZURE_BLOB_ACCOUNT_KEY") container_name = os.getenv('AZURE_BLOB_CONTAINER_NAME') if account_name == None or account_key == None or container_name == None: diff --git a/backend/requirements.txt b/backend/requirements.txt index f1c9499b2..eb143db62 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -24,4 +24,4 @@ azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 pandas==1.5.1 python-docx==0.8.11 -azure-keyvault-secrets == 4.4.* \ No newline at end of file +azure-keyvault-secrets==4.4.* \ No newline at end of file diff --git a/backend/utilities/helpers/EnvHelper.py b/backend/utilities/helpers/EnvHelper.py index 771a86152..e340b1e64 100644 --- a/backend/utilities/helpers/EnvHelper.py +++ b/backend/utilities/helpers/EnvHelper.py @@ -9,8 +9,9 @@ class EnvHelper: def __init__(self, **kwargs) -> None: load_dotenv() - credential = DefaultAzureCredential() - secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + if os.environ.get("USE_KEY_VAULT"): + credential = DefaultAzureCredential() + secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) # Azure Search self.AZURE_SEARCH_SERVICE = os.getenv('AZURE_SEARCH_SERVICE', '') self.AZURE_SEARCH_INDEX = os.getenv('AZURE_SEARCH_INDEX', '') diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 232fe34e9..0204ad31f 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -15,7 +15,7 @@ param azureCognitiveSearchName string = '' @secure() param appSettings object = {} param serviceName string = 'adminweb' -param useKeyVault bool = true +param useKeyVault bool param openAIKey string = '' param storageAccountKey string = '' param formRecognizerKey string = '' diff --git a/infra/app/function.bicep b/infra/app/function.bicep index 7f6df0e6c..98256af0a 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -16,7 +16,7 @@ param azureCognitiveSearchName string = '' param rgName string = '' param formRecognizerName string = '' param contentSafetyName string = '' -param useKeyVault bool = true +param useKeyVault bool param openAIKey string = '' param storageAccountKey string = '' param formRecognizerKey string = '' diff --git a/infra/app/storekeys.bicep b/infra/app/storekeys.bicep index fff777100..d4001d9fc 100644 --- a/infra/app/storekeys.bicep +++ b/infra/app/storekeys.bicep @@ -5,10 +5,16 @@ param azureCognitiveSearchName string = '' param rgName string = '' param formRecognizerName string = '' param contentSafetyName string = '' +param storageAccountKeyName string = 'AZURE-STORAGE-ACCOUNT-KEY' +param openAIKeyName string = 'AZURE-OPEN-AI-KEY' +param searchKeyName string = 'AZURE-SEARCH-KEY' +param formRecognizerKeyName string = 'AZURE-FORM-RECOGNIZER-KEY' +param contentSafety string = 'AZURE-CONTENT-SAFETY-KEY' + resource storageAccountKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault - name: 'storageAccountKey' + name: storageAccountKeyName properties: { value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value } @@ -16,7 +22,7 @@ resource storageAccountKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' resource openAIKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault - name: 'openAIKey' + name: openAIKeyName properties: { value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 } @@ -24,7 +30,7 @@ resource openAIKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { resource searchKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault - name: 'searchKey' + name: searchKeyName properties: { value: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey } @@ -32,7 +38,7 @@ resource searchKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { resource formRecognizerKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault - name: 'formRecognizerKey' + name: formRecognizerKeyName properties: { value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 } @@ -40,7 +46,7 @@ resource formRecognizerKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' resource contentSafetyKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault - name: 'contentSafetyKey' + name: contentSafety properties: { value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 } diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 74c9fa546..485964eae 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -15,7 +15,7 @@ param contentSafetyName string = '' @secure() param appSettings object = {} param serviceName string = 'web' -param useKeyVault bool = true +param useKeyVault bool param openAIKey string = '' param storageAccountKey string = '' param formRecognizerKey string = '' diff --git a/infra/main.bicep b/infra/main.bicep index a3aa94b98..ec86ef413 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -144,7 +144,7 @@ param contentSafetyName string = '${environmentName}-contentsafety-${resourceTok param newGuidString string = newGuid() param searchTag string = 'chatwithyourdata-sa' -param useKeyVault bool = true +param useKeyVault bool @description('Id of the user or app to assign application roles') param principalId string = '' @@ -155,6 +155,7 @@ var clientKey = '${uniqueString(guid(subscription().id, deployment().name))}${ne var eventGridSystemTopicName = 'doc-processing' var tags = { 'azd-env-name': environmentName } var rgName = 'rg-${environmentName}' +var keyVaultName = 'kv-${resourceToken}' // Organize resources in a resource group resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { @@ -227,11 +228,11 @@ module hostingplan './core/host/appserviceplan.bicep' = { } } -module storekeys './app/storekeys.bicep' = if(useKeyVault){ +module storekeys './app/storekeys.bicep' = if (useKeyVault) { name: 'storekeys' scope: rg params: { - keyVaultName: keyvault.outputs.name + keyVaultName: keyVaultName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME @@ -242,7 +243,7 @@ module storekeys './app/storekeys.bicep' = if(useKeyVault){ } // Store secrets in a keyvault -module keyvault './core/security/keyvault.bicep' = if(useKeyVault){ +module keyvault './core/security/keyvault.bicep' = if (useKeyVault) { name: 'keyvault' scope: rg params: { @@ -253,29 +254,29 @@ module keyvault './core/security/keyvault.bicep' = if(useKeyVault){ } } -module webaccess './core/security/keyvault-access.bicep' = if(useKeyVault){ +module webaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { name: 'web-keyvault-access' scope: rg params: { - keyVaultName: keyvault.outputs.name + keyVaultName: keyVaultName principalId: web.outputs.FRONTEND_API_IDENTITY_PRINCIPAL_ID } } -module adminwebaccess './core/security/keyvault-access.bicep' = if(useKeyVault){ +module adminwebaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { name: 'adminweb-keyvault-access' scope: rg params: { - keyVaultName: keyvault.outputs.name + keyVaultName: keyVaultName principalId: adminweb.outputs.WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID } } -module functionaccess './core/security/keyvault-access.bicep' = if(useKeyVault){ +module functionaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { name: 'function-keyvault-access' scope: rg params: { - keyVaultName: keyvault.outputs.name + keyVaultName: keyVaultName principalId: function.outputs.FUNCTION_IDENTITY_PRINCIPAL_ID } } @@ -301,8 +302,8 @@ module web './app/web.bicep' = { searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' useKeyVault: useKeyVault - keyVaultName: keyvault.outputs.name - keyVaultEndpoint: keyvault.outputs.endpoint + keyVaultName: useKeyVault ? keyvault.outputs.name : '' + keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -357,8 +358,8 @@ module adminweb './app/adminweb.bicep' = { searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' useKeyVault: useKeyVault - keyVaultName: keyvault.outputs.name - keyVaultEndpoint: keyvault.outputs.endpoint + keyVaultName: useKeyVault ? keyvault.outputs.name : '' + keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -430,8 +431,8 @@ module function './app/function.bicep' = { searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' useKeyVault: useKeyVault - keyVaultName: keyvault.outputs.name - keyVaultEndpoint: keyvault.outputs.endpoint + keyVaultName: useKeyVault ? keyvault.outputs.name : '' + keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' appSettings: { FUNCTIONS_EXTENSION_VERSION: '~4' WEBSITES_ENABLE_APP_SERVICE_STORAGE: 'false' @@ -496,18 +497,42 @@ module storage 'app/storage.bicep' = { } } -output AZURE_KEY_VAULT_ENDPOINT string = keyvault.outputs.endpoint -output AZURE_KEY_VAULT_NAME string = keyvault.outputs.name -output AZURE_LOCATION string = location -output AZURE_TENANT_ID string = tenant().tenantId -output USE_KEY_VAULT bool = useKeyVault output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString output APPINSIGHTS_INSTRUMENTATIONKEY string = monitoring.outputs.applicationInsightsInstrumentationKey +output AZURE_KEY_VAULT_ENDPOINT string = useKeyVault ? keyvault.outputs.endpoint : '' +output AZURE_KEY_VAULT_NAME string = useKeyVault ? keyvault.outputs.name : '' +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId output AZURE_CONTENT_SAFETY_ENDPOINT string = contentsafety.outputs.endpoint output AZURE_SEARCH_SERVICE string = search.outputs.endpoint output AZURE_FORM_RECOGNIZER_ENDPOINT string = formrecognizer.outputs.endpoint -output OPENAI_KEY string = useKeyVault ? storekeys.outputs.OPENAI_KEY : '' -output STORAGE_ACCOUNT_KEY string = useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' -output FORM_RECOGNIZER_KEY string = useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' -output SEARCH_KEY string = useKeyVault ? storekeys.outputs.SEARCH_KEY: '' -output CONTENT_SAFETY_KEY string = useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' +output AZURE_SEARCH_USE_SEMANTIC_SEARCH string = azureSearchUseSemanticSearch +output AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG string = azureSearchSemanticSearchConfig +output AZURE_SEARCH_INDEX_IS_PRECHUNKED string = azureSearchIndexIsPrechunked +output AZURE_SEARCH_TOP_K string = azureSearchTopK +output AZURE_SEARCH_ENABLE_IN_DOMAIN string = azureSearchEnableInDomain +output AZURE_SEARCH_CONTENT_COLUMNS string = azureSearchContentColumns +output AZURE_SEARCH_FILENAME_COLUMN string = azureSearchFilenameColumn +output AZURE_SEARCH_TITLE_COLUMN string = azureSearchTitleColumn +output AZURE_SEARCH_URL_COLUMN string = azureSearchUrlColumn +output AZURE_OPENAI_MODEL_NAME string = azureOpenAIModelName +output AZURE_OPENAI_STREAM string = azureOpenAIStream +output AZURE_OPENAI_SYSTEM_MESSAGE string = azureOpenAISystemMessage +output AZURE_OPENAI_STOP_SEQUENCE string = azureOpenAIStopSequence +output AZURE_OPENAI_MAX_TOKENS string = azureOpenAIMaxTokens +output AZURE_OPENAI_TOP_P string = azureOpenAITopP +output AZURE_OPENAI_TEMPERATURE string = azureOpenAITemperature +output AZURE_SEARCH_INDEX string = azureSearchIndex +output AZURE_OPENAI_API_VERSION string = azureOpenAIApiVersion +output DOCUMENT_PROCESSING_QUEUE_NAME string = queueName +output AZURE_BLOB_CONTAINER_NAME string = blobContainerName +output AZURE_BLOB_ACCOUNT_NAME string = storageAccountName +output AZURE_OPENAI_RESOURCE string = azureOpenAIResourceName +output AZURE_OPENAI_EMBEDDING_MODEL string = azureOpenAIEmbeddingModel +output AZURE_OPENAI_MODEL string = azureOpenAIModel +output USE_KEY_VAULT bool = useKeyVault +output AZURE_OPENAI_KEY string = useKeyVault ? storekeys.outputs.OPENAI_KEY : '' +output AZURE_BLOB_ACCOUNT_KEY string = useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' +output AZURE_FORM_RECOGNIZER_KEY string = useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' +output AZURE_SEARCH_KEY string = useKeyVault ? storekeys.outputs.SEARCH_KEY: '' +output AZURE_CONTENT_SAFETY_KEY string = useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 3c9f3f14e..e5b1584f1 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -6,4 +6,4 @@ param location = readEnvironmentVariable('AZURE_LOCATION', 'location') param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id') -param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) +param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'false')) diff --git a/requirements.txt b/requirements.txt index 67dddebb3..8ca241a72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,4 @@ azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 azure-ai-contentsafety==1.0.0b1 python-docx==0.8.11 -azure-keyvault-secrets == 4.4.* \ No newline at end of file +azure-keyvault-secrets==4.4.* \ No newline at end of file From 9bcb2a7f2e26c524d2adcf0af0e9b8a71253e8fb Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 14 Dec 2023 17:53:00 +0800 Subject: [PATCH 17/37] update rbac --- app.py | 3 +- backend/pages/01_Ingest_Data.py | 32 ++++++++++++---- .../helpers/AzureBlobStorageHelper.py | 37 +++++++++++++------ backend/utilities/helpers/EnvHelper.py | 11 +++--- infra/main.bicepparam | 2 +- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/app.py b/app.py index 62537cd4c..1a3c4d88a 100644 --- a/app.py +++ b/app.py @@ -28,6 +28,7 @@ def static_file(path): if os.environ.get("USE_KEY_VAULT"): credential = DefaultAzureCredential() secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + openai_token = credential.get_token("https://cognitiveservices.azure.com/.default") # ACS Integration Settings AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") @@ -45,7 +46,7 @@ def static_file(path): # AOAI Integration Settings AZURE_OPENAI_RESOURCE = os.environ.get("AZURE_OPENAI_RESOURCE") AZURE_OPENAI_MODEL = os.environ.get("AZURE_OPENAI_MODEL") -AZURE_OPENAI_KEY = secret_client.get_secret(os.environ.get("AZURE_OPENAI_KEY")).value if os.environ.get("USE_KEY_VAULT") else os.environ.get("AZURE_OPENAI_KEY") +AZURE_OPENAI_KEY = openai_token.token AZURE_OPENAI_TEMPERATURE = os.environ.get("AZURE_OPENAI_TEMPERATURE", 0) AZURE_OPENAI_TOP_P = os.environ.get("AZURE_OPENAI_TOP_P", 1.0) AZURE_OPENAI_MAX_TOKENS = os.environ.get("AZURE_OPENAI_MAX_TOKENS", 1000) diff --git a/backend/pages/01_Ingest_Data.py b/backend/pages/01_Ingest_Data.py index 78b97b71d..a1e928590 100644 --- a/backend/pages/01_Ingest_Data.py +++ b/backend/pages/01_Ingest_Data.py @@ -7,12 +7,13 @@ from datetime import datetime, timedelta import logging import requests -from azure.storage.blob import BlobServiceClient, generate_blob_sas, ContentSettings +from azure.storage.blob import BlobServiceClient, generate_blob_sas, ContentSettings, UserDelegationKey import urllib.parse from utilities.helpers.ConfigHelper import ConfigHelper from dotenv import load_dotenv from azure.identity import DefaultAzureCredential from azure.keyvault.secrets import SecretClient +from datetime import datetime, timedelta load_dotenv() logger = logging.getLogger('azure.core.pipeline.policies.http_logging_policy').setLevel(logging.WARNING) @@ -26,6 +27,18 @@ """ st.markdown(mod_page_style, unsafe_allow_html=True) +def request_user_delegation_key(blob_service_client: BlobServiceClient) -> UserDelegationKey: + # Get a user delegation key that's valid for 1 day + delegation_key_start_time = datetime.utcnow() + delegation_key_expiry_time = delegation_key_start_time + timedelta(days=1) + + user_delegation_key = blob_service_client.get_user_delegation_key( + key_start_time=delegation_key_start_time, + key_expiry_time=delegation_key_expiry_time + ) + + return user_delegation_key + def remote_convert_files_and_add_embeddings(process_all=False): backend_url = urllib.parse.urljoin(os.getenv('BACKEND_URL','http://localhost:7071'), "/api/BatchStartProcessing") params = {} @@ -69,19 +82,22 @@ def upload_file(bytes_data: bytes, file_name: str, content_type: Optional[str] = account_name = os.getenv('AZURE_BLOB_ACCOUNT_NAME') if os.environ.get("USE_KEY_VAULT"): credential = DefaultAzureCredential() - secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) - account_key = secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY")).value if os.getenv("USE_KEY_VAULT") else os.getenv("AZURE_BLOB_ACCOUNT_KEY") + # secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + blob_service_client = BlobServiceClient(account_url='', credential=credential) + user_delegation_key = request_user_delegation_key(blob_service_client=blob_service_client) + # account_key = secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY")).value if os.getenv("USE_KEY_VAULT") else os.getenv("AZURE_BLOB_ACCOUNT_KEY") container_name = os.getenv('AZURE_BLOB_CONTAINER_NAME') - if account_name == None or account_key == None or container_name == None: - raise ValueError("Please provide values for AZURE_BLOB_ACCOUNT_NAME, AZURE_BLOB_ACCOUNT_KEY and AZURE_BLOB_CONTAINER_NAME") - connect_str = f"DefaultEndpointsProtocol=https;AccountName={account_name};AccountKey={account_key};EndpointSuffix=core.windows.net" - blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(connect_str) + # if account_name == None or account_key == None or container_name == None: + # raise ValueError("Please provide values for AZURE_BLOB_ACCOUNT_NAME, AZURE_BLOB_ACCOUNT_KEY and AZURE_BLOB_CONTAINER_NAME") + # connect_str = f"DefaultEndpointsProtocol=https;AccountName={account_name};AccountKey={account_key};EndpointSuffix=core.windows.net" + # blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(connect_str) + # Create a blob client using the local file name as the name for the blob blob_client = blob_service_client.get_blob_client(container=container_name, blob=file_name) # Upload the created file blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type+charset)) # Generate a SAS URL to the blob and return it - st.session_state['file_url'] = blob_client.url + '?' + generate_blob_sas(account_name, container_name, file_name,account_key=account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + st.session_state['file_url'] = blob_client.url + '?' + generate_blob_sas(account_name, container_name, file_name,user_delegation_key=user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) try: with st.expander("Add documents in Batch", expanded=True): diff --git a/backend/utilities/helpers/AzureBlobStorageHelper.py b/backend/utilities/helpers/AzureBlobStorageHelper.py index 72374a8ca..148bfaf79 100644 --- a/backend/utilities/helpers/AzureBlobStorageHelper.py +++ b/backend/utilities/helpers/AzureBlobStorageHelper.py @@ -1,7 +1,9 @@ from typing import Optional from datetime import datetime, timedelta -from azure.storage.blob import BlobServiceClient, generate_blob_sas, generate_container_sas, ContentSettings +from azure.storage.blob import BlobServiceClient, generate_blob_sas, generate_container_sas, ContentSettings, UserDelegationKey from .EnvHelper import EnvHelper +from datetime import datetime, timedelta +from azure.identity import DefaultAzureCredential class AzureBlobStorageClient: def __init__(self, account_name: Optional[str] = None, account_key: Optional[str] = None, container_name: Optional[str] = None): @@ -9,19 +11,32 @@ def __init__(self, account_name: Optional[str] = None, account_key: Optional[str env_helper : EnvHelper = EnvHelper() self.account_name = account_name if account_name else env_helper.AZURE_BLOB_ACCOUNT_NAME - self.account_key = account_key if account_key else env_helper.AZURE_BLOB_ACCOUNT_KEY - self.connect_str = f"DefaultEndpointsProtocol=https;AccountName={self.account_name};AccountKey={self.account_key};EndpointSuffix=core.windows.net" + # self.account_key = account_key if account_key else env_helper.AZURE_BLOB_ACCOUNT_KEY + # self.connect_str = f"DefaultEndpointsProtocol=https;AccountName={self.account_name};AccountKey={self.account_key};EndpointSuffix=core.windows.net" self.container_name : str = container_name if container_name else env_helper.AZURE_BLOB_CONTAINER_NAME - self.blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(self.connect_str) - - + # self.blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(self.connect_str) + credential = DefaultAzureCredential() + self.blob_service_client = BlobServiceClient(account_url='', credential=credential) + self.user_delegation_key = self.request_user_delegation_key(blob_service_client=self.blob_service_client) + + def request_user_delegation_key(blob_service_client: BlobServiceClient) -> UserDelegationKey: + # Get a user delegation key that's valid for 1 day + delegation_key_start_time = datetime.utcnow() + delegation_key_expiry_time = delegation_key_start_time + timedelta(days=1) + + user_delegation_key = blob_service_client.get_user_delegation_key( + key_start_time=delegation_key_start_time, + key_expiry_time=delegation_key_expiry_time + ) + return user_delegation_key + def upload_file(self, bytes_data, file_name, content_type='application/pdf'): # Create a blob client using the local file name as the name for the blob blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) # Upload the created file blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type)) # Generate a SAS URL to the blob and return it - return blob_client.url + '?' + generate_blob_sas(self.account_name, self.container_name, file_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + return blob_client.url + '?' + generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) def download_file(self, file_name): blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) @@ -45,7 +60,7 @@ def get_all_files(self): container_client = self.blob_service_client.get_container_client(self.container_name) blob_list = container_client.list_blobs(include='metadata') # sas = generate_blob_sas(account_name, container_name, blob.name,account_key=account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - sas = generate_container_sas(self.account_name, self.container_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + sas = generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) files = [] converted_files = {} for blob in blob_list: @@ -70,7 +85,7 @@ def get_all_files(self): return files def upsert_blob_metadata(self, file_name, metadata): - blob_client = BlobServiceClient.from_connection_string(self.connect_str).get_blob_client(container=self.container_name, blob=file_name) + blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) # Read metadata from the blob blob_metadata = blob_client.get_blob_properties().metadata # Update metadata @@ -80,8 +95,8 @@ def upsert_blob_metadata(self, file_name, metadata): def get_container_sas(self): # Generate a SAS URL to the container and return it - return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) + return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) def get_blob_sas(self, file_name): # Generate a SAS URL to the blob and return it - return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, account_key= self.account_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) + return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, user_delegation_key= self.user_delegation_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) diff --git a/backend/utilities/helpers/EnvHelper.py b/backend/utilities/helpers/EnvHelper.py index e340b1e64..9404e30d6 100644 --- a/backend/utilities/helpers/EnvHelper.py +++ b/backend/utilities/helpers/EnvHelper.py @@ -12,6 +12,7 @@ def __init__(self, **kwargs) -> None: if os.environ.get("USE_KEY_VAULT"): credential = DefaultAzureCredential() secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + openai_token = credential.get_token("https://cognitiveservices.azure.com/.default") # Azure Search self.AZURE_SEARCH_SERVICE = os.getenv('AZURE_SEARCH_SERVICE', '') self.AZURE_SEARCH_INDEX = os.getenv('AZURE_SEARCH_INDEX', '') @@ -34,7 +35,7 @@ def __init__(self, **kwargs) -> None: # Azure OpenAI self.AZURE_OPENAI_RESOURCE = os.getenv('AZURE_OPENAI_RESOURCE', '') self.AZURE_OPENAI_MODEL = os.getenv('AZURE_OPENAI_MODEL', '') - self.AZURE_OPENAI_KEY = secret_client.get_secret(os.getenv("AZURE_OPENAI_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_OPENAI_KEY", '') + self.AZURE_OPENAI_KEY = openai_token.token self.AZURE_OPENAI_MODEL_NAME = os.getenv('AZURE_OPENAI_MODEL_NAME', '') self.AZURE_OPENAI_TEMPERATURE = os.getenv('AZURE_OPENAI_TEMPERATURE', '') self.AZURE_OPENAI_TOP_P = os.getenv('AZURE_OPENAI_TOP_P', '') @@ -45,11 +46,11 @@ def __init__(self, **kwargs) -> None: self.AZURE_OPENAI_STREAM = os.getenv('AZURE_OPENAI_STREAM', '') self.AZURE_OPENAI_EMBEDDING_MODEL = os.getenv('AZURE_OPENAI_EMBEDDING_MODEL', '') # Set env for OpenAI SDK - self.OPENAI_API_TYPE = "azure" + self.OPENAI_API_TYPE = "azuread" self.OPENAI_API_BASE = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" self.OPENAI_API_KEY = self.AZURE_OPENAI_KEY self.OPENAI_API_VERSION = self.AZURE_OPENAI_API_VERSION - os.environ["OPENAI_API_TYPE"] = "azure" + os.environ["OPENAI_API_TYPE"] = "azuread" os.environ["OPENAI_API_BASE"] = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" os.environ["OPENAI_API_KEY"] = self.AZURE_OPENAI_KEY os.environ["OPENAI_API_VERSION"] = self.AZURE_OPENAI_API_VERSION @@ -63,12 +64,12 @@ def __init__(self, **kwargs) -> None: self.AZURE_BLOB_CONTAINER_NAME = os.getenv('AZURE_BLOB_CONTAINER_NAME', '') # Azure Form Recognizer self.AZURE_FORM_RECOGNIZER_ENDPOINT = os.getenv('AZURE_FORM_RECOGNIZER_ENDPOINT', '') - self.AZURE_FORM_RECOGNIZER_KEY = secret_client.get_secret(os.getenv("AZURE_FORM_RECOGNIZER_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_FORM_RECOGNIZER_KEY", '') + self.AZURE_FORM_RECOGNIZER_KEY = openai_token.token # Azure App Insights self.APPINSIGHTS_CONNECTION_STRING = os.getenv('APPINSIGHTS_CONNECTION_STRING', '') # Azure AI Content Safety self.AZURE_CONTENT_SAFETY_ENDPOINT = os.getenv('AZURE_CONTENT_SAFETY_ENDPOINT', '') - self.AZURE_CONTENT_SAFETY_KEY = secret_client.get_secret(os.getenv("AZURE_CONTENT_SAFETY_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_CONTENT_SAFETY_KEY", '') + self.AZURE_CONTENT_SAFETY_KEY = openai_token.token # Orchestration Settings self.ORCHESTRATION_STRATEGY = os.getenv('ORCHESTRATION_STRATEGY', 'openai_function') diff --git a/infra/main.bicepparam b/infra/main.bicepparam index e5b1584f1..3c9f3f14e 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -6,4 +6,4 @@ param location = readEnvironmentVariable('AZURE_LOCATION', 'location') param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id') -param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'false')) +param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) From 1a9212b9a56efaaba6fe1cb3ecba60a4c7ea94d5 Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 21 Dec 2023 15:17:22 +0800 Subject: [PATCH 18/37] update rbac --- app.py | 11 +- backend/pages/01_Ingest_Data.py | 39 +-- backend/requirements.txt | 7 +- .../helpers/AzureBlobStorageHelper.py | 53 +++- .../helpers/AzureFormRecognizerHelper.py | 15 +- backend/utilities/helpers/EnvHelper.py | 23 +- backend/utilities/helpers/LLMHelper.py | 2 +- .../utilities/tools/ContentSafetyChecker.py | 17 +- infra/app/adminweb.bicep | 21 +- infra/app/function.bicep | 21 +- infra/app/storekeys.bicep | 63 ----- infra/app/web.bicep | 23 +- infra/main.bicep | 245 ++++++++++++------ infra/main.bicepparam | 2 + requirements.txt | 7 +- 15 files changed, 296 insertions(+), 253 deletions(-) delete mode 100644 infra/app/storekeys.bicep diff --git a/app.py b/app.py index 1a3c4d88a..ff907c93f 100644 --- a/app.py +++ b/app.py @@ -14,7 +14,6 @@ from dotenv import load_dotenv from backend.utilities.QuestionHandler import QuestionHandler from azure.identity import DefaultAzureCredential -from azure.keyvault.secrets import SecretClient load_dotenv() @@ -25,15 +24,17 @@ def static_file(path): return app.send_static_file(path) -if os.environ.get("USE_KEY_VAULT"): +USE_RBAC = False + +if os.environ.get("AUTH_TYPE") == 'rbac': + USE_RBAC = True credential = DefaultAzureCredential() - secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) openai_token = credential.get_token("https://cognitiveservices.azure.com/.default") # ACS Integration Settings AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") -AZURE_SEARCH_KEY = secret_client.get_secret(os.environ.get("AZURE_SEARCH_KEY")).value if os.environ.get("USE_KEY_VAULT") else os.environ.get("AZURE_SEARCH_KEY") +AZURE_SEARCH_KEY = None if USE_RBAC else os.environ.get("AZURE_SEARCH_KEY") AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.environ.get("AZURE_SEARCH_USE_SEMANTIC_SEARCH", False) AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.environ.get("AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG", "default") AZURE_SEARCH_TOP_K = os.environ.get("AZURE_SEARCH_TOP_K", 5) @@ -46,7 +47,7 @@ def static_file(path): # AOAI Integration Settings AZURE_OPENAI_RESOURCE = os.environ.get("AZURE_OPENAI_RESOURCE") AZURE_OPENAI_MODEL = os.environ.get("AZURE_OPENAI_MODEL") -AZURE_OPENAI_KEY = openai_token.token +AZURE_OPENAI_KEY = openai_token.token if USE_RBAC else os.environ.get("AZURE_OPENAI_KEY") AZURE_OPENAI_TEMPERATURE = os.environ.get("AZURE_OPENAI_TEMPERATURE", 0) AZURE_OPENAI_TOP_P = os.environ.get("AZURE_OPENAI_TOP_P", 1.0) AZURE_OPENAI_MAX_TOKENS = os.environ.get("AZURE_OPENAI_MAX_TOKENS", 1000) diff --git a/backend/pages/01_Ingest_Data.py b/backend/pages/01_Ingest_Data.py index a1e928590..6c537019a 100644 --- a/backend/pages/01_Ingest_Data.py +++ b/backend/pages/01_Ingest_Data.py @@ -12,7 +12,6 @@ from utilities.helpers.ConfigHelper import ConfigHelper from dotenv import load_dotenv from azure.identity import DefaultAzureCredential -from azure.keyvault.secrets import SecretClient from datetime import datetime, timedelta load_dotenv() @@ -80,24 +79,28 @@ def upload_file(bytes_data: bytes, file_name: str, content_type: Optional[str] = charset = f"; charset={chardet.detect(bytes_data)['encoding']}" if content_type == 'text/plain' else '' content_type = content_type if content_type != None else 'text/plain' account_name = os.getenv('AZURE_BLOB_ACCOUNT_NAME') - if os.environ.get("USE_KEY_VAULT"): + if os.environ.get("AUTH_TYPE") == 'rbac': credential = DefaultAzureCredential() - # secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) - blob_service_client = BlobServiceClient(account_url='', credential=credential) - user_delegation_key = request_user_delegation_key(blob_service_client=blob_service_client) - # account_key = secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY")).value if os.getenv("USE_KEY_VAULT") else os.getenv("AZURE_BLOB_ACCOUNT_KEY") - container_name = os.getenv('AZURE_BLOB_CONTAINER_NAME') - # if account_name == None or account_key == None or container_name == None: - # raise ValueError("Please provide values for AZURE_BLOB_ACCOUNT_NAME, AZURE_BLOB_ACCOUNT_KEY and AZURE_BLOB_CONTAINER_NAME") - # connect_str = f"DefaultEndpointsProtocol=https;AccountName={account_name};AccountKey={account_key};EndpointSuffix=core.windows.net" - # blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(connect_str) - - # Create a blob client using the local file name as the name for the blob - blob_client = blob_service_client.get_blob_client(container=container_name, blob=file_name) - # Upload the created file - blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type+charset)) - # Generate a SAS URL to the blob and return it - st.session_state['file_url'] = blob_client.url + '?' + generate_blob_sas(account_name, container_name, file_name,user_delegation_key=user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + account_url = 'https://' + account_name +'.blob.core.windows.net/' + blob_service_client = BlobServiceClient(account_url=account_url, credential=credential) + user_delegation_key = request_user_delegation_key(blob_service_client=blob_service_client) + container_name = os.getenv('AZURE_BLOB_CONTAINER_NAME') + blob_client = blob_service_client.get_blob_client(container=container_name, blob=file_name) + blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type+charset)) + st.session_state['file_url'] = blob_client.url + '?' + generate_blob_sas(account_name, container_name, file_name,user_delegation_key=user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + else: + account_key = os.getenv("AZURE_BLOB_ACCOUNT_KEY") + container_name = os.getenv('AZURE_BLOB_CONTAINER_NAME') + if account_name == None or account_key == None or container_name == None: + raise ValueError("Please provide values for AZURE_BLOB_ACCOUNT_NAME, AZURE_BLOB_ACCOUNT_KEY and AZURE_BLOB_CONTAINER_NAME") + connect_str = f"DefaultEndpointsProtocol=https;AccountName={account_name};AccountKey={account_key};EndpointSuffix=core.windows.net" + blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(connect_str) + # Create a blob client using the local file name as the name for the blob + blob_client = blob_service_client.get_blob_client(container=container_name, blob=file_name) + # Upload the created file + blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type+charset)) + # Generate a SAS URL to the blob and return it + st.session_state['file_url'] = blob_client.url + '?' + generate_blob_sas(account_name, container_name, file_name,account_key=account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) try: with st.expander("Add documents in Batch", expanded=True): diff --git a/backend/requirements.txt b/backend/requirements.txt index eb143db62..ccc7b2962 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -10,8 +10,8 @@ transformers==4.30.0 python-dotenv==1.0.0 azure-ai-formrecognizer==3.2.0 azure-storage-blob==12.14.1 -azure-identity==1.12.0 -azure-ai-contentsafety==1.0.0b1 +azure-identity==1.15.0 +azure-ai-contentsafety==1.0.0 requests==2.31.0 tiktoken==0.2.0 azure-storage-queue==12.5.0 @@ -23,5 +23,4 @@ chardet==5.1.0 azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 pandas==1.5.1 -python-docx==0.8.11 -azure-keyvault-secrets==4.4.* \ No newline at end of file +python-docx==0.8.11 \ No newline at end of file diff --git a/backend/utilities/helpers/AzureBlobStorageHelper.py b/backend/utilities/helpers/AzureBlobStorageHelper.py index 148bfaf79..bc44025fd 100644 --- a/backend/utilities/helpers/AzureBlobStorageHelper.py +++ b/backend/utilities/helpers/AzureBlobStorageHelper.py @@ -4,22 +4,30 @@ from .EnvHelper import EnvHelper from datetime import datetime, timedelta from azure.identity import DefaultAzureCredential +import os class AzureBlobStorageClient: def __init__(self, account_name: Optional[str] = None, account_key: Optional[str] = None, container_name: Optional[str] = None): env_helper : EnvHelper = EnvHelper() - self.account_name = account_name if account_name else env_helper.AZURE_BLOB_ACCOUNT_NAME - # self.account_key = account_key if account_key else env_helper.AZURE_BLOB_ACCOUNT_KEY - # self.connect_str = f"DefaultEndpointsProtocol=https;AccountName={self.account_name};AccountKey={self.account_key};EndpointSuffix=core.windows.net" - self.container_name : str = container_name if container_name else env_helper.AZURE_BLOB_CONTAINER_NAME - # self.blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(self.connect_str) - credential = DefaultAzureCredential() - self.blob_service_client = BlobServiceClient(account_url='', credential=credential) - self.user_delegation_key = self.request_user_delegation_key(blob_service_client=self.blob_service_client) + self.use_rbac = False + if os.environ.get("AUTH_TYPE") == 'rbac': + self.use_rbac = True + self.account_name = account_name if account_name else env_helper.AZURE_BLOB_ACCOUNT_NAME + self.container_name : str = container_name if container_name else env_helper.AZURE_BLOB_CONTAINER_NAME + credential = DefaultAzureCredential() + account_url = 'https://' + self.account_name +'.blob.core.windows.net/' + self.blob_service_client = BlobServiceClient(account_url=account_url, credential=credential) + self.user_delegation_key = self.request_user_delegation_key(blob_service_client=self.blob_service_client) + else: + self.account_name = account_name if account_name else env_helper.AZURE_BLOB_ACCOUNT_NAME + self.account_key = account_key if account_key else env_helper.AZURE_BLOB_ACCOUNT_KEY + self.connect_str = f"DefaultEndpointsProtocol=https;AccountName={self.account_name};AccountKey={self.account_key};EndpointSuffix=core.windows.net" + self.container_name : str = container_name if container_name else env_helper.AZURE_BLOB_CONTAINER_NAME + self.blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(self.connect_str) - def request_user_delegation_key(blob_service_client: BlobServiceClient) -> UserDelegationKey: + def request_user_delegation_key(self, blob_service_client: BlobServiceClient) -> UserDelegationKey: # Get a user delegation key that's valid for 1 day delegation_key_start_time = datetime.utcnow() delegation_key_expiry_time = delegation_key_start_time + timedelta(days=1) @@ -36,7 +44,12 @@ def upload_file(self, bytes_data, file_name, content_type='application/pdf'): # Upload the created file blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type)) # Generate a SAS URL to the blob and return it - return blob_client.url + '?' + generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + sas_url = blob_client.url + '?' + if self.use_rbac: + sas_url += generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + else: + sas_url += generate_blob_sas(self.account_name, self.container_name, file_name, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + return sas_url def download_file(self, file_name): blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) @@ -60,7 +73,10 @@ def get_all_files(self): container_client = self.blob_service_client.get_container_client(self.container_name) blob_list = container_client.list_blobs(include='metadata') # sas = generate_blob_sas(account_name, container_name, blob.name,account_key=account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - sas = generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + if self.use_rbac: + sas = generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + else: + sas = generate_container_sas(self.account_name, self.container_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) files = [] converted_files = {} for blob in blob_list: @@ -85,7 +101,10 @@ def get_all_files(self): return files def upsert_blob_metadata(self, file_name, metadata): - blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) + if self.use_rbac: + blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) + else: + blob_client = BlobServiceClient.from_connection_string(self.connect_str).get_blob_client(container=self.container_name, blob=file_name) # Read metadata from the blob blob_metadata = blob_client.get_blob_properties().metadata # Update metadata @@ -95,8 +114,14 @@ def upsert_blob_metadata(self, file_name, metadata): def get_container_sas(self): # Generate a SAS URL to the container and return it - return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) + if self.use_rbac: + return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) + else: + return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) def get_blob_sas(self, file_name): # Generate a SAS URL to the blob and return it - return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, user_delegation_key= self.user_delegation_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) + if self.use_rbac: + return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, user_delegation_key= self.user_delegation_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) + else: + return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, account_key= self.account_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) diff --git a/backend/utilities/helpers/AzureFormRecognizerHelper.py b/backend/utilities/helpers/AzureFormRecognizerHelper.py index d8ffbfde5..14990b7b1 100644 --- a/backend/utilities/helpers/AzureFormRecognizerHelper.py +++ b/backend/utilities/helpers/AzureFormRecognizerHelper.py @@ -1,19 +1,26 @@ from azure.core.credentials import AzureKeyCredential from azure.ai.formrecognizer import DocumentAnalysisClient +from azure.identity import DefaultAzureCredential import html import traceback from .EnvHelper import EnvHelper +import os class AzureFormRecognizerClient: def __init__(self) -> None: env_helper : EnvHelper = EnvHelper() self.AZURE_FORM_RECOGNIZER_ENDPOINT : str = env_helper.AZURE_FORM_RECOGNIZER_ENDPOINT - self.AZURE_FORM_RECOGNIZER_KEY : str = env_helper.AZURE_FORM_RECOGNIZER_KEY + if os.environ.get("AUTH_TYPE") == 'rbac': + self.document_analysis_client = DocumentAnalysisClient( + endpoint=self.AZURE_FORM_RECOGNIZER_ENDPOINT, credential=DefaultAzureCredential() + ) + else: + self.AZURE_FORM_RECOGNIZER_KEY : str = env_helper.AZURE_FORM_RECOGNIZER_KEY - self.document_analysis_client = DocumentAnalysisClient( - endpoint=self.AZURE_FORM_RECOGNIZER_ENDPOINT, credential=AzureKeyCredential(self.AZURE_FORM_RECOGNIZER_KEY) - ) + self.document_analysis_client = DocumentAnalysisClient( + endpoint=self.AZURE_FORM_RECOGNIZER_ENDPOINT, credential=AzureKeyCredential(self.AZURE_FORM_RECOGNIZER_KEY) + ) form_recognizer_role_to_html = { "title": "h1", diff --git a/backend/utilities/helpers/EnvHelper.py b/backend/utilities/helpers/EnvHelper.py index 9404e30d6..b7537e3f6 100644 --- a/backend/utilities/helpers/EnvHelper.py +++ b/backend/utilities/helpers/EnvHelper.py @@ -2,21 +2,24 @@ import logging from dotenv import load_dotenv from azure.identity import DefaultAzureCredential -from azure.keyvault.secrets import SecretClient logger = logging.getLogger(__name__) class EnvHelper: def __init__(self, **kwargs) -> None: load_dotenv() - if os.environ.get("USE_KEY_VAULT"): + + USE_RBAC = False + + if os.environ.get("AUTH_TYPE") == 'rbac': + USE_RBAC = True credential = DefaultAzureCredential() - secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) openai_token = credential.get_token("https://cognitiveservices.azure.com/.default") + # Azure Search self.AZURE_SEARCH_SERVICE = os.getenv('AZURE_SEARCH_SERVICE', '') self.AZURE_SEARCH_INDEX = os.getenv('AZURE_SEARCH_INDEX', '') - self.AZURE_SEARCH_KEY = secret_client.get_secret(os.getenv("AZURE_SEARCH_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_SEARCH_KEY", '') + self.AZURE_SEARCH_KEY = None if USE_RBAC else os.getenv('AZURE_SEARCH_KEY', '') self.AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.getenv('AZURE_SEARCH_USE_SEMANTIC_SEARCH', '') self.AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.getenv('AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG', '') self.AZURE_SEARCH_INDEX_IS_PRECHUNKED = os.getenv('AZURE_SEARCH_INDEX_IS_PRECHUNKED', '') @@ -35,7 +38,7 @@ def __init__(self, **kwargs) -> None: # Azure OpenAI self.AZURE_OPENAI_RESOURCE = os.getenv('AZURE_OPENAI_RESOURCE', '') self.AZURE_OPENAI_MODEL = os.getenv('AZURE_OPENAI_MODEL', '') - self.AZURE_OPENAI_KEY = openai_token.token + self.AZURE_OPENAI_KEY = openai_token.token if USE_RBAC else os.getenv('AZURE_OPENAI_KEY') self.AZURE_OPENAI_MODEL_NAME = os.getenv('AZURE_OPENAI_MODEL_NAME', '') self.AZURE_OPENAI_TEMPERATURE = os.getenv('AZURE_OPENAI_TEMPERATURE', '') self.AZURE_OPENAI_TOP_P = os.getenv('AZURE_OPENAI_TOP_P', '') @@ -46,11 +49,11 @@ def __init__(self, **kwargs) -> None: self.AZURE_OPENAI_STREAM = os.getenv('AZURE_OPENAI_STREAM', '') self.AZURE_OPENAI_EMBEDDING_MODEL = os.getenv('AZURE_OPENAI_EMBEDDING_MODEL', '') # Set env for OpenAI SDK - self.OPENAI_API_TYPE = "azuread" + self.OPENAI_API_TYPE = "azuread" if USE_RBAC else "azure" self.OPENAI_API_BASE = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" self.OPENAI_API_KEY = self.AZURE_OPENAI_KEY self.OPENAI_API_VERSION = self.AZURE_OPENAI_API_VERSION - os.environ["OPENAI_API_TYPE"] = "azuread" + os.environ["OPENAI_API_TYPE"] = "azuread" if USE_RBAC else "azure" os.environ["OPENAI_API_BASE"] = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" os.environ["OPENAI_API_KEY"] = self.AZURE_OPENAI_KEY os.environ["OPENAI_API_VERSION"] = self.AZURE_OPENAI_API_VERSION @@ -60,16 +63,16 @@ def __init__(self, **kwargs) -> None: self.DOCUMENT_PROCESSING_QUEUE_NAME = os.getenv('DOCUMENT_PROCESSING_QUEUE_NAME', '') # Azure Blob Storage self.AZURE_BLOB_ACCOUNT_NAME = os.getenv('AZURE_BLOB_ACCOUNT_NAME', '') - self.AZURE_BLOB_ACCOUNT_KEY = secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_BLOB_ACCOUNT_KEY", '') + self.AZURE_BLOB_ACCOUNT_KEY = os.getenv('AZURE_BLOB_ACCOUNT_KEY', '') self.AZURE_BLOB_CONTAINER_NAME = os.getenv('AZURE_BLOB_CONTAINER_NAME', '') # Azure Form Recognizer self.AZURE_FORM_RECOGNIZER_ENDPOINT = os.getenv('AZURE_FORM_RECOGNIZER_ENDPOINT', '') - self.AZURE_FORM_RECOGNIZER_KEY = openai_token.token + self.AZURE_FORM_RECOGNIZER_KEY = os.getenv('AZURE_FORM_RECOGNIZER_KEY', '') # Azure App Insights self.APPINSIGHTS_CONNECTION_STRING = os.getenv('APPINSIGHTS_CONNECTION_STRING', '') # Azure AI Content Safety self.AZURE_CONTENT_SAFETY_ENDPOINT = os.getenv('AZURE_CONTENT_SAFETY_ENDPOINT', '') - self.AZURE_CONTENT_SAFETY_KEY = openai_token.token + self.AZURE_CONTENT_SAFETY_KEY = os.getenv('AZURE_CONTENT_SAFETY_KEY', '') # Orchestration Settings self.ORCHESTRATION_STRATEGY = os.getenv('ORCHESTRATION_STRATEGY', 'openai_function') diff --git a/backend/utilities/helpers/LLMHelper.py b/backend/utilities/helpers/LLMHelper.py index ba69a7348..74742ab5c 100644 --- a/backend/utilities/helpers/LLMHelper.py +++ b/backend/utilities/helpers/LLMHelper.py @@ -10,7 +10,7 @@ def __init__(self): env_helper: EnvHelper = EnvHelper() # Configure OpenAI API - openai.api_type = "azure" + openai.api_type = env_helper.OPENAI_API_TYPE openai.api_version = env_helper.AZURE_OPENAI_API_VERSION openai.api_base = env_helper.OPENAI_API_BASE openai.api_key = env_helper.OPENAI_API_KEY diff --git a/backend/utilities/tools/ContentSafetyChecker.py b/backend/utilities/tools/ContentSafetyChecker.py index 58005a6b6..faa073440 100644 --- a/backend/utilities/tools/ContentSafetyChecker.py +++ b/backend/utilities/tools/ContentSafetyChecker.py @@ -1,16 +1,22 @@ from typing import List from azure.ai.contentsafety import ContentSafetyClient from azure.core.credentials import AzureKeyCredential +from azure.identity import DefaultAzureCredential from azure.core.exceptions import HttpResponseError from azure.ai.contentsafety.models import AnalyzeTextOptions from ..helpers.EnvHelper import EnvHelper from .AnswerProcessingBase import AnswerProcessingBase from ..common.Answer import Answer +import os class ContentSafetyChecker(AnswerProcessingBase): def __init__(self): env_helper = EnvHelper() - self.content_safety_client = ContentSafetyClient(env_helper.AZURE_CONTENT_SAFETY_ENDPOINT, AzureKeyCredential(env_helper.AZURE_CONTENT_SAFETY_KEY)) + + if os.environ.get("AUTH_TYPE") == 'rbac': + self.content_safety_client = ContentSafetyClient(env_helper.AZURE_CONTENT_SAFETY_ENDPOINT, DefaultAzureCredential()) + else: + self.content_safety_client = ContentSafetyClient(env_helper.AZURE_CONTENT_SAFETY_ENDPOINT, AzureKeyCredential(env_helper.AZURE_CONTENT_SAFETY_KEY)) def process_answer(self, answer: Answer,**kwargs: dict) -> Answer: response_template = kwargs['response_template'] @@ -40,8 +46,11 @@ def _filter_text_and_replace(self, text, response_template): filtered_text = text - if response.hate_result.severity > 0 or response.self_harm_result.severity > 0 or response.sexual_result.severity > 0 or response.violence_result.severity > 0: - filtered_text = response_template - + # if response.hate_result.severity > 0 or response.self_harm_result.severity > 0 or response.sexual_result.severity > 0 or response.violence_result.severity > 0: + # filtered_text = response_template + for result in response.categories_analysis: + if result.severity > 0: + filtered_text = response_template + return filtered_text \ No newline at end of file diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 0204ad31f..3578a1549 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -15,13 +15,7 @@ param azureCognitiveSearchName string = '' @secure() param appSettings object = {} param serviceName string = 'adminweb' -param useKeyVault bool -param openAIKey string = '' -param storageAccountKey string = '' -param formRecognizerKey string = '' -param searchKey string = '' -param contentSafetyKey string = '' -param keyVaultEndpoint string = '' +param authType string module adminweb '../core/host/appservice.bicep' = { name: '${name}-app-module' @@ -34,13 +28,12 @@ module adminweb '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - USE_KEY_VAULT: useKeyVault ? useKeyVault : '' - AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' - AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AUTH_TYPE: authType + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/app/function.bicep b/infra/app/function.bicep index 98256af0a..ea70b209f 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -16,13 +16,7 @@ param azureCognitiveSearchName string = '' param rgName string = '' param formRecognizerName string = '' param contentSafetyName string = '' -param useKeyVault bool -param openAIKey string = '' -param storageAccountKey string = '' -param formRecognizerKey string = '' -param searchKey string = '' -param contentSafetyKey string = '' -param keyVaultEndpoint string = '' +param authType string module function '../core/host/functions.bicep' = { name: '${name}-app-module' @@ -36,13 +30,12 @@ module function '../core/host/functions.bicep' = { runtimeName: runtimeName runtimeVersion: runtimeVersion appSettings: union(appSettings, { - USE_KEY_VAULT: useKeyVault ? useKeyVault : '' - AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' - AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AUTH_TYPE: authType + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) } } diff --git a/infra/app/storekeys.bicep b/infra/app/storekeys.bicep deleted file mode 100644 index d4001d9fc..000000000 --- a/infra/app/storekeys.bicep +++ /dev/null @@ -1,63 +0,0 @@ -param keyVaultName string = '' -param storageAccountName string = '' -param azureOpenAIName string = '' -param azureCognitiveSearchName string = '' -param rgName string = '' -param formRecognizerName string = '' -param contentSafetyName string = '' -param storageAccountKeyName string = 'AZURE-STORAGE-ACCOUNT-KEY' -param openAIKeyName string = 'AZURE-OPEN-AI-KEY' -param searchKeyName string = 'AZURE-SEARCH-KEY' -param formRecognizerKeyName string = 'AZURE-FORM-RECOGNIZER-KEY' -param contentSafety string = 'AZURE-CONTENT-SAFETY-KEY' - - -resource storageAccountKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: storageAccountKeyName - properties: { - value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - } -} - -resource openAIKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: openAIKeyName - properties: { - value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - } -} - -resource searchKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: searchKeyName - properties: { - value: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - } -} - -resource formRecognizerKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: formRecognizerKeyName - properties: { - value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - } -} - -resource contentSafetyKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: contentSafety - properties: { - value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 - } -} - -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: keyVaultName -} - -output CONTENT_SAFETY_KEY string = contentSafetyKeySecret.name -output FORM_RECOGNIZER_KEY string = formRecognizerKeySecret.name -output SEARCH_KEY string = searchKeySecret.name -output OPENAI_KEY string = openAIKeySecret.name -output STORAGE_ACCOUNT_KEY string = storageAccountKeySecret.name diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 485964eae..173375c54 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -15,14 +15,7 @@ param contentSafetyName string = '' @secure() param appSettings object = {} param serviceName string = 'web' -param useKeyVault bool -param openAIKey string = '' -param storageAccountKey string = '' -param formRecognizerKey string = '' -param searchKey string = '' -param contentSafetyKey string = '' -@secure() -param keyVaultEndpoint string = '' +param authType string module web '../core/host/appservice.bicep' = { name: '${name}-app-module' @@ -35,15 +28,13 @@ module web '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - USE_KEY_VAULT: useKeyVault ? useKeyVault : '' - AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' - AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AUTH_TYPE: authType + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) - keyVaultName: keyVaultName runtimeName: 'python' runtimeVersion: '3.11' diff --git a/infra/main.bicep b/infra/main.bicep index ec86ef413..e369098fb 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -149,6 +149,12 @@ param useKeyVault bool @description('Id of the user or app to assign application roles') param principalId string = '' +@allowed([ + 'rbac' + 'keys' +]) +param authType string + var blobContainerName = 'documents' var queueName = 'doc-processing' var clientKey = '${uniqueString(guid(subscription().id, deployment().name))}${newGuidString}' @@ -164,6 +170,45 @@ resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { tags: tags } +// Store secrets in a keyvault +module keyvault './core/security/keyvault.bicep' = if (useKeyVault) { + name: 'keyvault' + scope: rg + params: { + name: 'kv-${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +module webaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { + name: 'web-keyvault-access' + scope: rg + params: { + keyVaultName: keyVaultName + principalId: web.outputs.FRONTEND_API_IDENTITY_PRINCIPAL_ID + } +} + +module adminwebaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { + name: 'adminweb-keyvault-access' + scope: rg + params: { + keyVaultName: keyVaultName + principalId: adminweb.outputs.WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID + } +} + +module functionaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { + name: 'function-keyvault-access' + scope: rg + params: { + keyVaultName: keyVaultName + principalId: function.outputs.FUNCTION_IDENTITY_PRINCIPAL_ID + } +} + module openai 'core/ai/cognitiveservices.bicep' = { name: azureOpenAIResourceName scope: rg @@ -212,6 +257,11 @@ module search './core/search/search-services.bicep' = { sku: { name: azureCognitiveSearchSku } + authOptions: { + aadOrApiKey: { + aadAuthFailureMode: 'http403' + } + } } } @@ -228,59 +278,6 @@ module hostingplan './core/host/appserviceplan.bicep' = { } } -module storekeys './app/storekeys.bicep' = if (useKeyVault) { - name: 'storekeys' - scope: rg - params: { - keyVaultName: keyVaultName - azureOpenAIName: openai.outputs.name - azureCognitiveSearchName: search.outputs.name - storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME - formRecognizerName: formrecognizer.outputs.name - contentSafetyName: contentsafety.outputs.name - rgName: rgName - } -} - -// Store secrets in a keyvault -module keyvault './core/security/keyvault.bicep' = if (useKeyVault) { - name: 'keyvault' - scope: rg - params: { - name: 'kv-${resourceToken}' - location: location - tags: tags - principalId: principalId - } -} - -module webaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { - name: 'web-keyvault-access' - scope: rg - params: { - keyVaultName: keyVaultName - principalId: web.outputs.FRONTEND_API_IDENTITY_PRINCIPAL_ID - } -} - -module adminwebaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { - name: 'adminweb-keyvault-access' - scope: rg - params: { - keyVaultName: keyVaultName - principalId: adminweb.outputs.WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID - } -} - -module functionaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { - name: 'function-keyvault-access' - scope: rg - params: { - keyVaultName: keyVaultName - principalId: function.outputs.FUNCTION_IDENTITY_PRINCIPAL_ID - } -} - module web './app/web.bicep' = { name: websiteName scope: rg @@ -296,14 +293,8 @@ module web './app/web.bicep' = { storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name - openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' - storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' - formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' - searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' - contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' - useKeyVault: useKeyVault keyVaultName: useKeyVault ? keyvault.outputs.name : '' - keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' + authType: authType appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -352,14 +343,8 @@ module adminweb './app/adminweb.bicep' = { storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name - openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' - storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' - formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' - searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' - contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' - useKeyVault: useKeyVault keyVaultName: useKeyVault ? keyvault.outputs.name : '' - keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' + authType: authType appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' AZURE_SEARCH_INDEX: azureSearchIndex @@ -425,14 +410,8 @@ module function './app/function.bicep' = { formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name clientKey: clientKey - openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' - storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' - formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' - searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' - contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' - useKeyVault: useKeyVault keyVaultName: useKeyVault ? keyvault.outputs.name : '' - keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' + authType: authType appSettings: { FUNCTIONS_EXTENSION_VERSION: '~4' WEBSITES_ENABLE_APP_SERVICE_STORAGE: 'false' @@ -497,10 +476,118 @@ module storage 'app/storage.bicep' = { } } +// SYSTEM IDENTITIES +module storageRoleBackend 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'storage-role-backend' + params: { + principalId: adminweb.outputs.WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID + roleDefinitionId: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module openAIRoleBackend 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'openai-role-backend' + params: { + principalId: adminweb.outputs.WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID + roleDefinitionId: 'a97b65f3-24c7-4388-baec-2e87135dc908' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module openAIRoleWeb 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'openai-role-web' + params: { + principalId: web.outputs.FRONTEND_API_IDENTITY_PRINCIPAL_ID + roleDefinitionId: 'a97b65f3-24c7-4388-baec-2e87135dc908' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module openAIRoleFunction 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'openai-role-function' + params: { + principalId: function.outputs.FUNCTION_IDENTITY_PRINCIPAL_ID + roleDefinitionId: 'a97b65f3-24c7-4388-baec-2e87135dc908' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module openAIRoleBackendContributor 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'openai-role-backend-contributor' + params: { + principalId: adminweb.outputs.WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module openAIRoleWebContributor 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'openai-role-web-contributor' + params: { + principalId: web.outputs.FRONTEND_API_IDENTITY_PRINCIPAL_ID + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module openAIRoleFunctionContributor 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'openai-role-function-contributor' + params: { + principalId: function.outputs.FUNCTION_IDENTITY_PRINCIPAL_ID + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module searchRoleBackend 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'search-role-backend' + params: { + principalId: adminweb.outputs.WEBSITE_ADMIN_IDENTITY_PRINCIPAL_ID + roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module searchRoleWeb 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'search-role-web' + params: { + principalId: web.outputs.FRONTEND_API_IDENTITY_PRINCIPAL_ID + roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7' + principalType: 'ServicePrincipal' + } +} + +// SYSTEM IDENTITIES +module searchRoleFunction 'core/security/role.bicep' = if (authType == 'rbac'){ + scope: rg + name: 'search-role-function' + params: { + principalId: function.outputs.FUNCTION_IDENTITY_PRINCIPAL_ID + roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7' + principalType: 'ServicePrincipal' + } +} + output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString output APPINSIGHTS_INSTRUMENTATIONKEY string = monitoring.outputs.applicationInsightsInstrumentationKey -output AZURE_KEY_VAULT_ENDPOINT string = useKeyVault ? keyvault.outputs.endpoint : '' -output AZURE_KEY_VAULT_NAME string = useKeyVault ? keyvault.outputs.name : '' output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output AZURE_CONTENT_SAFETY_ENDPOINT string = contentsafety.outputs.endpoint @@ -530,9 +617,3 @@ output AZURE_BLOB_ACCOUNT_NAME string = storageAccountName output AZURE_OPENAI_RESOURCE string = azureOpenAIResourceName output AZURE_OPENAI_EMBEDDING_MODEL string = azureOpenAIEmbeddingModel output AZURE_OPENAI_MODEL string = azureOpenAIModel -output USE_KEY_VAULT bool = useKeyVault -output AZURE_OPENAI_KEY string = useKeyVault ? storekeys.outputs.OPENAI_KEY : '' -output AZURE_BLOB_ACCOUNT_KEY string = useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' -output AZURE_FORM_RECOGNIZER_KEY string = useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' -output AZURE_SEARCH_KEY string = useKeyVault ? storekeys.outputs.SEARCH_KEY: '' -output AZURE_CONTENT_SAFETY_KEY string = useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 3c9f3f14e..ede2fb0d0 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -7,3 +7,5 @@ param location = readEnvironmentVariable('AZURE_LOCATION', 'location') param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id') param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) + +param authType = readEnvironmentVariable('AUTH_TYPE', 'rbac') diff --git a/requirements.txt b/requirements.txt index 8ca241a72..7d50edb2f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -azure-identity==1.12.0 +azure-identity==1.15.0 Flask==2.3.2 openai==0.27.8 azure-storage-blob==12.14.1 @@ -14,6 +14,5 @@ chardet==5.1.0 --extra-index-url https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/ azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 -azure-ai-contentsafety==1.0.0b1 -python-docx==0.8.11 -azure-keyvault-secrets==4.4.* \ No newline at end of file +azure-ai-contentsafety==1.0.0 +python-docx==0.8.11 \ No newline at end of file From eb11eab6cf064ca0f0a132a296a8ff409a1b61c9 Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 3 Jan 2024 15:04:49 +0800 Subject: [PATCH 19/37] fix some format and update infra core --- README.md | 4 +- azure.yaml | 2 +- backend/pages/01_Ingest_Data.py | 5 +- .../helpers/AzureBlobStorageHelper.py | 3 +- infra/app/adminweb.bicep | 10 +- infra/core/ai/cognitiveservices.bicep | 11 + .../core/database/cosmos/cosmos-account.bicep | 49 ++++ .../cosmos/mongo/cosmos-mongo-account.bicep | 23 ++ .../cosmos/mongo/cosmos-mongo-db.bicep | 47 ++++ .../cosmos/sql/cosmos-sql-account.bicep | 22 ++ .../database/cosmos/sql/cosmos-sql-db.bicep | 74 ++++++ .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 ++ .../cosmos/sql/cosmos-sql-role-def.bicep | 30 +++ .../database/postgresql/flexibleserver.bicep | 65 ++++++ infra/core/database/sqlserver/sqlserver.bicep | 130 +++++++++++ infra/core/gateway/apim.bicep | 79 +++++++ infra/core/host/aks-agent-pool.bicep | 18 ++ infra/core/host/aks-managed-cluster.bicep | 140 ++++++++++++ infra/core/host/aks.bicep | 214 ++++++++++++++++++ infra/core/host/appservice.bicep | 27 ++- infra/core/host/container-app-upsert.bicep | 105 +++++++++ infra/core/host/container-app.bicep | 162 +++++++++++++ .../host/container-apps-environment.bicep | 41 ++++ infra/core/host/container-apps.bicep | 38 ++++ infra/core/host/container-registry.bicep | 83 +++++++ infra/core/host/staticwebapp.bicep | 22 ++ infra/core/monitor/applicationinsights.bicep | 5 +- infra/core/monitor/monitoring.bicep | 4 +- infra/core/networking/cdn-endpoint.bicep | 52 +++++ infra/core/networking/cdn-profile.bicep | 34 +++ infra/core/networking/cdn.bicep | 42 ++++ infra/core/search/search-services.bicep | 2 +- infra/core/storage/storage-account.bicep | 64 ++++++ infra/core/testing/loadtesting.bicep | 15 ++ infra/main.bicep | 44 ++++ 35 files changed, 1654 insertions(+), 31 deletions(-) create mode 100644 infra/core/database/cosmos/cosmos-account.bicep create mode 100644 infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep create mode 100644 infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep create mode 100644 infra/core/database/cosmos/sql/cosmos-sql-account.bicep create mode 100644 infra/core/database/cosmos/sql/cosmos-sql-db.bicep create mode 100644 infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep create mode 100644 infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep create mode 100644 infra/core/database/postgresql/flexibleserver.bicep create mode 100644 infra/core/database/sqlserver/sqlserver.bicep create mode 100644 infra/core/gateway/apim.bicep create mode 100644 infra/core/host/aks-agent-pool.bicep create mode 100644 infra/core/host/aks-managed-cluster.bicep create mode 100644 infra/core/host/aks.bicep create mode 100644 infra/core/host/container-app-upsert.bicep create mode 100644 infra/core/host/container-app.bicep create mode 100644 infra/core/host/container-apps-environment.bicep create mode 100644 infra/core/host/container-apps.bicep create mode 100644 infra/core/host/container-registry.bicep create mode 100644 infra/core/host/staticwebapp.bicep create mode 100644 infra/core/networking/cdn-endpoint.bicep create mode 100644 infra/core/networking/cdn-profile.bicep create mode 100644 infra/core/networking/cdn.bicep create mode 100644 infra/core/storage/storage-account.bicep create mode 100644 infra/core/testing/loadtesting.bicep diff --git a/README.md b/README.md index 176ae235d..b81bb3442 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ Out-of-the-box, you can upload the following file types: ![A screenshot of the chat app.](./media/chat-app.png) -## Running the sample using the Azure Developer CLI (azd) template +## Running the sample using the Azure Developer CLI (azd) The Azure Developer CLI (`azd`) is a developer-centric command-line interface (CLI) tool for creating Azure applications. @@ -130,7 +130,7 @@ azd auth login Then, execute the `azd init` command to initialize the environment. ``` -azd init -t jongio/chat-with-your-data-solution-accelerator +azd init -t chat-with-your-data-solution-accelerator ``` According to the prompt, enter an environment name. diff --git a/azure.yaml b/azure.yaml index 63287d07e..65c81a5ce 100644 --- a/azure.yaml +++ b/azure.yaml @@ -2,7 +2,7 @@ name: chat-with-your-data-solution-accelerator metadata: - template: chat-with-your-data-solution-accelerator + template: chat-with-your-data-solution-accelerator@0.0.1-beta services: web: project: . diff --git a/backend/pages/01_Ingest_Data.py b/backend/pages/01_Ingest_Data.py index 6c537019a..1c6ad8486 100644 --- a/backend/pages/01_Ingest_Data.py +++ b/backend/pages/01_Ingest_Data.py @@ -7,12 +7,11 @@ from datetime import datetime, timedelta import logging import requests +from azure.identity import DefaultAzureCredential from azure.storage.blob import BlobServiceClient, generate_blob_sas, ContentSettings, UserDelegationKey import urllib.parse from utilities.helpers.ConfigHelper import ConfigHelper from dotenv import load_dotenv -from azure.identity import DefaultAzureCredential -from datetime import datetime, timedelta load_dotenv() logger = logging.getLogger('azure.core.pipeline.policies.http_logging_policy').setLevel(logging.WARNING) @@ -81,7 +80,7 @@ def upload_file(bytes_data: bytes, file_name: str, content_type: Optional[str] = account_name = os.getenv('AZURE_BLOB_ACCOUNT_NAME') if os.environ.get("AUTH_TYPE") == 'rbac': credential = DefaultAzureCredential() - account_url = 'https://' + account_name +'.blob.core.windows.net/' + account_url = f"https://{account_name}.blob.core.windows.net/" blob_service_client = BlobServiceClient(account_url=account_url, credential=credential) user_delegation_key = request_user_delegation_key(blob_service_client=blob_service_client) container_name = os.getenv('AZURE_BLOB_CONTAINER_NAME') diff --git a/backend/utilities/helpers/AzureBlobStorageHelper.py b/backend/utilities/helpers/AzureBlobStorageHelper.py index bc44025fd..9afc6bff8 100644 --- a/backend/utilities/helpers/AzureBlobStorageHelper.py +++ b/backend/utilities/helpers/AzureBlobStorageHelper.py @@ -2,7 +2,6 @@ from datetime import datetime, timedelta from azure.storage.blob import BlobServiceClient, generate_blob_sas, generate_container_sas, ContentSettings, UserDelegationKey from .EnvHelper import EnvHelper -from datetime import datetime, timedelta from azure.identity import DefaultAzureCredential import os @@ -17,7 +16,7 @@ def __init__(self, account_name: Optional[str] = None, account_key: Optional[str self.account_name = account_name if account_name else env_helper.AZURE_BLOB_ACCOUNT_NAME self.container_name : str = container_name if container_name else env_helper.AZURE_BLOB_CONTAINER_NAME credential = DefaultAzureCredential() - account_url = 'https://' + self.account_name +'.blob.core.windows.net/' + account_url = f"https://{self.account_name}.blob.core.windows.net/" self.blob_service_client = BlobServiceClient(account_url=account_url, credential=credential) self.user_delegation_key = self.request_user_delegation_key(blob_service_client=self.blob_service_client) else: diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 3578a1549..5c331e38d 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -29,11 +29,11 @@ module adminweb '../core/host/appservice.bicep' = { appServicePlanId: appServicePlanId appSettings: union(appSettings, { AUTH_TYPE: authType - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/core/ai/cognitiveservices.bicep b/infra/core/ai/cognitiveservices.bicep index 1eafef93f..1bf5666b9 100644 --- a/infra/core/ai/cognitiveservices.bicep +++ b/infra/core/ai/cognitiveservices.bicep @@ -6,11 +6,21 @@ param tags object = {} param customSubDomainName string = name param deployments array = [] param kind string = 'OpenAI' + +@allowed([ 'Enabled', 'Disabled' ]) param publicNetworkAccess string = 'Enabled' param sku object = { name: 'S0' } +param allowedIpRules array = [] +param networkAcls object = empty(allowedIpRules) ? { + defaultAction: 'Allow' +} : { + ipRules: allowedIpRules + defaultAction: 'Deny' +} + resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { name: name location: location @@ -19,6 +29,7 @@ resource account 'Microsoft.CognitiveServices/accounts@2023-05-01' = { properties: { customSubDomainName: customSubDomainName publicNetworkAccess: publicNetworkAccess + networkAcls: networkAcls } sku: sku } diff --git a/infra/core/database/cosmos/cosmos-account.bicep b/infra/core/database/cosmos/cosmos-account.bicep new file mode 100644 index 000000000..6f8747f5a --- /dev/null +++ b/infra/core/database/cosmos/cosmos-account.bicep @@ -0,0 +1,49 @@ +metadata description = 'Creates an Azure Cosmos DB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) +param kind string + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { + name: name + kind: kind + location: location + tags: tags + properties: { + consistencyPolicy: { defaultConsistencyLevel: 'Session' } + locations: [ + { + locationName: location + failoverPriority: 0 + isZoneRedundant: false + } + ] + databaseAccountOfferType: 'Standard' + enableAutomaticFailover: false + enableMultipleWriteLocations: false + apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} + capabilities: [ { name: 'EnableServerless' } ] + } +} + +resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: cosmos.listConnectionStrings().connectionStrings[0].connectionString + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output connectionStringKey string = connectionStringKey +output endpoint string = cosmos.properties.documentEndpoint +output id string = cosmos.id +output name string = cosmos.name diff --git a/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep new file mode 100644 index 000000000..4aafbf386 --- /dev/null +++ b/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep @@ -0,0 +1,23 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + connectionStringKey: connectionStringKey + keyVaultName: keyVaultName + kind: 'MongoDB' + tags: tags + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id diff --git a/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep new file mode 100644 index 000000000..2a670578d --- /dev/null +++ b/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep @@ -0,0 +1,47 @@ +metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param collections array = [] +param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' +param keyVaultName string + +module cosmos 'cosmos-mongo-account.bicep' = { + name: 'cosmos-mongo-account' + params: { + name: accountName + location: location + keyVaultName: keyVaultName + tags: tags + connectionStringKey: connectionStringKey + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { + name: '${accountName}/${databaseName}' + tags: tags + properties: { + resource: { id: databaseName } + } + + resource list 'collections' = [for collection in collections: { + name: collection.name + properties: { + resource: { + id: collection.id + shardKey: { _id: collection.shardKey } + indexes: [ { key: { keys: [ collection.indexKey ] } } ] + } + } + }] + + dependsOn: [ + cosmos + ] +} + +output connectionStringKey string = connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint diff --git a/infra/core/database/cosmos/sql/cosmos-sql-account.bicep b/infra/core/database/cosmos/sql/cosmos-sql-account.bicep new file mode 100644 index 000000000..8431135ee --- /dev/null +++ b/infra/core/database/cosmos/sql/cosmos-sql-account.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param keyVaultName string + +module cosmos '../../cosmos/cosmos-account.bicep' = { + name: 'cosmos-account' + params: { + name: name + location: location + tags: tags + keyVaultName: keyVaultName + kind: 'GlobalDocumentDB' + } +} + +output connectionStringKey string = cosmos.outputs.connectionStringKey +output endpoint string = cosmos.outputs.endpoint +output id string = cosmos.outputs.id +output name string = cosmos.outputs.name diff --git a/infra/core/database/cosmos/sql/cosmos-sql-db.bicep b/infra/core/database/cosmos/sql/cosmos-sql-db.bicep new file mode 100644 index 000000000..265880dc6 --- /dev/null +++ b/infra/core/database/cosmos/sql/cosmos-sql-db.bicep @@ -0,0 +1,74 @@ +metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' +param accountName string +param databaseName string +param location string = resourceGroup().location +param tags object = {} + +param containers array = [] +param keyVaultName string +param principalIds array = [] + +module cosmos 'cosmos-sql-account.bicep' = { + name: 'cosmos-sql-account' + params: { + name: accountName + location: location + tags: tags + keyVaultName: keyVaultName + } +} + +resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { + name: '${accountName}/${databaseName}' + properties: { + resource: { id: databaseName } + } + + resource list 'containers' = [for container in containers: { + name: container.name + properties: { + resource: { + id: container.id + partitionKey: { paths: [ container.partitionKey ] } + } + options: {} + } + }] + + dependsOn: [ + cosmos + ] +} + +module roleDefinition 'cosmos-sql-role-def.bicep' = { + name: 'cosmos-sql-role-definition' + params: { + accountName: accountName + } + dependsOn: [ + cosmos + database + ] +} + +// We need batchSize(1) here because sql role assignments have to be done sequentially +@batchSize(1) +module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { + name: 'cosmos-sql-user-role-${uniqueString(principalId)}' + params: { + accountName: accountName + roleDefinitionId: roleDefinition.outputs.id + principalId: principalId + } + dependsOn: [ + cosmos + database + ] +}] + +output accountId string = cosmos.outputs.id +output accountName string = cosmos.outputs.name +output connectionStringKey string = cosmos.outputs.connectionStringKey +output databaseName string = databaseName +output endpoint string = cosmos.outputs.endpoint +output roleDefinitionId string = roleDefinition.outputs.id diff --git a/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep new file mode 100644 index 000000000..3949efef0 --- /dev/null +++ b/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep @@ -0,0 +1,19 @@ +metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' +param accountName string + +param roleDefinitionId string +param principalId string = '' + +resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { + parent: cosmos + name: guid(roleDefinitionId, principalId, cosmos.id) + properties: { + principalId: principalId + roleDefinitionId: roleDefinitionId + scope: cosmos.id + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} diff --git a/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep new file mode 100644 index 000000000..778d6dc47 --- /dev/null +++ b/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep @@ -0,0 +1,30 @@ +metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' +param accountName string + +resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { + parent: cosmos + name: guid(cosmos.id, accountName, 'sql-role') + properties: { + assignableScopes: [ + cosmos.id + ] + permissions: [ + { + dataActions: [ + 'Microsoft.DocumentDB/databaseAccounts/readMetadata' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' + 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' + ] + notDataActions: [] + } + ] + roleName: 'Reader Writer' + type: 'CustomRole' + } +} + +resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { + name: accountName +} + +output id string = roleDefinition.id diff --git a/infra/core/database/postgresql/flexibleserver.bicep b/infra/core/database/postgresql/flexibleserver.bicep new file mode 100644 index 000000000..7e26b1a8d --- /dev/null +++ b/infra/core/database/postgresql/flexibleserver.bicep @@ -0,0 +1,65 @@ +metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object +param storage object +param administratorLogin string +@secure() +param administratorLoginPassword string +param databaseNames array = [] +param allowAzureIPsFirewall bool = false +param allowAllIPsFirewall bool = false +param allowedSingleIPs array = [] + +// PostgreSQL version +param version string + +// Latest official version 2022-12-01 does not have Bicep types available +resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { + location: location + tags: tags + name: name + sku: sku + properties: { + version: version + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + storage: storage + highAvailability: { + mode: 'Disabled' + } + } + + resource database 'databases' = [for name in databaseNames: { + name: name + }] + + resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { + name: 'allow-all-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '255.255.255.255' + } + } + + resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { + name: 'allow-all-azure-internal-IPs' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + + resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { + name: 'allow-single-${replace(ip, '.', '')}' + properties: { + startIpAddress: ip + endIpAddress: ip + } + }] + +} + +output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/infra/core/database/sqlserver/sqlserver.bicep b/infra/core/database/sqlserver/sqlserver.bicep new file mode 100644 index 000000000..84f2cc2c8 --- /dev/null +++ b/infra/core/database/sqlserver/sqlserver.bicep @@ -0,0 +1,130 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param appUser string = 'appUser' +param databaseName string +param keyVaultName string +param sqlAdmin string = 'sqlAdmin' +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +@secure() +param sqlAdminPassword string +@secure() +param appUserPassword string + +resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { + name: name + location: location + tags: tags + properties: { + version: '12.0' + minimalTlsVersion: '1.2' + publicNetworkAccess: 'Enabled' + administratorLogin: sqlAdmin + administratorLoginPassword: sqlAdminPassword + } + + resource database 'databases' = { + name: databaseName + location: location + } + + resource firewall 'firewallRules' = { + name: 'Azure Services' + properties: { + // Allow all clients + // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". + // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. + startIpAddress: '0.0.0.1' + endIpAddress: '255.255.255.254' + } + } +} + +resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { + name: '${name}-deployment-script' + location: location + kind: 'AzureCLI' + properties: { + azCliVersion: '2.37.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'APPUSERNAME' + value: appUser + } + { + name: 'APPUSERPASSWORD' + secureValue: appUserPassword + } + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + ] + + scriptContent: ''' +wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 +tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . + +cat < ./initDb.sql +drop user if exists ${APPUSERNAME} +go +create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' +go +alter role db_owner add member ${APPUSERNAME} +go +SCRIPT_END + +./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql + ''' + } +} + +resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'sqlAdminPassword' + properties: { + value: sqlAdminPassword + } +} + +resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: 'appUserPassword' + properties: { + value: appUserPassword + } +} + +resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: connectionStringKey + properties: { + value: '${connectionString}; Password=${appUserPassword}' + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output databaseName string = sqlServer::database.name diff --git a/infra/core/gateway/apim.bicep b/infra/core/gateway/apim.bicep new file mode 100644 index 000000000..be7464f06 --- /dev/null +++ b/infra/core/gateway/apim.bicep @@ -0,0 +1,79 @@ +metadata description = 'Creates an Azure API Management instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The email address of the owner of the service') +@minLength(1) +param publisherEmail string = 'noreply@microsoft.com' + +@description('The name of the owner of the service') +@minLength(1) +param publisherName string = 'n/a' + +@description('The pricing tier of this API Management service') +@allowed([ + 'Consumption' + 'Developer' + 'Standard' + 'Premium' +]) +param sku string = 'Consumption' + +@description('The instance size of this API Management service.') +@allowed([ 0, 1, 2 ]) +param skuCount int = 0 + +@description('Azure Application Insights Name') +param applicationInsightsName string + +resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { + name: name + location: location + tags: union(tags, { 'azd-service-name': name }) + sku: { + name: sku + capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) + } + properties: { + publisherEmail: publisherEmail + publisherName: publisherName + // Custom properties are not supported for Consumption SKU + customProperties: sku == 'Consumption' ? {} : { + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' + 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' + } + } +} + +resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { + name: 'app-insights-logger' + parent: apimService + properties: { + credentials: { + instrumentationKey: applicationInsights.properties.InstrumentationKey + } + description: 'Logger to Azure Application Insights' + isBuffered: false + loggerType: 'applicationInsights' + resourceId: applicationInsights.id + } +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output apimServiceName string = apimService.name diff --git a/infra/core/host/aks-agent-pool.bicep b/infra/core/host/aks-agent-pool.bicep new file mode 100644 index 000000000..ac406005b --- /dev/null +++ b/infra/core/host/aks-agent-pool.bicep @@ -0,0 +1,18 @@ +metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' +param clusterName string + +@description('The agent pool name') +param name string + +@description('The agent pool configuration') +param config object + +resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' existing = { + name: clusterName +} + +resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-03-02-preview' = { + parent: aksCluster + name: name + properties: config +} diff --git a/infra/core/host/aks-managed-cluster.bicep b/infra/core/host/aks-managed-cluster.bicep new file mode 100644 index 000000000..0d4229eaf --- /dev/null +++ b/infra/core/host/aks-managed-cluster.bicep @@ -0,0 +1,140 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name of the resource group for the managed resources of the AKS cluster') +param nodeResourceGroupName string = '' + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('Kubernetes Version') +param kubernetesVersion string = '1.25.5' + +@description('Whether RBAC is enabled for local accounts') +param enableRbac bool = true + +// Add-ons +@description('Whether web app routing (preview) add-on is enabled') +param webAppRoutingAddon bool = true + +// AAD Integration +@description('Enable Azure Active Directory integration') +param enableAad bool = false + +@description('Enable RBAC using AAD') +param enableAzureRbac bool = false + +@description('The Tenant ID associated to the Azure Active Directory') +param aadTenantId string = '' + +@description('The load balancer SKU to use for ingress into the AKS cluster') +@allowed([ 'basic', 'standard' ]) +param loadBalancerSku string = 'standard' + +@description('Network plugin used for building the Kubernetes network.') +@allowed([ 'azure', 'kubenet', 'none' ]) +param networkPlugin string = 'azure' + +@description('Network policy used for building the Kubernetes network.') +@allowed([ 'azure', 'calico' ]) +param networkPolicy string = 'azure' + +@description('If set to true, getting static credentials will be disabled for this cluster.') +param disableLocalAccounts bool = false + +@description('The managed cluster SKU.') +@allowed([ 'Free', 'Paid', 'Standard' ]) +param sku string = 'Free' + +@description('Configuration of AKS add-ons') +param addOns object = {} + +@description('The log analytics workspace id used for logging & monitoring') +param workspaceId string = '' + +@description('The node pool configuration for the System agent pool') +param systemPoolConfig object + +@description('The DNS prefix to associate with the AKS cluster') +param dnsPrefix string = '' + +resource aks 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = { + name: name + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + sku: { + name: 'Base' + tier: sku + } + properties: { + nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' + kubernetesVersion: kubernetesVersion + dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix + enableRBAC: enableRbac + aadProfile: enableAad ? { + managed: true + enableAzureRBAC: enableAzureRbac + tenantID: aadTenantId + } : null + agentPoolProfiles: [ + systemPoolConfig + ] + networkProfile: { + loadBalancerSku: loadBalancerSku + networkPlugin: networkPlugin + networkPolicy: networkPolicy + } + disableLocalAccounts: disableLocalAccounts && enableAad + addonProfiles: addOns + ingressProfile: { + webAppRouting: { + enabled: webAppRoutingAddon + } + } + } +} + +var aksDiagCategories = [ + 'cluster-autoscaler' + 'kube-controller-manager' + 'kube-audit-admin' + 'guard' +] + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'aks-diagnostics' + scope: aks + properties: { + workspaceId: workspaceId + logs: [for category in aksDiagCategories: { + category: category + enabled: true + }] + metrics: [ + { + category: 'AllMetrics' + enabled: true + } + ] + } +} + +@description('The resource name of the AKS cluster') +output clusterName string = aks.name + +@description('The AKS cluster identity') +output clusterIdentity object = { + clientId: aks.properties.identityProfile.kubeletidentity.clientId + objectId: aks.properties.identityProfile.kubeletidentity.objectId + resourceId: aks.properties.identityProfile.kubeletidentity.resourceId +} diff --git a/infra/core/host/aks.bicep b/infra/core/host/aks.bicep new file mode 100644 index 000000000..452e059b2 --- /dev/null +++ b/infra/core/host/aks.bicep @@ -0,0 +1,214 @@ +metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' +@description('The name for the AKS managed cluster') +param name string + +@description('The name for the Azure container registry (ACR)') +param containerRegistryName string + +@description('The name of the connected log analytics workspace') +param logAnalyticsName string = '' + +@description('The name of the keyvault to grant access') +param keyVaultName string + +@description('The Azure region/location for the AKS resources') +param location string = resourceGroup().location + +@description('Custom tags to apply to the AKS resources') +param tags object = {} + +@description('AKS add-ons configuration') +param addOns object = { + azurePolicy: { + enabled: true + config: { + version: 'v2' + } + } + keyVault: { + enabled: true + config: { + enableSecretRotation: 'true' + rotationPollInterval: '2m' + } + } + openServiceMesh: { + enabled: false + config: {} + } + omsAgent: { + enabled: true + config: {} + } + applicationGateway: { + enabled: false + config: {} + } +} + +@allowed([ + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The System Pool Preset sizing') +param systemPoolType string = 'CostOptimised' + +@allowed([ + '' + 'CostOptimised' + 'Standard' + 'HighSpec' + 'Custom' +]) +@description('The User Pool Preset sizing') +param agentPoolType string = '' + +// Configure system / user agent pools +@description('Custom configuration of system node pool') +param systemPoolConfig object = {} +@description('Custom configuration of user node pool') +param agentPoolConfig object = {} + +// Configure AKS add-ons +var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( + addOns.omsAgent, + { + config: { + logAnalyticsWorkspaceResourceID: logAnalytics.id + } + } +) : {} + +var addOnsConfig = union( + (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, + (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, + (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, + (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, + (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} +) + +// Link to existing log analytics workspace when available +resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { + name: logAnalyticsName +} + +var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] + +// Create the primary AKS cluster resources and system node pool +module managedCluster 'aks-managed-cluster.bicep' = { + name: 'managed-cluster' + params: { + name: name + location: location + tags: tags + systemPoolConfig: union( + { name: 'npsystem', mode: 'System' }, + nodePoolBase, + systemPoolSpec + ) + addOns: addOnsConfig + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) +var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] + +// Create additional user agent pool when specified +module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { + name: 'aks-node-pool' + params: { + clusterName: managedCluster.outputs.clusterName + name: 'npuserpool' + config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) + } +} + +// Creates container registry (ACR) +module containerRegistry 'container-registry.bicep' = { + name: 'container-registry' + params: { + name: containerRegistryName + location: location + tags: tags + workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' + } +} + +// Grant ACR Pull access from cluster managed identity to container registry +module containerRegistryAccess '../security/registry-access.bicep' = { + name: 'cluster-container-registry-access' + params: { + containerRegistryName: containerRegistry.outputs.name + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Give the AKS Cluster access to KeyVault +module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { + name: 'cluster-keyvault-access' + params: { + keyVaultName: keyVaultName + principalId: managedCluster.outputs.clusterIdentity.objectId + } +} + +// Helpers for node pool configuration +var nodePoolBase = { + osType: 'Linux' + maxPods: 30 + type: 'VirtualMachineScaleSets' + upgradeSettings: { + maxSurge: '33%' + } +} + +var nodePoolPresets = { + CostOptimised: { + vmSize: 'Standard_B4ms' + count: 1 + minCount: 1 + maxCount: 3 + enableAutoScaling: true + availabilityZones: [] + } + Standard: { + vmSize: 'Standard_DS2_v2' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } + HighSpec: { + vmSize: 'Standard_D4s_v3' + count: 3 + minCount: 3 + maxCount: 5 + enableAutoScaling: true + availabilityZones: [ + '1' + '2' + '3' + ] + } +} + +// Module outputs +@description('The resource name of the AKS cluster') +output clusterName string = managedCluster.outputs.clusterName + +@description('The AKS cluster identity') +output clusterIdentity object = managedCluster.outputs.clusterIdentity + +@description('The resource name of the ACR') +output containerRegistryName string = containerRegistry.outputs.name + +@description('The login server for the container registry') +output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/infra/core/host/appservice.bicep b/infra/core/host/appservice.bicep index 4626bab5f..bef4d2ba4 100644 --- a/infra/core/host/appservice.bicep +++ b/infra/core/host/appservice.bicep @@ -65,16 +65,6 @@ resource appService 'Microsoft.Web/sites@2022-03-01' = { identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } - resource configLogs 'config' = { - name: 'logs' - properties: { - applicationLogs: { fileSystem: { level: 'Verbose' } } - detailedErrorMessages: { enabled: true } - failedRequestsTracing: { enabled: true } - httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } - } - } - resource basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { name: 'ftp' properties: { @@ -90,7 +80,9 @@ resource appService 'Microsoft.Web/sites@2022-03-01' = { } } -module config 'appservice-appsettings.bicep' = if (!empty(appSettings)) { +// Updates to the single Microsoft.sites/web/config resources that need to be performed sequentially +// sites/web/config 'appsettings' +module configAppSettings 'appservice-appsettings.bicep' = { name: '${name}-appSettings' params: { name: appService.name @@ -105,6 +97,19 @@ module config 'appservice-appsettings.bicep' = if (!empty(appSettings)) { } } +// sites/web/config 'logs' +resource configLogs 'Microsoft.Web/sites/config@2022-03-01' = { + name: 'logs' + parent: appService + properties: { + applicationLogs: { fileSystem: { level: 'Verbose' } } + detailedErrorMessages: { enabled: true } + failedRequestsTracing: { enabled: true } + httpLogs: { fileSystem: { enabled: true, retentionInDays: 1, retentionInMb: 35 } } + } + dependsOn: [configAppSettings] +} + resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (!(empty(keyVaultName))) { name: keyVaultName } diff --git a/infra/core/host/container-app-upsert.bicep b/infra/core/host/container-app-upsert.bicep new file mode 100644 index 000000000..3eec62f29 --- /dev/null +++ b/infra/core/host/container-app-upsert.bicep @@ -0,0 +1,105 @@ +metadata description = 'Creates or updates an existing Azure Container App.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The environment name for the container apps') +param containerAppsEnvironmentName string + +@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('The amount of memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@allowed([ 'http', 'grpc' ]) +@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') +param daprAppProtocol string = 'http' + +@description('Enable or disable Dapr for the container app') +param daprEnabled bool = false + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Specifies if the resource already exists') +param exists bool = false + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The name of the container image') +param imageName string = '' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The target port for the container') +param targetPort int = 80 + +resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { + name: name +} + +module app 'container-app.bicep' = { + name: '${deployment().name}-update' + params: { + name: name + location: location + tags: tags + identityType: identityType + identityName: identityName + ingressEnabled: ingressEnabled + containerName: containerName + containerAppsEnvironmentName: containerAppsEnvironmentName + containerRegistryName: containerRegistryName + containerCpuCoreCount: containerCpuCoreCount + containerMemory: containerMemory + containerMinReplicas: containerMinReplicas + containerMaxReplicas: containerMaxReplicas + daprEnabled: daprEnabled + daprAppId: daprAppId + daprAppProtocol: daprAppProtocol + secrets: secrets + external: external + env: env + imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' + targetPort: targetPort + serviceBinds: serviceBinds + } +} + +output defaultDomain string = app.outputs.defaultDomain +output imageName string = app.outputs.imageName +output name string = app.outputs.name +output uri string = app.outputs.uri diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep new file mode 100644 index 000000000..3724086d2 --- /dev/null +++ b/infra/core/host/container-app.bicep @@ -0,0 +1,162 @@ +metadata description = 'Creates a container app in an Azure Container App environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Allowed origins') +param allowedOrigins array = [] + +@description('Name of the environment for container apps') +param containerAppsEnvironmentName string + +@description('CPU cores allocated to a single container instance, e.g., 0.5') +param containerCpuCoreCount string = '0.5' + +@description('The maximum number of replicas to run. Must be at least 1.') +@minValue(1) +param containerMaxReplicas int = 10 + +@description('Memory allocated to a single container instance, e.g., 1Gi') +param containerMemory string = '1.0Gi' + +@description('The minimum number of replicas to run. Must be at least 1.') +param containerMinReplicas int = 1 + +@description('The name of the container') +param containerName string = 'main' + +@description('The name of the container registry') +param containerRegistryName string = '' + +@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') +@allowed([ 'http', 'grpc' ]) +param daprAppProtocol string = 'http' + +@description('The Dapr app ID') +param daprAppId string = containerName + +@description('Enable Dapr') +param daprEnabled bool = false + +@description('The environment variables for the container') +param env array = [] + +@description('Specifies if the resource ingress is exposed externally') +param external bool = true + +@description('The name of the user-assigned identity') +param identityName string = '' + +@description('The type of identity for the resource') +@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) +param identityType string = 'None' + +@description('The name of the container image') +param imageName string = '' + +@description('Specifies if Ingress is enabled for the container app') +param ingressEnabled bool = true + +param revisionMode string = 'Single' + +@description('The secrets required for the container') +param secrets array = [] + +@description('The service binds associated with the container') +param serviceBinds array = [] + +@description('The name of the container apps add-on to use. e.g. redis') +param serviceType string = '' + +@description('The target port for the container') +param targetPort int = 80 + +resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { + name: identityName +} + +// Private registry support requires both an ACR name and a User Assigned managed identity +var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) + +// Automatically set to `UserAssigned` when an `identityName` has been set +var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType + +module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { + name: '${deployment().name}-registry-access' + params: { + containerRegistryName: containerRegistryName + principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' + } +} + +resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { + name: name + location: location + tags: tags + // It is critical that the identity is granted ACR pull access before the app is created + // otherwise the container app will throw a provision error + // This also forces us to use an user assigned managed identity since there would no way to + // provide the system assigned identity with the ACR pull access before the app is created + dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] + identity: { + type: normalizedIdentityType + userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null + } + properties: { + managedEnvironmentId: containerAppsEnvironment.id + configuration: { + activeRevisionsMode: revisionMode + ingress: ingressEnabled ? { + external: external + targetPort: targetPort + transport: 'auto' + corsPolicy: { + allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) + } + } : null + dapr: daprEnabled ? { + enabled: true + appId: daprAppId + appProtocol: daprAppProtocol + appPort: ingressEnabled ? targetPort : 0 + } : { enabled: false } + secrets: secrets + service: !empty(serviceType) ? { type: serviceType } : null + registries: usePrivateRegistry ? [ + { + server: '${containerRegistryName}.azurecr.io' + identity: userIdentity.id + } + ] : [] + } + template: { + serviceBinds: !empty(serviceBinds) ? serviceBinds : null + containers: [ + { + image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: containerName + env: env + resources: { + cpu: json(containerCpuCoreCount) + memory: containerMemory + } + } + ] + scale: { + minReplicas: containerMinReplicas + maxReplicas: containerMaxReplicas + } + } + } +} + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { + name: containerAppsEnvironmentName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) +output imageName string = imageName +output name string = app.name +output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} +output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/infra/core/host/container-apps-environment.bicep b/infra/core/host/container-apps-environment.bicep new file mode 100644 index 000000000..8633ba487 --- /dev/null +++ b/infra/core/host/container-apps-environment.bicep @@ -0,0 +1,41 @@ +metadata description = 'Creates an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the Application Insights resource') +param applicationInsightsName string = '' + +@description('Specifies if Dapr is enabled') +param daprEnabled bool = false + +@description('Name of the Log Analytics workspace') +param logAnalyticsWorkspaceName string + +resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { + name: name + location: location + tags: tags + properties: { + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { + name: logAnalyticsWorkspaceName +} + +resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { + name: applicationInsightsName +} + +output defaultDomain string = containerAppsEnvironment.properties.defaultDomain +output id string = containerAppsEnvironment.id +output name string = containerAppsEnvironment.name diff --git a/infra/core/host/container-apps.bicep b/infra/core/host/container-apps.bicep new file mode 100644 index 000000000..f3f7dadd1 --- /dev/null +++ b/infra/core/host/container-apps.bicep @@ -0,0 +1,38 @@ +metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param containerAppsEnvironmentName string +param containerRegistryName string +param containerRegistryResourceGroupName string = '' +param logAnalyticsWorkspaceName string +param applicationInsightsName string = '' + +module containerAppsEnvironment 'container-apps-environment.bicep' = { + name: '${name}-container-apps-environment' + params: { + name: containerAppsEnvironmentName + location: location + tags: tags + logAnalyticsWorkspaceName: logAnalyticsWorkspaceName + applicationInsightsName: applicationInsightsName + } +} + +module containerRegistry 'container-registry.bicep' = { + name: '${name}-container-registry' + scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() + params: { + name: containerRegistryName + location: location + tags: tags + } +} + +output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain +output environmentName string = containerAppsEnvironment.outputs.name +output environmentId string = containerAppsEnvironment.outputs.id + +output registryLoginServer string = containerRegistry.outputs.loginServer +output registryName string = containerRegistry.outputs.name diff --git a/infra/core/host/container-registry.bicep b/infra/core/host/container-registry.bicep new file mode 100644 index 000000000..9c64531b9 --- /dev/null +++ b/infra/core/host/container-registry.bicep @@ -0,0 +1,83 @@ +metadata description = 'Creates an Azure Container Registry.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('Indicates whether admin user is enabled') +param adminUserEnabled bool = false + +@description('Indicates whether anonymous pull is enabled') +param anonymousPullEnabled bool = false + +@description('Indicates whether data endpoint is enabled') +param dataEndpointEnabled bool = false + +@description('Encryption settings') +param encryption object = { + status: 'disabled' +} + +@description('Options for bypassing network rules') +param networkRuleBypassOptions string = 'AzureServices' + +@description('Public network access setting') +param publicNetworkAccess string = 'Enabled' + +@description('SKU settings') +param sku object = { + name: 'Basic' +} + +@description('Zone redundancy setting') +param zoneRedundancy string = 'Disabled' + +@description('The log analytics workspace ID used for logging and monitoring') +param workspaceId string = '' + +// 2022-02-01-preview needed for anonymousPullEnabled +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { + name: name + location: location + tags: tags + sku: sku + properties: { + adminUserEnabled: adminUserEnabled + anonymousPullEnabled: anonymousPullEnabled + dataEndpointEnabled: dataEndpointEnabled + encryption: encryption + networkRuleBypassOptions: networkRuleBypassOptions + publicNetworkAccess: publicNetworkAccess + zoneRedundancy: zoneRedundancy + } +} + +// TODO: Update diagnostics to be its own module +// Blocking issue: https://github.com/Azure/bicep/issues/622 +// Unable to pass in a `resource` scope or unable to use string interpolation in resource types +resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { + name: 'registry-diagnostics' + scope: containerRegistry + properties: { + workspaceId: workspaceId + logs: [ + { + category: 'ContainerRegistryRepositoryEvents' + enabled: true + } + { + category: 'ContainerRegistryLoginEvents' + enabled: true + } + ] + metrics: [ + { + category: 'AllMetrics' + enabled: true + timeGrain: 'PT1M' + } + ] + } +} + +output loginServer string = containerRegistry.properties.loginServer +output name string = containerRegistry.name diff --git a/infra/core/host/staticwebapp.bicep b/infra/core/host/staticwebapp.bicep new file mode 100644 index 000000000..cedaf906e --- /dev/null +++ b/infra/core/host/staticwebapp.bicep @@ -0,0 +1,22 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} + +param sku object = { + name: 'Free' + tier: 'Free' +} + +resource web 'Microsoft.Web/staticSites@2022-03-01' = { + name: name + location: location + tags: tags + sku: sku + properties: { + provider: 'Custom' + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/infra/core/monitor/applicationinsights.bicep b/infra/core/monitor/applicationinsights.bicep index 4ee08255a..4b4d01e34 100644 --- a/infra/core/monitor/applicationinsights.bicep +++ b/infra/core/monitor/applicationinsights.bicep @@ -1,9 +1,8 @@ metadata description = 'Creates an Application Insights instance based on an existing Log Analytics workspace.' param name string -param dashboardName string +param dashboardName string = '' param location string = resourceGroup().location param tags object = {} -param includeDashboard bool = true param logAnalyticsWorkspaceId string resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { @@ -17,7 +16,7 @@ resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { } } -module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (includeDashboard) { +module applicationInsightsDashboard 'applicationinsights-dashboard.bicep' = if (!empty(dashboardName)) { name: 'application-insights-dashboard' params: { name: dashboardName diff --git a/infra/core/monitor/monitoring.bicep b/infra/core/monitor/monitoring.bicep index 165ada8b3..6bb05b0bd 100644 --- a/infra/core/monitor/monitoring.bicep +++ b/infra/core/monitor/monitoring.bicep @@ -1,10 +1,9 @@ metadata description = 'Creates an Application Insights instance and a Log Analytics workspace.' param logAnalyticsName string param applicationInsightsName string -param applicationInsightsDashboardName string +param applicationInsightsDashboardName string = '' param location string = resourceGroup().location param tags object = {} -param includeDashboard bool = true module logAnalytics 'loganalytics.bicep' = { name: 'loganalytics' @@ -22,7 +21,6 @@ module applicationInsights 'applicationinsights.bicep' = { location: location tags: tags dashboardName: applicationInsightsDashboardName - includeDashboard: includeDashboard logAnalyticsWorkspaceId: logAnalytics.outputs.id } } diff --git a/infra/core/networking/cdn-endpoint.bicep b/infra/core/networking/cdn-endpoint.bicep new file mode 100644 index 000000000..5e8ab695d --- /dev/null +++ b/infra/core/networking/cdn-endpoint.bicep @@ -0,0 +1,52 @@ +metadata description = 'Adds an endpoint to an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The name of the CDN profile resource') +@minLength(1) +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('The origin URL for the endpoint') +@minLength(1) +param originUrl string + +resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { + parent: cdnProfile + name: name + location: location + tags: tags + properties: { + originHostHeader: originUrl + isHttpAllowed: false + isHttpsAllowed: true + queryStringCachingBehavior: 'UseQueryString' + optimizationType: 'GeneralWebDelivery' + origins: [ + { + name: replace(originUrl, '.', '-') + properties: { + hostName: originUrl + originHostHeader: originUrl + priority: 1 + weight: 1000 + enabled: true + } + } + ] + deliveryPolicy: { + rules: deliveryPolicyRules + } + } +} + +resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { + name: cdnProfileName +} + +output id string = endpoint.id +output name string = endpoint.name +output uri string = 'https://${endpoint.properties.hostName}' diff --git a/infra/core/networking/cdn-profile.bicep b/infra/core/networking/cdn-profile.bicep new file mode 100644 index 000000000..27669ee2e --- /dev/null +++ b/infra/core/networking/cdn-profile.bicep @@ -0,0 +1,34 @@ +metadata description = 'Creates an Azure CDN profile.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@description('The pricing tier of this CDN profile') +@allowed([ + 'Custom_Verizon' + 'Premium_AzureFrontDoor' + 'Premium_Verizon' + 'StandardPlus_955BandWidth_ChinaCdn' + 'StandardPlus_AvgBandWidth_ChinaCdn' + 'StandardPlus_ChinaCdn' + 'Standard_955BandWidth_ChinaCdn' + 'Standard_Akamai' + 'Standard_AvgBandWidth_ChinaCdn' + 'Standard_AzureFrontDoor' + 'Standard_ChinaCdn' + 'Standard_Microsoft' + 'Standard_Verizon' +]) +param sku string = 'Standard_Microsoft' + +resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { + name: name + location: location + tags: tags + sku: { + name: sku + } +} + +output id string = profile.id +output name string = profile.name diff --git a/infra/core/networking/cdn.bicep b/infra/core/networking/cdn.bicep new file mode 100644 index 000000000..de98a1f99 --- /dev/null +++ b/infra/core/networking/cdn.bicep @@ -0,0 +1,42 @@ +metadata description = 'Creates an Azure CDN profile with a single endpoint.' +param location string = resourceGroup().location +param tags object = {} + +@description('Name of the CDN endpoint resource') +param cdnEndpointName string + +@description('Name of the CDN profile resource') +param cdnProfileName string + +@description('Delivery policy rules') +param deliveryPolicyRules array = [] + +@description('Origin URL for the CDN endpoint') +param originUrl string + +module cdnProfile 'cdn-profile.bicep' = { + name: 'cdn-profile' + params: { + name: cdnProfileName + location: location + tags: tags + } +} + +module cdnEndpoint 'cdn-endpoint.bicep' = { + name: 'cdn-endpoint' + params: { + name: cdnEndpointName + location: location + tags: tags + cdnProfileName: cdnProfile.outputs.name + originUrl: originUrl + deliveryPolicyRules: deliveryPolicyRules + } +} + +output endpointName string = cdnEndpoint.outputs.name +output endpointId string = cdnEndpoint.outputs.id +output profileName string = cdnProfile.outputs.name +output profileId string = cdnProfile.outputs.id +output uri string = cdnEndpoint.outputs.uri diff --git a/infra/core/search/search-services.bicep b/infra/core/search/search-services.bicep index be6e3b344..e7747e515 100644 --- a/infra/core/search/search-services.bicep +++ b/infra/core/search/search-services.bicep @@ -1,4 +1,4 @@ -metadata description = 'Creates an Azure Cognitive Search instance.' +metadata description = 'Creates an Azure AI Search instance.' param name string param location string = resourceGroup().location param tags object = {} diff --git a/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep new file mode 100644 index 000000000..4b6febbea --- /dev/null +++ b/infra/core/storage/storage-account.bicep @@ -0,0 +1,64 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } +} + +output name string = storage.name +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/infra/core/testing/loadtesting.bicep b/infra/core/testing/loadtesting.bicep new file mode 100644 index 000000000..467810867 --- /dev/null +++ b/infra/core/testing/loadtesting.bicep @@ -0,0 +1,15 @@ +param name string +param location string = resourceGroup().location +param managedIdentity bool = false +param tags object = {} + +resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { + name: name + location: location + tags: tags + identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } + properties: { + } +} + +output loadTestingName string = loadTest.name diff --git a/infra/main.bicep b/infra/main.bicep index e369098fb..6ebb3370e 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -476,6 +476,50 @@ module storage 'app/storage.bicep' = { } } +// USER ROLES +module storageRoleUser 'core/security/role.bicep' = if (authType == 'rbac') { + scope: rg + name: 'storage-role-user' + params: { + principalId: principalId + roleDefinitionId: 'ba92f5b4-2d11-453d-a403-e96b0029c9fe' + principalType: 'User' + } +} + +// USER ROLES +module openaiRoleUser 'core/security/role.bicep' = if (authType == 'rbac') { + scope: rg + name: 'openai-role-user' + params: { + principalId: principalId + roleDefinitionId: 'a97b65f3-24c7-4388-baec-2e87135dc908' + principalType: 'User' + } +} + +// USER ROLES +module openaiRoleUserContributor 'core/security/role.bicep' = if (authType == 'rbac') { + scope: rg + name: 'openai-role-user-contributor' + params: { + principalId: principalId + roleDefinitionId: 'b24988ac-6180-42a0-ab88-20f7382dd24c' + principalType: 'User' + } +} + +// USER ROLES +module searchRoleUser 'core/security/role.bicep' = if (authType == 'rbac') { + scope: rg + name: 'search-role-user' + params: { + principalId: principalId + roleDefinitionId: '8ebe5a00-799e-43f5-93ac-243d3dce84a7' + principalType: 'User' + } +} + // SYSTEM IDENTITIES module storageRoleBackend 'core/security/role.bicep' = if (authType == 'rbac'){ scope: rg From a403d25c05402994f602caa24e8a8600d96408f2 Mon Sep 17 00:00:00 2001 From: ChenxiJiang333 Date: Wed, 3 Jan 2024 15:49:13 +0800 Subject: [PATCH 20/37] Update openai to v1 --- app.py | 9 +++------ backend/requirements.txt | 4 ++-- backend/utilities/QuestionHandler.py | 1 - backend/utilities/helpers/LLMHelper.py | 20 ++++++++------------ 4 files changed, 13 insertions(+), 21 deletions(-) diff --git a/app.py b/app.py index ff907c93f..e6438be31 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,7 @@ import os import logging import requests -import openai +from openai import AzureOpenAI # Fixing MIME types for static files under Windows import mimetypes @@ -199,10 +199,7 @@ def stream_without_data(response): def conversation_without_data(request): - openai.api_type = "azure" - openai.api_base = f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/" - openai.api_version = "2023-03-15-preview" - openai.api_key = AZURE_OPENAI_KEY + openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, api_key=AZURE_OPENAI_KEY) request_messages = request.json["messages"] messages = [ @@ -218,7 +215,7 @@ def conversation_without_data(request): "content": message["content"] }) - response = openai.ChatCompletion.create( + response = openai_client.chat.completions.create( engine=AZURE_OPENAI_MODEL, messages = messages, temperature=float(AZURE_OPENAI_TEMPERATURE), diff --git a/backend/requirements.txt b/backend/requirements.txt index ccc7b2962..d16d8fc72 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,7 +1,7 @@ azure-functions streamlit==1.24.0 -openai==0.27.8 +openai==1.6.1 matplotlib==3.6.3 plotly==5.12.0 scipy==1.10.0 @@ -15,7 +15,7 @@ azure-ai-contentsafety==1.0.0 requests==2.31.0 tiktoken==0.2.0 azure-storage-queue==12.5.0 -langchain==0.0.274 +langchain==0.0.353 beautifulsoup4==4.12.0 fake-useragent==1.1.3 chardet==5.1.0 diff --git a/backend/utilities/QuestionHandler.py b/backend/utilities/QuestionHandler.py index afe432160..b5e386ba6 100644 --- a/backend/utilities/QuestionHandler.py +++ b/backend/utilities/QuestionHandler.py @@ -1,5 +1,4 @@ import os -import openai import logging import re import json diff --git a/backend/utilities/helpers/LLMHelper.py b/backend/utilities/helpers/LLMHelper.py index 74742ab5c..2fe32ae8a 100644 --- a/backend/utilities/helpers/LLMHelper.py +++ b/backend/utilities/helpers/LLMHelper.py @@ -1,7 +1,7 @@ -import openai +from openai import AzureOpenAI from typing import List from langchain.chat_models import AzureChatOpenAI -from langchain.embeddings import OpenAIEmbeddings +from langchain.embeddings import AzureOpenAIEmbeddings from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from .EnvHelper import EnvHelper @@ -9,29 +9,25 @@ class LLMHelper: def __init__(self): env_helper: EnvHelper = EnvHelper() - # Configure OpenAI API - openai.api_type = env_helper.OPENAI_API_TYPE - openai.api_version = env_helper.AZURE_OPENAI_API_VERSION - openai.api_base = env_helper.OPENAI_API_BASE - openai.api_key = env_helper.OPENAI_API_KEY + self.openai_client = AzureOpenAI(azure_endpoint=env_helper.OPENAI_API_BASE, api_version=env_helper.AZURE_OPENAI_API_VERSION, api_key=env_helper.OPENAI_API_KEY) self.llm_model = env_helper.AZURE_OPENAI_MODEL self.llm_max_tokens = env_helper.AZURE_OPENAI_MAX_TOKENS if env_helper.AZURE_OPENAI_MAX_TOKENS != '' else None self.embedding_model = env_helper.AZURE_OPENAI_EMBEDDING_MODEL def get_llm(self): - return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=openai.api_version) + return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) # TODO: This needs to have a custom callback to stream back to the UI def get_streaming_llm(self): return AzureChatOpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler], deployment_name=self.llm_model, temperature=0, - max_tokens=self.llm_max_tokens, openai_api_version=openai.api_version) + max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) def get_embedding_model(self): - return OpenAIEmbeddings(deployment=self.embedding_model, chunk_size=1) + return AzureOpenAIEmbeddings(azure_deployment=self.embedding_model, chunk_size=1, validate_base_url=False) def get_chat_completion_with_functions(self, messages: List[dict], functions: List[dict], function_call: str="auto"): - return openai.ChatCompletion.create( + return self.openai_client.chat.completions.create( deployment_id=self.llm_model, messages=messages, functions=functions, @@ -39,7 +35,7 @@ def get_chat_completion_with_functions(self, messages: List[dict], functions: Li ) def get_chat_completion(self, messages: List[dict]): - return openai.ChatCompletion.create( + return self.openai_client.chat.completions.create( deployment_id=self.llm_model, messages=messages, ) From b88ee5fcdeded129d409af5cac1b263b5d3c519b Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 3 Jan 2024 15:53:43 +0800 Subject: [PATCH 21/37] use resourcegroup.name() instead pass in rhName --- infra/app/adminweb.bicep | 1 - infra/app/function.bicep | 11 +++++------ infra/app/web.bicep | 11 +++++------ infra/main.bicep | 3 --- 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 5c331e38d..cdb54763b 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -1,7 +1,6 @@ param name string param location string = resourceGroup().location param tags object = {} -param rgName string = '' param storageAccountName string = '' param formRecognizerName string = '' param contentSafetyName string = '' diff --git a/infra/app/function.bicep b/infra/app/function.bicep index ea70b209f..9cfcba9c3 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -13,7 +13,6 @@ param clientKey string param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' -param rgName string = '' param formRecognizerName string = '' param contentSafetyName string = '' param authType string @@ -31,11 +30,11 @@ module function '../core/host/functions.bicep' = { runtimeVersion: runtimeVersion appSettings: union(appSettings, { AUTH_TYPE: authType - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) } } diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 173375c54..a2c7ab5bb 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -8,7 +8,6 @@ param applicationInsightsName string = '' param keyVaultName string = '' param azureOpenAIName string = '' param azureCognitiveSearchName string = '' -param rgName string = '' param storageAccountName string = '' param formRecognizerName string = '' param contentSafetyName string = '' @@ -29,11 +28,11 @@ module web '../core/host/appservice.bicep' = { appServicePlanId: appServicePlanId appSettings: union(appSettings, { AUTH_TYPE: authType - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/main.bicep b/infra/main.bicep index 6ebb3370e..5fe6bb985 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -285,7 +285,6 @@ module web './app/web.bicep' = { name: websiteName location: location tags: { 'azd-service-name': 'web' } - rgName: rgName appServicePlanId: hostingplan.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name @@ -335,7 +334,6 @@ module adminweb './app/adminweb.bicep' = { name: '${websiteName}-admin' location: location tags: { 'azd-service-name': 'adminweb' } - rgName: rgName appServicePlanId: hostingplan.outputs.name applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name @@ -402,7 +400,6 @@ module function './app/function.bicep' = { name: functionName location: location tags: { 'azd-service-name': 'function' } - rgName: rgName appServicePlanId: hostingplan.outputs.name azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name From 1ecb9be06cff57aefd60474749c97be1fb4b3347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenjie=20Yu=EF=BC=88MSFT=EF=BC=89?= <81678720+zedy-wj@users.noreply.github.com> Date: Wed, 3 Jan 2024 16:21:45 +0800 Subject: [PATCH 22/37] Revert "Update openai to v1" --- app.py | 9 ++++++--- backend/requirements.txt | 4 ++-- backend/utilities/QuestionHandler.py | 1 + backend/utilities/helpers/LLMHelper.py | 20 ++++++++++++-------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/app.py b/app.py index e6438be31..ff907c93f 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,7 @@ import os import logging import requests -from openai import AzureOpenAI +import openai # Fixing MIME types for static files under Windows import mimetypes @@ -199,7 +199,10 @@ def stream_without_data(response): def conversation_without_data(request): - openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, api_key=AZURE_OPENAI_KEY) + openai.api_type = "azure" + openai.api_base = f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/" + openai.api_version = "2023-03-15-preview" + openai.api_key = AZURE_OPENAI_KEY request_messages = request.json["messages"] messages = [ @@ -215,7 +218,7 @@ def conversation_without_data(request): "content": message["content"] }) - response = openai_client.chat.completions.create( + response = openai.ChatCompletion.create( engine=AZURE_OPENAI_MODEL, messages = messages, temperature=float(AZURE_OPENAI_TEMPERATURE), diff --git a/backend/requirements.txt b/backend/requirements.txt index d16d8fc72..ccc7b2962 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,7 +1,7 @@ azure-functions streamlit==1.24.0 -openai==1.6.1 +openai==0.27.8 matplotlib==3.6.3 plotly==5.12.0 scipy==1.10.0 @@ -15,7 +15,7 @@ azure-ai-contentsafety==1.0.0 requests==2.31.0 tiktoken==0.2.0 azure-storage-queue==12.5.0 -langchain==0.0.353 +langchain==0.0.274 beautifulsoup4==4.12.0 fake-useragent==1.1.3 chardet==5.1.0 diff --git a/backend/utilities/QuestionHandler.py b/backend/utilities/QuestionHandler.py index b5e386ba6..afe432160 100644 --- a/backend/utilities/QuestionHandler.py +++ b/backend/utilities/QuestionHandler.py @@ -1,4 +1,5 @@ import os +import openai import logging import re import json diff --git a/backend/utilities/helpers/LLMHelper.py b/backend/utilities/helpers/LLMHelper.py index 2fe32ae8a..74742ab5c 100644 --- a/backend/utilities/helpers/LLMHelper.py +++ b/backend/utilities/helpers/LLMHelper.py @@ -1,7 +1,7 @@ -from openai import AzureOpenAI +import openai from typing import List from langchain.chat_models import AzureChatOpenAI -from langchain.embeddings import AzureOpenAIEmbeddings +from langchain.embeddings import OpenAIEmbeddings from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from .EnvHelper import EnvHelper @@ -9,25 +9,29 @@ class LLMHelper: def __init__(self): env_helper: EnvHelper = EnvHelper() - self.openai_client = AzureOpenAI(azure_endpoint=env_helper.OPENAI_API_BASE, api_version=env_helper.AZURE_OPENAI_API_VERSION, api_key=env_helper.OPENAI_API_KEY) + # Configure OpenAI API + openai.api_type = env_helper.OPENAI_API_TYPE + openai.api_version = env_helper.AZURE_OPENAI_API_VERSION + openai.api_base = env_helper.OPENAI_API_BASE + openai.api_key = env_helper.OPENAI_API_KEY self.llm_model = env_helper.AZURE_OPENAI_MODEL self.llm_max_tokens = env_helper.AZURE_OPENAI_MAX_TOKENS if env_helper.AZURE_OPENAI_MAX_TOKENS != '' else None self.embedding_model = env_helper.AZURE_OPENAI_EMBEDDING_MODEL def get_llm(self): - return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) + return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=openai.api_version) # TODO: This needs to have a custom callback to stream back to the UI def get_streaming_llm(self): return AzureChatOpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler], deployment_name=self.llm_model, temperature=0, - max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) + max_tokens=self.llm_max_tokens, openai_api_version=openai.api_version) def get_embedding_model(self): - return AzureOpenAIEmbeddings(azure_deployment=self.embedding_model, chunk_size=1, validate_base_url=False) + return OpenAIEmbeddings(deployment=self.embedding_model, chunk_size=1) def get_chat_completion_with_functions(self, messages: List[dict], functions: List[dict], function_call: str="auto"): - return self.openai_client.chat.completions.create( + return openai.ChatCompletion.create( deployment_id=self.llm_model, messages=messages, functions=functions, @@ -35,7 +39,7 @@ def get_chat_completion_with_functions(self, messages: List[dict], functions: Li ) def get_chat_completion(self, messages: List[dict]): - return self.openai_client.chat.completions.create( + return openai.ChatCompletion.create( deployment_id=self.llm_model, messages=messages, ) From 969aa253358cf1fea11dc2193dbf2e82e158a844 Mon Sep 17 00:00:00 2001 From: ChenxiJiang333 Date: Thu, 4 Jan 2024 15:50:31 +0800 Subject: [PATCH 23/37] add token provider --- app.py | 17 ++++++++--------- backend/requirements.txt | 2 +- backend/utilities/helpers/EnvHelper.py | 18 +++++++----------- backend/utilities/helpers/LLMHelper.py | 23 +++++++++++++++++++---- requirements.txt | 4 ++-- 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/app.py b/app.py index e6438be31..c71751992 100644 --- a/app.py +++ b/app.py @@ -13,7 +13,7 @@ from flask import Flask, Response, request, jsonify from dotenv import load_dotenv from backend.utilities.QuestionHandler import QuestionHandler -from azure.identity import DefaultAzureCredential +from azure.identity import DefaultAzureCredential, get_bearer_token_provider load_dotenv() @@ -24,12 +24,7 @@ def static_file(path): return app.send_static_file(path) -USE_RBAC = False - -if os.environ.get("AUTH_TYPE") == 'rbac': - USE_RBAC = True - credential = DefaultAzureCredential() - openai_token = credential.get_token("https://cognitiveservices.azure.com/.default") +USE_RBAC = True if os.environ.get("AUTH_TYPE") == 'rbac' else False # ACS Integration Settings AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") @@ -47,7 +42,7 @@ def static_file(path): # AOAI Integration Settings AZURE_OPENAI_RESOURCE = os.environ.get("AZURE_OPENAI_RESOURCE") AZURE_OPENAI_MODEL = os.environ.get("AZURE_OPENAI_MODEL") -AZURE_OPENAI_KEY = openai_token.token if USE_RBAC else os.environ.get("AZURE_OPENAI_KEY") +AZURE_OPENAI_KEY = os.environ.get("AZURE_OPENAI_KEY") AZURE_OPENAI_TEMPERATURE = os.environ.get("AZURE_OPENAI_TEMPERATURE", 0) AZURE_OPENAI_TOP_P = os.environ.get("AZURE_OPENAI_TOP_P", 1.0) AZURE_OPENAI_MAX_TOKENS = os.environ.get("AZURE_OPENAI_MAX_TOKENS", 1000) @@ -56,6 +51,7 @@ def static_file(path): AZURE_OPENAI_API_VERSION = os.environ.get("AZURE_OPENAI_API_VERSION", "2023-06-01-preview") AZURE_OPENAI_STREAM = os.environ.get("AZURE_OPENAI_STREAM", "true") AZURE_OPENAI_MODEL_NAME = os.environ.get("AZURE_OPENAI_MODEL_NAME", "gpt-35-turbo") # Name of the model, e.g. 'gpt-35-turbo' or 'gpt-4' +AZURE_TOKEN_PROVIDER = get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default") SHOULD_STREAM = True if AZURE_OPENAI_STREAM.lower() == "true" else False @@ -199,7 +195,10 @@ def stream_without_data(response): def conversation_without_data(request): - openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, api_key=AZURE_OPENAI_KEY) + if USE_RBAC: + openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, azure_ad_token_provider=AZURE_TOKEN_PROVIDER) + else: + openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, api_key=AZURE_OPENAI_KEY) request_messages = request.json["messages"] messages = [ diff --git a/backend/requirements.txt b/backend/requirements.txt index d16d8fc72..23684fcd7 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -15,7 +15,7 @@ azure-ai-contentsafety==1.0.0 requests==2.31.0 tiktoken==0.2.0 azure-storage-queue==12.5.0 -langchain==0.0.353 +langchain==0.0.354 beautifulsoup4==4.12.0 fake-useragent==1.1.3 chardet==5.1.0 diff --git a/backend/utilities/helpers/EnvHelper.py b/backend/utilities/helpers/EnvHelper.py index b7537e3f6..a9b564d16 100644 --- a/backend/utilities/helpers/EnvHelper.py +++ b/backend/utilities/helpers/EnvHelper.py @@ -1,7 +1,7 @@ import os import logging from dotenv import load_dotenv -from azure.identity import DefaultAzureCredential +from azure.identity import DefaultAzureCredential, get_bearer_token_provider logger = logging.getLogger(__name__) @@ -9,17 +9,12 @@ class EnvHelper: def __init__(self, **kwargs) -> None: load_dotenv() - USE_RBAC = False - - if os.environ.get("AUTH_TYPE") == 'rbac': - USE_RBAC = True - credential = DefaultAzureCredential() - openai_token = credential.get_token("https://cognitiveservices.azure.com/.default") + self.USE_RBAC = True if os.environ.get("AUTH_TYPE") == 'rbac' else False # Azure Search self.AZURE_SEARCH_SERVICE = os.getenv('AZURE_SEARCH_SERVICE', '') self.AZURE_SEARCH_INDEX = os.getenv('AZURE_SEARCH_INDEX', '') - self.AZURE_SEARCH_KEY = None if USE_RBAC else os.getenv('AZURE_SEARCH_KEY', '') + self.AZURE_SEARCH_KEY = None if self.USE_RBAC else os.getenv('AZURE_SEARCH_KEY', '') self.AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.getenv('AZURE_SEARCH_USE_SEMANTIC_SEARCH', '') self.AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.getenv('AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG', '') self.AZURE_SEARCH_INDEX_IS_PRECHUNKED = os.getenv('AZURE_SEARCH_INDEX_IS_PRECHUNKED', '') @@ -38,7 +33,7 @@ def __init__(self, **kwargs) -> None: # Azure OpenAI self.AZURE_OPENAI_RESOURCE = os.getenv('AZURE_OPENAI_RESOURCE', '') self.AZURE_OPENAI_MODEL = os.getenv('AZURE_OPENAI_MODEL', '') - self.AZURE_OPENAI_KEY = openai_token.token if USE_RBAC else os.getenv('AZURE_OPENAI_KEY') + self.AZURE_OPENAI_KEY = os.getenv('AZURE_OPENAI_KEY') self.AZURE_OPENAI_MODEL_NAME = os.getenv('AZURE_OPENAI_MODEL_NAME', '') self.AZURE_OPENAI_TEMPERATURE = os.getenv('AZURE_OPENAI_TEMPERATURE', '') self.AZURE_OPENAI_TOP_P = os.getenv('AZURE_OPENAI_TOP_P', '') @@ -48,12 +43,13 @@ def __init__(self, **kwargs) -> None: self.AZURE_OPENAI_API_VERSION = os.getenv('AZURE_OPENAI_API_VERSION', '') self.AZURE_OPENAI_STREAM = os.getenv('AZURE_OPENAI_STREAM', '') self.AZURE_OPENAI_EMBEDDING_MODEL = os.getenv('AZURE_OPENAI_EMBEDDING_MODEL', '') + self.AZURE_TOKEN_PROVIDER = get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default") # Set env for OpenAI SDK - self.OPENAI_API_TYPE = "azuread" if USE_RBAC else "azure" + self.OPENAI_API_TYPE = "azuread" if self.USE_RBAC else "azure" self.OPENAI_API_BASE = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" self.OPENAI_API_KEY = self.AZURE_OPENAI_KEY self.OPENAI_API_VERSION = self.AZURE_OPENAI_API_VERSION - os.environ["OPENAI_API_TYPE"] = "azuread" if USE_RBAC else "azure" + os.environ["OPENAI_API_TYPE"] = "azuread" if self.USE_RBAC else "azure" os.environ["OPENAI_API_BASE"] = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" os.environ["OPENAI_API_KEY"] = self.AZURE_OPENAI_KEY os.environ["OPENAI_API_VERSION"] = self.AZURE_OPENAI_API_VERSION diff --git a/backend/utilities/helpers/LLMHelper.py b/backend/utilities/helpers/LLMHelper.py index 2fe32ae8a..394658fbf 100644 --- a/backend/utilities/helpers/LLMHelper.py +++ b/backend/utilities/helpers/LLMHelper.py @@ -8,23 +8,38 @@ class LLMHelper: def __init__(self): env_helper: EnvHelper = EnvHelper() + self.use_rbac = env_helper.USE_RBAC + self.token_provider = env_helper.AZURE_TOKEN_PROVIDER - self.openai_client = AzureOpenAI(azure_endpoint=env_helper.OPENAI_API_BASE, api_version=env_helper.AZURE_OPENAI_API_VERSION, api_key=env_helper.OPENAI_API_KEY) + if self.use_rbac: + self.openai_client = AzureOpenAI(azure_endpoint=env_helper.OPENAI_API_BASE, api_version=env_helper.AZURE_OPENAI_API_VERSION, azure_ad_token_provider=self.token_provider) + else: + self.openai_client = AzureOpenAI(azure_endpoint=env_helper.OPENAI_API_BASE, api_version=env_helper.AZURE_OPENAI_API_VERSION, api_key=env_helper.OPENAI_API_KEY) self.llm_model = env_helper.AZURE_OPENAI_MODEL self.llm_max_tokens = env_helper.AZURE_OPENAI_MAX_TOKENS if env_helper.AZURE_OPENAI_MAX_TOKENS != '' else None self.embedding_model = env_helper.AZURE_OPENAI_EMBEDDING_MODEL def get_llm(self): - return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) + if self.use_rbac: + return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version, azure_ad_token_provider=self.token_provider) + else: + return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) # TODO: This needs to have a custom callback to stream back to the UI def get_streaming_llm(self): - return AzureChatOpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler], deployment_name=self.llm_model, temperature=0, + if self.use_rbac: + return AzureChatOpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler], deployment_name=self.llm_model, temperature=0, + max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version, azure_ad_token_provider=self.token_provider) + else: + return AzureChatOpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler], deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) def get_embedding_model(self): - return AzureOpenAIEmbeddings(azure_deployment=self.embedding_model, chunk_size=1, validate_base_url=False) + if self.use_rbac: + return AzureOpenAIEmbeddings(azure_deployment=self.embedding_model, chunk_size=1, azure_ad_token_provider=self.token_provider) + else: + return AzureOpenAIEmbeddings(azure_deployment=self.embedding_model, chunk_size=1) def get_chat_completion_with_functions(self, messages: List[dict], functions: List[dict], function_call: str="auto"): return self.openai_client.chat.completions.create( diff --git a/requirements.txt b/requirements.txt index 7d50edb2f..3bb8b7fe3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,9 @@ azure-identity==1.15.0 Flask==2.3.2 -openai==0.27.8 +openai==1.6.1 azure-storage-blob==12.14.1 python-dotenv==1.0.0 -langchain==0.0.274 +langchain==0.0.354 azure-ai-formrecognizer==3.2.0 requests==2.31.0 tiktoken==0.2.0 From 5892d5deccac155034919c46dcb1b88c7bb60550 Mon Sep 17 00:00:00 2001 From: ChenxiJiang333 <119990644+ChenxiJiang333@users.noreply.github.com> Date: Thu, 4 Jan 2024 16:22:59 +0800 Subject: [PATCH 24/37] Update requirements.txt --- backend/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/requirements.txt b/backend/requirements.txt index 7aa135def..a975baf32 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,7 +1,7 @@ azure-functions streamlit==1.24.0 -openai==0.27.8 +openai==1.6.1 matplotlib==3.6.3 plotly==5.12.0 scipy==1.10.0 @@ -23,4 +23,4 @@ chardet==5.1.0 azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 pandas==1.5.1 -python-docx==0.8.11 \ No newline at end of file +python-docx==0.8.11 From 2b683de8bfaf24e1652fca35737d552f33c285af Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 4 Jan 2024 16:54:04 +0800 Subject: [PATCH 25/37] revert --- app.py | 4 ++-- backend/utilities/QuestionHandler.py | 1 - backend/utilities/helpers/LLMHelper.py | 8 ++++---- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app.py b/app.py index 8e4da0296..c71751992 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,7 @@ import os import logging import requests -import openai +from openai import AzureOpenAI # Fixing MIME types for static files under Windows import mimetypes @@ -214,7 +214,7 @@ def conversation_without_data(request): "content": message["content"] }) - response = openai.ChatCompletion.create( + response = openai_client.chat.completions.create( engine=AZURE_OPENAI_MODEL, messages = messages, temperature=float(AZURE_OPENAI_TEMPERATURE), diff --git a/backend/utilities/QuestionHandler.py b/backend/utilities/QuestionHandler.py index afe432160..b5e386ba6 100644 --- a/backend/utilities/QuestionHandler.py +++ b/backend/utilities/QuestionHandler.py @@ -1,5 +1,4 @@ import os -import openai import logging import re import json diff --git a/backend/utilities/helpers/LLMHelper.py b/backend/utilities/helpers/LLMHelper.py index 14e09f5d9..394658fbf 100644 --- a/backend/utilities/helpers/LLMHelper.py +++ b/backend/utilities/helpers/LLMHelper.py @@ -1,7 +1,7 @@ -import openai +from openai import AzureOpenAI from typing import List from langchain.chat_models import AzureChatOpenAI -from langchain.embeddings import OpenAIEmbeddings +from langchain.embeddings import AzureOpenAIEmbeddings from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler from .EnvHelper import EnvHelper @@ -42,7 +42,7 @@ def get_embedding_model(self): return AzureOpenAIEmbeddings(azure_deployment=self.embedding_model, chunk_size=1) def get_chat_completion_with_functions(self, messages: List[dict], functions: List[dict], function_call: str="auto"): - return openai.ChatCompletion.create( + return self.openai_client.chat.completions.create( deployment_id=self.llm_model, messages=messages, functions=functions, @@ -50,7 +50,7 @@ def get_chat_completion_with_functions(self, messages: List[dict], functions: Li ) def get_chat_completion(self, messages: List[dict]): - return openai.ChatCompletion.create( + return self.openai_client.chat.completions.create( deployment_id=self.llm_model, messages=messages, ) From 397ab11b69475168edcc4c43d798a12da03010a8 Mon Sep 17 00:00:00 2001 From: ChenxiJiang333 Date: Thu, 4 Jan 2024 17:22:26 +0800 Subject: [PATCH 26/37] fix chat --- app.py | 2 +- backend/utilities/helpers/LLMHelper.py | 4 ++-- .../utilities/orchestrator/OpenAIFunctions.py | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app.py b/app.py index c71751992..78745ac17 100644 --- a/app.py +++ b/app.py @@ -215,7 +215,7 @@ def conversation_without_data(request): }) response = openai_client.chat.completions.create( - engine=AZURE_OPENAI_MODEL, + model=AZURE_OPENAI_MODEL, messages = messages, temperature=float(AZURE_OPENAI_TEMPERATURE), max_tokens=int(AZURE_OPENAI_MAX_TOKENS), diff --git a/backend/utilities/helpers/LLMHelper.py b/backend/utilities/helpers/LLMHelper.py index 394658fbf..dd65692f4 100644 --- a/backend/utilities/helpers/LLMHelper.py +++ b/backend/utilities/helpers/LLMHelper.py @@ -43,7 +43,7 @@ def get_embedding_model(self): def get_chat_completion_with_functions(self, messages: List[dict], functions: List[dict], function_call: str="auto"): return self.openai_client.chat.completions.create( - deployment_id=self.llm_model, + model=self.llm_model, messages=messages, functions=functions, function_call=function_call, @@ -51,6 +51,6 @@ def get_chat_completion_with_functions(self, messages: List[dict], functions: Li def get_chat_completion(self, messages: List[dict]): return self.openai_client.chat.completions.create( - deployment_id=self.llm_model, + model=self.llm_model, messages=messages, ) diff --git a/backend/utilities/orchestrator/OpenAIFunctions.py b/backend/utilities/orchestrator/OpenAIFunctions.py index d04d1a98e..4e4fed426 100644 --- a/backend/utilities/orchestrator/OpenAIFunctions.py +++ b/backend/utilities/orchestrator/OpenAIFunctions.py @@ -76,13 +76,13 @@ def orchestrate(self, user_message: str, chat_history: List[dict], **kwargs: dic messages.append({"role": "user", "content": user_message}) result = llm_helper.get_chat_completion_with_functions(messages, self.functions, function_call="auto") - self.log_tokens(prompt_tokens=result['usage']['prompt_tokens'], completion_tokens=result['usage']['completion_tokens']) + self.log_tokens(prompt_tokens=result.usage.prompt_tokens, completion_tokens=result.usage.completion_tokens) # TODO: call content safety if needed - if result['choices'][0]['finish_reason'] == "function_call": - if result['choices'][0]['message'].function_call.name == "search_documents": - question = json.loads(result['choices'][0]['message']['function_call']['arguments'])['question'] + if result.choices[0].finish_reason == "function_call": + if result.choices[0].message.function_call.name == "search_documents": + question = json.loads(result.choices[0].message.function_call.arguments)['question'] # run answering chain answering_tool = QuestionAnswerTool() answer = answering_tool.answer_question(question, chat_history) @@ -94,14 +94,14 @@ def orchestrate(self, user_message: str, chat_history: List[dict], **kwargs: dic post_prompt_tool = PostPromptTool() answer = post_prompt_tool.validate_answer(answer) self.log_tokens(prompt_tokens=answer.prompt_tokens, completion_tokens=answer.completion_tokens) - elif result['choices'][0]['message'].function_call.name == "text_processing": - text = json.loads(result['choices'][0]['message']['function_call']['arguments'])['text'] - operation = json.loads(result['choices'][0]['message']['function_call']['arguments'])['operation'] + elif result.choices[0].message.function_call.name == "text_processing": + text = json.loads(result.choices[0].message.function_call.arguments)['text'] + operation = json.loads(result.choices[0].message.function_call.arguments)['operation'] text_processing_tool = TextProcessingTool() answer = text_processing_tool.answer_question(user_message, chat_history, text=text, operation=operation) self.log_tokens(prompt_tokens=answer.prompt_tokens, completion_tokens=answer.completion_tokens) else: - text = result['choices'][0]['message']['content'] + text = result.choices[0].message.content answer = Answer(question=user_message, answer=text) # Call Content Safety tool From 1e21f2a8c294c5a51d300e13765bb3f683700680 Mon Sep 17 00:00:00 2001 From: ChenxiJiang333 <119990644+ChenxiJiang333@users.noreply.github.com> Date: Thu, 4 Jan 2024 17:26:18 +0800 Subject: [PATCH 27/37] Update TextProcessingTool.py --- backend/utilities/tools/TextProcessingTool.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/utilities/tools/TextProcessingTool.py b/backend/utilities/tools/TextProcessingTool.py index 2140c677c..d814d3210 100644 --- a/backend/utilities/tools/TextProcessingTool.py +++ b/backend/utilities/tools/TextProcessingTool.py @@ -23,8 +23,8 @@ def answer_question(self, question: str, chat_history: List[dict], **kwargs: dic ) answer = Answer(question=question, - answer=result['choices'][0]['message']['content'], + answer=result.choices[0].message.content, source_documents=[], - prompt_tokens=result['usage']['prompt_tokens'], - completion_tokens=result['usage']['completion_tokens']) - return answer \ No newline at end of file + prompt_tokens=result.usage.prompt_tokens, + completion_tokens=result.usage.completion_tokens) + return answer From b25cce56c51a79e3f79535870acbfd671c42374b Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 10 Jan 2024 17:23:51 +0800 Subject: [PATCH 28/37] update KeyVault options --- app.py | 16 +++-- backend/pages/01_Ingest_Data.py | 6 +- backend/requirements.txt | 1 + .../helpers/AzureBlobStorageHelper.py | 15 +++-- .../helpers/AzureFormRecognizerHelper.py | 2 +- backend/utilities/helpers/EnvHelper.py | 25 +++++--- backend/utilities/helpers/LLMHelper.py | 10 +-- .../utilities/tools/ContentSafetyChecker.py | 2 +- infra/app/adminweb.bicep | 19 ++++-- infra/app/function.bicep | 19 ++++-- infra/app/storekeys.bicep | 63 +++++++++++++++++++ infra/app/web.bicep | 19 ++++-- infra/main.bicep | 49 ++++++++++++++- infra/main.bicepparam | 2 +- requirements.txt | 3 +- 15 files changed, 202 insertions(+), 49 deletions(-) create mode 100644 infra/app/storekeys.bicep diff --git a/app.py b/app.py index 78745ac17..31521137f 100644 --- a/app.py +++ b/app.py @@ -14,6 +14,7 @@ from dotenv import load_dotenv from backend.utilities.QuestionHandler import QuestionHandler from azure.identity import DefaultAzureCredential, get_bearer_token_provider +from azure.keyvault.secrets import SecretClient load_dotenv() @@ -24,12 +25,20 @@ def static_file(path): return app.send_static_file(path) -USE_RBAC = True if os.environ.get("AUTH_TYPE") == 'rbac' else False +AUTH_TYPE = os.environ.get("AUTH_TYPE") + +if not AUTH_TYPE == 'rbac' and os.environ.get("USE_KEY_VAULT"): + credential = DefaultAzureCredential() + secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + AZURE_SEARCH_KEY = secret_client.get_secret(os.environ.get("AZURE_SEARCH_KEY")).value + AZURE_OPENAI_KEY = secret_client.get_secret(os.environ.get("AZURE_OPENAI_KEY")).value +else: + AZURE_SEARCH_KEY = None if AUTH_TYPE == 'rbac' else os.environ.get("AZURE_SEARCH_KEY") + AZURE_OPENAI_KEY = None if AUTH_TYPE == 'rbac' else os.environ.get("AZURE_OPENAI_KEY") # ACS Integration Settings AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") -AZURE_SEARCH_KEY = None if USE_RBAC else os.environ.get("AZURE_SEARCH_KEY") AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.environ.get("AZURE_SEARCH_USE_SEMANTIC_SEARCH", False) AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.environ.get("AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG", "default") AZURE_SEARCH_TOP_K = os.environ.get("AZURE_SEARCH_TOP_K", 5) @@ -42,7 +51,6 @@ def static_file(path): # AOAI Integration Settings AZURE_OPENAI_RESOURCE = os.environ.get("AZURE_OPENAI_RESOURCE") AZURE_OPENAI_MODEL = os.environ.get("AZURE_OPENAI_MODEL") -AZURE_OPENAI_KEY = os.environ.get("AZURE_OPENAI_KEY") AZURE_OPENAI_TEMPERATURE = os.environ.get("AZURE_OPENAI_TEMPERATURE", 0) AZURE_OPENAI_TOP_P = os.environ.get("AZURE_OPENAI_TOP_P", 1.0) AZURE_OPENAI_MAX_TOKENS = os.environ.get("AZURE_OPENAI_MAX_TOKENS", 1000) @@ -195,7 +203,7 @@ def stream_without_data(response): def conversation_without_data(request): - if USE_RBAC: + if AUTH_TYPE == 'rbac': openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, azure_ad_token_provider=AZURE_TOKEN_PROVIDER) else: openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, api_key=AZURE_OPENAI_KEY) diff --git a/backend/pages/01_Ingest_Data.py b/backend/pages/01_Ingest_Data.py index 1c6ad8486..3681e1fc5 100644 --- a/backend/pages/01_Ingest_Data.py +++ b/backend/pages/01_Ingest_Data.py @@ -8,6 +8,7 @@ import logging import requests from azure.identity import DefaultAzureCredential +from azure.keyvault.secrets import SecretClient from azure.storage.blob import BlobServiceClient, generate_blob_sas, ContentSettings, UserDelegationKey import urllib.parse from utilities.helpers.ConfigHelper import ConfigHelper @@ -88,7 +89,10 @@ def upload_file(bytes_data: bytes, file_name: str, content_type: Optional[str] = blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type+charset)) st.session_state['file_url'] = blob_client.url + '?' + generate_blob_sas(account_name, container_name, file_name,user_delegation_key=user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) else: - account_key = os.getenv("AZURE_BLOB_ACCOUNT_KEY") + if os.environ.get("USE_KEY_VAULT"): + credential = DefaultAzureCredential() + secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) + account_key = secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY")).value if os.getenv("USE_KEY_VAULT") else os.getenv("AZURE_BLOB_ACCOUNT_KEY") container_name = os.getenv('AZURE_BLOB_CONTAINER_NAME') if account_name == None or account_key == None or container_name == None: raise ValueError("Please provide values for AZURE_BLOB_ACCOUNT_NAME, AZURE_BLOB_ACCOUNT_KEY and AZURE_BLOB_CONTAINER_NAME") diff --git a/backend/requirements.txt b/backend/requirements.txt index a975baf32..fe90aedf1 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -24,3 +24,4 @@ azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 pandas==1.5.1 python-docx==0.8.11 +azure-keyvault-secrets==4.4.* diff --git a/backend/utilities/helpers/AzureBlobStorageHelper.py b/backend/utilities/helpers/AzureBlobStorageHelper.py index 9afc6bff8..7de46c2f0 100644 --- a/backend/utilities/helpers/AzureBlobStorageHelper.py +++ b/backend/utilities/helpers/AzureBlobStorageHelper.py @@ -10,9 +10,8 @@ def __init__(self, account_name: Optional[str] = None, account_key: Optional[str env_helper : EnvHelper = EnvHelper() - self.use_rbac = False - if os.environ.get("AUTH_TYPE") == 'rbac': - self.use_rbac = True + self.auth_type = env_helper.AUTH_TYPE + if self.auth_type == 'rbac': self.account_name = account_name if account_name else env_helper.AZURE_BLOB_ACCOUNT_NAME self.container_name : str = container_name if container_name else env_helper.AZURE_BLOB_CONTAINER_NAME credential = DefaultAzureCredential() @@ -44,7 +43,7 @@ def upload_file(self, bytes_data, file_name, content_type='application/pdf'): blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type)) # Generate a SAS URL to the blob and return it sas_url = blob_client.url + '?' - if self.use_rbac: + if self.auth_type == 'rbac': sas_url += generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) else: sas_url += generate_blob_sas(self.account_name, self.container_name, file_name, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) @@ -72,7 +71,7 @@ def get_all_files(self): container_client = self.blob_service_client.get_container_client(self.container_name) blob_list = container_client.list_blobs(include='metadata') # sas = generate_blob_sas(account_name, container_name, blob.name,account_key=account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - if self.use_rbac: + if self.auth_type == 'rbac': sas = generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) else: sas = generate_container_sas(self.account_name, self.container_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) @@ -100,7 +99,7 @@ def get_all_files(self): return files def upsert_blob_metadata(self, file_name, metadata): - if self.use_rbac: + if self.auth_type == 'rbac': blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) else: blob_client = BlobServiceClient.from_connection_string(self.connect_str).get_blob_client(container=self.container_name, blob=file_name) @@ -113,14 +112,14 @@ def upsert_blob_metadata(self, file_name, metadata): def get_container_sas(self): # Generate a SAS URL to the container and return it - if self.use_rbac: + if self.auth_type == 'rbac': return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) else: return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) def get_blob_sas(self, file_name): # Generate a SAS URL to the blob and return it - if self.use_rbac: + if self.auth_type == 'rbac': return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, user_delegation_key= self.user_delegation_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) else: return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, account_key= self.account_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) diff --git a/backend/utilities/helpers/AzureFormRecognizerHelper.py b/backend/utilities/helpers/AzureFormRecognizerHelper.py index 14990b7b1..a026217a5 100644 --- a/backend/utilities/helpers/AzureFormRecognizerHelper.py +++ b/backend/utilities/helpers/AzureFormRecognizerHelper.py @@ -11,7 +11,7 @@ def __init__(self) -> None: env_helper : EnvHelper = EnvHelper() self.AZURE_FORM_RECOGNIZER_ENDPOINT : str = env_helper.AZURE_FORM_RECOGNIZER_ENDPOINT - if os.environ.get("AUTH_TYPE") == 'rbac': + if env_helper.AUTH_TYPE == 'rbac': self.document_analysis_client = DocumentAnalysisClient( endpoint=self.AZURE_FORM_RECOGNIZER_ENDPOINT, credential=DefaultAzureCredential() ) diff --git a/backend/utilities/helpers/EnvHelper.py b/backend/utilities/helpers/EnvHelper.py index a9b564d16..c619865b4 100644 --- a/backend/utilities/helpers/EnvHelper.py +++ b/backend/utilities/helpers/EnvHelper.py @@ -2,6 +2,7 @@ import logging from dotenv import load_dotenv from azure.identity import DefaultAzureCredential, get_bearer_token_provider +from azure.keyvault.secrets import SecretClient logger = logging.getLogger(__name__) @@ -9,12 +10,19 @@ class EnvHelper: def __init__(self, **kwargs) -> None: load_dotenv() - self.USE_RBAC = True if os.environ.get("AUTH_TYPE") == 'rbac' else False - + self.AUTH_TYPE =os.environ.get("AUTH_TYPE") + if not self.AUTH_TYPE == 'rbac' and os.environ.get("USE_KEY_VAULT"): + self.credential = DefaultAzureCredential() + self.secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), self.credential) + self.AZURE_SEARCH_KEY = self.secret_client.get_secret(os.environ.get("AZURE_SEARCH_KEY")).value + self.AZURE_OPENAI_KEY = self.secret_client.get_secret(os.environ.get("AZURE_OPENAI_KEY")).value + else: + self.AZURE_SEARCH_KEY = None if self.AUTH_TYPE == 'rbac' else os.environ.get("AZURE_SEARCH_KEY") + self.AZURE_OPENAI_KEY = None if self.AUTH_TYPE == 'rbac' else os.environ.get("AZURE_OPENAI_KEY") + # Azure Search self.AZURE_SEARCH_SERVICE = os.getenv('AZURE_SEARCH_SERVICE', '') self.AZURE_SEARCH_INDEX = os.getenv('AZURE_SEARCH_INDEX', '') - self.AZURE_SEARCH_KEY = None if self.USE_RBAC else os.getenv('AZURE_SEARCH_KEY', '') self.AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.getenv('AZURE_SEARCH_USE_SEMANTIC_SEARCH', '') self.AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.getenv('AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG', '') self.AZURE_SEARCH_INDEX_IS_PRECHUNKED = os.getenv('AZURE_SEARCH_INDEX_IS_PRECHUNKED', '') @@ -33,7 +41,6 @@ def __init__(self, **kwargs) -> None: # Azure OpenAI self.AZURE_OPENAI_RESOURCE = os.getenv('AZURE_OPENAI_RESOURCE', '') self.AZURE_OPENAI_MODEL = os.getenv('AZURE_OPENAI_MODEL', '') - self.AZURE_OPENAI_KEY = os.getenv('AZURE_OPENAI_KEY') self.AZURE_OPENAI_MODEL_NAME = os.getenv('AZURE_OPENAI_MODEL_NAME', '') self.AZURE_OPENAI_TEMPERATURE = os.getenv('AZURE_OPENAI_TEMPERATURE', '') self.AZURE_OPENAI_TOP_P = os.getenv('AZURE_OPENAI_TOP_P', '') @@ -45,11 +52,11 @@ def __init__(self, **kwargs) -> None: self.AZURE_OPENAI_EMBEDDING_MODEL = os.getenv('AZURE_OPENAI_EMBEDDING_MODEL', '') self.AZURE_TOKEN_PROVIDER = get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default") # Set env for OpenAI SDK - self.OPENAI_API_TYPE = "azuread" if self.USE_RBAC else "azure" + self.OPENAI_API_TYPE = "azuread" if self.AUTH_TYPE == 'rbac' else "azure" self.OPENAI_API_BASE = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" self.OPENAI_API_KEY = self.AZURE_OPENAI_KEY self.OPENAI_API_VERSION = self.AZURE_OPENAI_API_VERSION - os.environ["OPENAI_API_TYPE"] = "azuread" if self.USE_RBAC else "azure" + os.environ["OPENAI_API_TYPE"] = "azuread" if self.AUTH_TYPE == 'rbac' else "azure" os.environ["OPENAI_API_BASE"] = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" os.environ["OPENAI_API_KEY"] = self.AZURE_OPENAI_KEY os.environ["OPENAI_API_VERSION"] = self.AZURE_OPENAI_API_VERSION @@ -59,16 +66,16 @@ def __init__(self, **kwargs) -> None: self.DOCUMENT_PROCESSING_QUEUE_NAME = os.getenv('DOCUMENT_PROCESSING_QUEUE_NAME', '') # Azure Blob Storage self.AZURE_BLOB_ACCOUNT_NAME = os.getenv('AZURE_BLOB_ACCOUNT_NAME', '') - self.AZURE_BLOB_ACCOUNT_KEY = os.getenv('AZURE_BLOB_ACCOUNT_KEY', '') + self.AZURE_BLOB_ACCOUNT_KEY = self.secret_client.get_secret(os.getenv("AZURE_BLOB_ACCOUNT_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv("AZURE_BLOB_ACCOUNT_KEY", '') self.AZURE_BLOB_CONTAINER_NAME = os.getenv('AZURE_BLOB_CONTAINER_NAME', '') # Azure Form Recognizer self.AZURE_FORM_RECOGNIZER_ENDPOINT = os.getenv('AZURE_FORM_RECOGNIZER_ENDPOINT', '') - self.AZURE_FORM_RECOGNIZER_KEY = os.getenv('AZURE_FORM_RECOGNIZER_KEY', '') + self.AZURE_FORM_RECOGNIZER_KEY = self.secret_client.get_secret(os.getenv("AZURE_FORM_RECOGNIZER_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv('AZURE_FORM_RECOGNIZER_KEY', '') # Azure App Insights self.APPINSIGHTS_CONNECTION_STRING = os.getenv('APPINSIGHTS_CONNECTION_STRING', '') # Azure AI Content Safety self.AZURE_CONTENT_SAFETY_ENDPOINT = os.getenv('AZURE_CONTENT_SAFETY_ENDPOINT', '') - self.AZURE_CONTENT_SAFETY_KEY = os.getenv('AZURE_CONTENT_SAFETY_KEY', '') + self.AZURE_CONTENT_SAFETY_KEY = self.secret_client.get_secret(os.getenv("AZURE_CONTENT_SAFETY_KEY", '')).value if os.getenv("USE_KEY_VAULT", '') else os.getenv('AZURE_CONTENT_SAFETY_KEY', '') # Orchestration Settings self.ORCHESTRATION_STRATEGY = os.getenv('ORCHESTRATION_STRATEGY', 'openai_function') diff --git a/backend/utilities/helpers/LLMHelper.py b/backend/utilities/helpers/LLMHelper.py index dd65692f4..467cdfd07 100644 --- a/backend/utilities/helpers/LLMHelper.py +++ b/backend/utilities/helpers/LLMHelper.py @@ -8,10 +8,10 @@ class LLMHelper: def __init__(self): env_helper: EnvHelper = EnvHelper() - self.use_rbac = env_helper.USE_RBAC + self.auth_type = env_helper.AUTH_TYPE self.token_provider = env_helper.AZURE_TOKEN_PROVIDER - if self.use_rbac: + if self.auth_type == 'rbac': self.openai_client = AzureOpenAI(azure_endpoint=env_helper.OPENAI_API_BASE, api_version=env_helper.AZURE_OPENAI_API_VERSION, azure_ad_token_provider=self.token_provider) else: self.openai_client = AzureOpenAI(azure_endpoint=env_helper.OPENAI_API_BASE, api_version=env_helper.AZURE_OPENAI_API_VERSION, api_key=env_helper.OPENAI_API_KEY) @@ -21,14 +21,14 @@ def __init__(self): self.embedding_model = env_helper.AZURE_OPENAI_EMBEDDING_MODEL def get_llm(self): - if self.use_rbac: + if self.auth_type == 'rbac': return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version, azure_ad_token_provider=self.token_provider) else: return AzureChatOpenAI(deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) # TODO: This needs to have a custom callback to stream back to the UI def get_streaming_llm(self): - if self.use_rbac: + if self.auth_type == 'rbac': return AzureChatOpenAI(streaming=True, callbacks=[StreamingStdOutCallbackHandler], deployment_name=self.llm_model, temperature=0, max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version, azure_ad_token_provider=self.token_provider) else: @@ -36,7 +36,7 @@ def get_streaming_llm(self): max_tokens=self.llm_max_tokens, openai_api_version=self.openai_client._api_version) def get_embedding_model(self): - if self.use_rbac: + if self.auth_type == 'rbac': return AzureOpenAIEmbeddings(azure_deployment=self.embedding_model, chunk_size=1, azure_ad_token_provider=self.token_provider) else: return AzureOpenAIEmbeddings(azure_deployment=self.embedding_model, chunk_size=1) diff --git a/backend/utilities/tools/ContentSafetyChecker.py b/backend/utilities/tools/ContentSafetyChecker.py index faa073440..294bcaa33 100644 --- a/backend/utilities/tools/ContentSafetyChecker.py +++ b/backend/utilities/tools/ContentSafetyChecker.py @@ -13,7 +13,7 @@ class ContentSafetyChecker(AnswerProcessingBase): def __init__(self): env_helper = EnvHelper() - if os.environ.get("AUTH_TYPE") == 'rbac': + if env_helper.AUTH_TYPE == 'rbac': self.content_safety_client = ContentSafetyClient(env_helper.AZURE_CONTENT_SAFETY_ENDPOINT, DefaultAzureCredential()) else: self.content_safety_client = ContentSafetyClient(env_helper.AZURE_CONTENT_SAFETY_ENDPOINT, AzureKeyCredential(env_helper.AZURE_CONTENT_SAFETY_KEY)) diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index cdb54763b..35df87d10 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -14,6 +14,13 @@ param azureCognitiveSearchName string = '' @secure() param appSettings object = {} param serviceName string = 'adminweb' +param useKeyVault bool +param openAIKey string = '' +param storageAccountKey string = '' +param formRecognizerKey string = '' +param searchKey string = '' +param contentSafetyKey string = '' +param keyVaultEndpoint string = '' param authType string module adminweb '../core/host/appservice.bicep' = { @@ -28,11 +35,13 @@ module adminweb '../core/host/appservice.bicep' = { appServicePlanId: appServicePlanId appSettings: union(appSettings, { AUTH_TYPE: authType - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + USE_KEY_VAULT: useKeyVault ? useKeyVault : '' + AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' + AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/app/function.bicep b/infra/app/function.bicep index 9cfcba9c3..d813ee8e0 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -15,6 +15,13 @@ param azureOpenAIName string = '' param azureCognitiveSearchName string = '' param formRecognizerName string = '' param contentSafetyName string = '' +param useKeyVault bool +param openAIKey string = '' +param storageAccountKey string = '' +param formRecognizerKey string = '' +param searchKey string = '' +param contentSafetyKey string = '' +param keyVaultEndpoint string = '' param authType string module function '../core/host/functions.bicep' = { @@ -30,11 +37,13 @@ module function '../core/host/functions.bicep' = { runtimeVersion: runtimeVersion appSettings: union(appSettings, { AUTH_TYPE: authType - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + USE_KEY_VAULT: useKeyVault ? useKeyVault : '' + AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' + AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) } } diff --git a/infra/app/storekeys.bicep b/infra/app/storekeys.bicep new file mode 100644 index 000000000..d4001d9fc --- /dev/null +++ b/infra/app/storekeys.bicep @@ -0,0 +1,63 @@ +param keyVaultName string = '' +param storageAccountName string = '' +param azureOpenAIName string = '' +param azureCognitiveSearchName string = '' +param rgName string = '' +param formRecognizerName string = '' +param contentSafetyName string = '' +param storageAccountKeyName string = 'AZURE-STORAGE-ACCOUNT-KEY' +param openAIKeyName string = 'AZURE-OPEN-AI-KEY' +param searchKeyName string = 'AZURE-SEARCH-KEY' +param formRecognizerKeyName string = 'AZURE-FORM-RECOGNIZER-KEY' +param contentSafety string = 'AZURE-CONTENT-SAFETY-KEY' + + +resource storageAccountKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: storageAccountKeyName + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + } +} + +resource openAIKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: openAIKeyName + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + } +} + +resource searchKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: searchKeyName + properties: { + value: listAdminKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + } +} + +resource formRecognizerKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: formRecognizerKeyName + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + } +} + +resource contentSafetyKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: contentSafety + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + } +} + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { + name: keyVaultName +} + +output CONTENT_SAFETY_KEY string = contentSafetyKeySecret.name +output FORM_RECOGNIZER_KEY string = formRecognizerKeySecret.name +output SEARCH_KEY string = searchKeySecret.name +output OPENAI_KEY string = openAIKeySecret.name +output STORAGE_ACCOUNT_KEY string = storageAccountKeySecret.name diff --git a/infra/app/web.bicep b/infra/app/web.bicep index a2c7ab5bb..3e3c33bd4 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -14,6 +14,13 @@ param contentSafetyName string = '' @secure() param appSettings object = {} param serviceName string = 'web' +param useKeyVault bool +param openAIKey string = '' +param storageAccountKey string = '' +param formRecognizerKey string = '' +param searchKey string = '' +param contentSafetyKey string = '' +param keyVaultEndpoint string = '' param authType string module web '../core/host/appservice.bicep' = { @@ -28,11 +35,13 @@ module web '../core/host/appservice.bicep' = { appServicePlanId: appServicePlanId appSettings: union(appSettings, { AUTH_TYPE: authType - AZURE_OPENAI_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + USE_KEY_VAULT: useKeyVault ? useKeyVault : '' + AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' + AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/main.bicep b/infra/main.bicep index 5fe6bb985..18667cfd3 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -245,6 +245,20 @@ module openai 'core/ai/cognitiveservices.bicep' = { } } +module storekeys './app/storekeys.bicep' = if (useKeyVault) { + name: 'storekeys' + scope: rg + params: { + keyVaultName: keyVaultName + azureOpenAIName: openai.outputs.name + azureCognitiveSearchName: search.outputs.name + storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + formRecognizerName: formrecognizer.outputs.name + contentSafetyName: contentsafety.outputs.name + rgName: rgName + } +} + module search './core/search/search-services.bicep' = { name: azureCognitiveSearchName scope: rg @@ -292,7 +306,14 @@ module web './app/web.bicep' = { storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name - keyVaultName: useKeyVault ? keyvault.outputs.name : '' + openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' + storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' + formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' + searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' + contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + useKeyVault: useKeyVault + keyVaultName: useKeyVault || authType == 'rbac' ? keyvault.outputs.name : '' + keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' authType: authType appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' @@ -341,7 +362,14 @@ module adminweb './app/adminweb.bicep' = { storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name - keyVaultName: useKeyVault ? keyvault.outputs.name : '' + openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' + storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' + formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' + searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' + contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + useKeyVault: useKeyVault + keyVaultName: useKeyVault || authType == 'rbac' ? keyvault.outputs.name : '' + keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' authType: authType appSettings: { AZURE_SEARCH_SERVICE: 'https://${azureCognitiveSearchName}.search.windows.net' @@ -407,7 +435,14 @@ module function './app/function.bicep' = { formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name clientKey: clientKey - keyVaultName: useKeyVault ? keyvault.outputs.name : '' + openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' + storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' + formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' + searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' + contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + useKeyVault: useKeyVault + keyVaultName: useKeyVault || authType == 'rbac' ? keyvault.outputs.name : '' + keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' authType: authType appSettings: { FUNCTIONS_EXTENSION_VERSION: '~4' @@ -629,6 +664,8 @@ module searchRoleFunction 'core/security/role.bicep' = if (authType == 'rbac'){ output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString output APPINSIGHTS_INSTRUMENTATIONKEY string = monitoring.outputs.applicationInsightsInstrumentationKey +output AZURE_KEY_VAULT_ENDPOINT string = useKeyVault ? keyvault.outputs.endpoint : '' +output AZURE_KEY_VAULT_NAME string = useKeyVault ? keyvault.outputs.name : '' output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output AZURE_CONTENT_SAFETY_ENDPOINT string = contentsafety.outputs.endpoint @@ -658,3 +695,9 @@ output AZURE_BLOB_ACCOUNT_NAME string = storageAccountName output AZURE_OPENAI_RESOURCE string = azureOpenAIResourceName output AZURE_OPENAI_EMBEDDING_MODEL string = azureOpenAIEmbeddingModel output AZURE_OPENAI_MODEL string = azureOpenAIModel +output USE_KEY_VAULT bool = useKeyVault +output AZURE_OPENAI_KEY string = useKeyVault ? storekeys.outputs.OPENAI_KEY : '' +output AZURE_BLOB_ACCOUNT_KEY string = useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' +output AZURE_FORM_RECOGNIZER_KEY string = useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' +output AZURE_SEARCH_KEY string = useKeyVault ? storekeys.outputs.SEARCH_KEY: '' +output AZURE_CONTENT_SAFETY_KEY string = useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' diff --git a/infra/main.bicepparam b/infra/main.bicepparam index ede2fb0d0..0d8ce0b39 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -8,4 +8,4 @@ param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id' param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) -param authType = readEnvironmentVariable('AUTH_TYPE', 'rbac') +param authType = readEnvironmentVariable('AUTH_TYPE', 'keys') diff --git a/requirements.txt b/requirements.txt index 3bb8b7fe3..17a07ddcb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,5 @@ chardet==5.1.0 azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.9 azure-ai-contentsafety==1.0.0 -python-docx==0.8.11 \ No newline at end of file +python-docx==0.8.11 +azure-keyvault-secrets==4.4.* \ No newline at end of file From 332402afe0269fd871a31b68fc3ce84ca8afcb9e Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 11 Jan 2024 13:48:19 +0800 Subject: [PATCH 29/37] abstract auth type with define method --- app.py | 2 +- .../helpers/AzureBlobStorageHelper.py | 34 +++++++++---------- backend/utilities/helpers/EnvHelper.py | 2 +- infra/main.bicep | 4 +-- infra/main.bicepparam | 5 +-- 5 files changed, 23 insertions(+), 24 deletions(-) diff --git a/app.py b/app.py index 31521137f..f9dbd1eca 100644 --- a/app.py +++ b/app.py @@ -34,7 +34,7 @@ def static_file(path): AZURE_OPENAI_KEY = secret_client.get_secret(os.environ.get("AZURE_OPENAI_KEY")).value else: AZURE_SEARCH_KEY = None if AUTH_TYPE == 'rbac' else os.environ.get("AZURE_SEARCH_KEY") - AZURE_OPENAI_KEY = None if AUTH_TYPE == 'rbac' else os.environ.get("AZURE_OPENAI_KEY") + AZURE_OPENAI_KEY = "" if AUTH_TYPE == 'rbac' else os.environ.get("AZURE_OPENAI_KEY") # ACS Integration Settings AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") diff --git a/backend/utilities/helpers/AzureBlobStorageHelper.py b/backend/utilities/helpers/AzureBlobStorageHelper.py index 7de46c2f0..6cdf38179 100644 --- a/backend/utilities/helpers/AzureBlobStorageHelper.py +++ b/backend/utilities/helpers/AzureBlobStorageHelper.py @@ -36,18 +36,25 @@ def request_user_delegation_key(self, blob_service_client: BlobServiceClient) -> ) return user_delegation_key + def get_blob_sas_with_diff_key(self, file_name, auth_type): + if auth_type == 'rbac': + return generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + else: + return generate_blob_sas(self.account_name, self.container_name, file_name, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + + def get_container_sas_with_diff_key(self, auth_type): + if auth_type == 'rbac': + return generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + else: + return generate_container_sas(self.account_name, self.container_name, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + def upload_file(self, bytes_data, file_name, content_type='application/pdf'): # Create a blob client using the local file name as the name for the blob blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) # Upload the created file blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type)) # Generate a SAS URL to the blob and return it - sas_url = blob_client.url + '?' - if self.auth_type == 'rbac': - sas_url += generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - else: - sas_url += generate_blob_sas(self.account_name, self.container_name, file_name, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - return sas_url + return blob_client.url + '?' + self.get_blob_sas_with_diff_key(file_name=file_name, auth_type=self.auth_type) def download_file(self, file_name): blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) @@ -71,10 +78,7 @@ def get_all_files(self): container_client = self.blob_service_client.get_container_client(self.container_name) blob_list = container_client.list_blobs(include='metadata') # sas = generate_blob_sas(account_name, container_name, blob.name,account_key=account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - if self.auth_type == 'rbac': - sas = generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - else: - sas = generate_container_sas(self.account_name, self.container_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) + sas = self.get_container_sas_with_diff_key(auth_type=self.auth_type) files = [] converted_files = {} for blob in blob_list: @@ -112,14 +116,8 @@ def upsert_blob_metadata(self, file_name, metadata): def get_container_sas(self): # Generate a SAS URL to the container and return it - if self.auth_type == 'rbac': - return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) - else: - return "?" + generate_container_sas(account_name= self.account_name, container_name= self.container_name,account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=1)) + return "?" + self.get_container_sas_with_diff_key(auth_type=self.auth_type) def get_blob_sas(self, file_name): # Generate a SAS URL to the blob and return it - if self.auth_type == 'rbac': - return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, user_delegation_key= self.user_delegation_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) - else: - return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(account_name= self.account_name, container_name=self.container_name, blob_name= file_name, account_key= self.account_key, permission='r', expiry=datetime.utcnow() + timedelta(hours=1)) + return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + self.get_blob_sas_with_diff_key(file_name=file_name, auth_type=self.auth_type) diff --git a/backend/utilities/helpers/EnvHelper.py b/backend/utilities/helpers/EnvHelper.py index c619865b4..20f74d3e4 100644 --- a/backend/utilities/helpers/EnvHelper.py +++ b/backend/utilities/helpers/EnvHelper.py @@ -18,7 +18,7 @@ def __init__(self, **kwargs) -> None: self.AZURE_OPENAI_KEY = self.secret_client.get_secret(os.environ.get("AZURE_OPENAI_KEY")).value else: self.AZURE_SEARCH_KEY = None if self.AUTH_TYPE == 'rbac' else os.environ.get("AZURE_SEARCH_KEY") - self.AZURE_OPENAI_KEY = None if self.AUTH_TYPE == 'rbac' else os.environ.get("AZURE_OPENAI_KEY") + self.AZURE_OPENAI_KEY = "" if self.AUTH_TYPE == 'rbac' else os.environ.get("AZURE_OPENAI_KEY") # Azure Search self.AZURE_SEARCH_SERVICE = os.getenv('AZURE_SEARCH_SERVICE', '') diff --git a/infra/main.bicep b/infra/main.bicep index 18667cfd3..e9e31b000 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -171,7 +171,7 @@ resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = { } // Store secrets in a keyvault -module keyvault './core/security/keyvault.bicep' = if (useKeyVault) { +module keyvault './core/security/keyvault.bicep' = if (useKeyVault || authType == 'rbac') { name: 'keyvault' scope: rg params: { @@ -665,7 +665,7 @@ module searchRoleFunction 'core/security/role.bicep' = if (authType == 'rbac'){ output APPLICATIONINSIGHTS_CONNECTION_STRING string = monitoring.outputs.applicationInsightsConnectionString output APPINSIGHTS_INSTRUMENTATIONKEY string = monitoring.outputs.applicationInsightsInstrumentationKey output AZURE_KEY_VAULT_ENDPOINT string = useKeyVault ? keyvault.outputs.endpoint : '' -output AZURE_KEY_VAULT_NAME string = useKeyVault ? keyvault.outputs.name : '' +output AZURE_KEY_VAULT_NAME string = useKeyVault || authType == 'rbac' ? keyvault.outputs.name : '' output AZURE_LOCATION string = location output AZURE_TENANT_ID string = tenant().tenantId output AZURE_CONTENT_SAFETY_ENDPOINT string = contentsafety.outputs.endpoint diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 0d8ce0b39..340c69b11 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -6,6 +6,7 @@ param location = readEnvironmentVariable('AZURE_LOCATION', 'location') param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id') -param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) +// Please make sure to set this value to false when using rbac with AUTH_TYPE +param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'false')) -param authType = readEnvironmentVariable('AUTH_TYPE', 'keys') +param authType = readEnvironmentVariable('AUTH_TYPE', 'rbac') From bd092dfc59b122103dc45ccfb19d093fc4321965 Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 11 Jan 2024 14:29:50 +0800 Subject: [PATCH 30/37] remove invalid infra/core --- .../helpers/AzureBlobStorageHelper.py | 25 +- .../core/database/cosmos/cosmos-account.bicep | 49 ---- .../cosmos/mongo/cosmos-mongo-account.bicep | 23 -- .../cosmos/mongo/cosmos-mongo-db.bicep | 47 ---- .../cosmos/sql/cosmos-sql-account.bicep | 22 -- .../database/cosmos/sql/cosmos-sql-db.bicep | 74 ------ .../cosmos/sql/cosmos-sql-role-assign.bicep | 19 -- .../cosmos/sql/cosmos-sql-role-def.bicep | 30 --- .../database/postgresql/flexibleserver.bicep | 65 ------ infra/core/database/sqlserver/sqlserver.bicep | 130 ----------- infra/core/gateway/apim.bicep | 79 ------- infra/core/host/aks-agent-pool.bicep | 18 -- infra/core/host/aks-managed-cluster.bicep | 140 ------------ infra/core/host/aks.bicep | 214 ------------------ infra/core/host/container-app-upsert.bicep | 105 --------- infra/core/host/container-app.bicep | 162 ------------- .../host/container-apps-environment.bicep | 41 ---- infra/core/host/container-apps.bicep | 38 ---- infra/core/host/container-registry.bicep | 83 ------- infra/core/host/staticwebapp.bicep | 22 -- infra/core/networking/cdn-endpoint.bicep | 52 ----- infra/core/networking/cdn-profile.bicep | 34 --- infra/core/networking/cdn.bicep | 42 ---- infra/core/storage/storage-account.bicep | 64 ------ infra/core/testing/loadtesting.bicep | 15 -- infra/main.bicepparam | 4 +- 26 files changed, 9 insertions(+), 1588 deletions(-) delete mode 100644 infra/core/database/cosmos/cosmos-account.bicep delete mode 100644 infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep delete mode 100644 infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep delete mode 100644 infra/core/database/cosmos/sql/cosmos-sql-account.bicep delete mode 100644 infra/core/database/cosmos/sql/cosmos-sql-db.bicep delete mode 100644 infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep delete mode 100644 infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep delete mode 100644 infra/core/database/postgresql/flexibleserver.bicep delete mode 100644 infra/core/database/sqlserver/sqlserver.bicep delete mode 100644 infra/core/gateway/apim.bicep delete mode 100644 infra/core/host/aks-agent-pool.bicep delete mode 100644 infra/core/host/aks-managed-cluster.bicep delete mode 100644 infra/core/host/aks.bicep delete mode 100644 infra/core/host/container-app-upsert.bicep delete mode 100644 infra/core/host/container-app.bicep delete mode 100644 infra/core/host/container-apps-environment.bicep delete mode 100644 infra/core/host/container-apps.bicep delete mode 100644 infra/core/host/container-registry.bicep delete mode 100644 infra/core/host/staticwebapp.bicep delete mode 100644 infra/core/networking/cdn-endpoint.bicep delete mode 100644 infra/core/networking/cdn-profile.bicep delete mode 100644 infra/core/networking/cdn.bicep delete mode 100644 infra/core/storage/storage-account.bicep delete mode 100644 infra/core/testing/loadtesting.bicep diff --git a/backend/utilities/helpers/AzureBlobStorageHelper.py b/backend/utilities/helpers/AzureBlobStorageHelper.py index 6cdf38179..d63507399 100644 --- a/backend/utilities/helpers/AzureBlobStorageHelper.py +++ b/backend/utilities/helpers/AzureBlobStorageHelper.py @@ -18,9 +18,11 @@ def __init__(self, account_name: Optional[str] = None, account_key: Optional[str account_url = f"https://{self.account_name}.blob.core.windows.net/" self.blob_service_client = BlobServiceClient(account_url=account_url, credential=credential) self.user_delegation_key = self.request_user_delegation_key(blob_service_client=self.blob_service_client) + self.account_key = None else: self.account_name = account_name if account_name else env_helper.AZURE_BLOB_ACCOUNT_NAME self.account_key = account_key if account_key else env_helper.AZURE_BLOB_ACCOUNT_KEY + self.user_delegation_key = None self.connect_str = f"DefaultEndpointsProtocol=https;AccountName={self.account_name};AccountKey={self.account_key};EndpointSuffix=core.windows.net" self.container_name : str = container_name if container_name else env_helper.AZURE_BLOB_CONTAINER_NAME self.blob_service_client : BlobServiceClient = BlobServiceClient.from_connection_string(self.connect_str) @@ -36,25 +38,13 @@ def request_user_delegation_key(self, blob_service_client: BlobServiceClient) -> ) return user_delegation_key - def get_blob_sas_with_diff_key(self, file_name, auth_type): - if auth_type == 'rbac': - return generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - else: - return generate_blob_sas(self.account_name, self.container_name, file_name, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - - def get_container_sas_with_diff_key(self, auth_type): - if auth_type == 'rbac': - return generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - else: - return generate_container_sas(self.account_name, self.container_name, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - def upload_file(self, bytes_data, file_name, content_type='application/pdf'): # Create a blob client using the local file name as the name for the blob blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) # Upload the created file blob_client.upload_blob(bytes_data, overwrite=True, content_settings=ContentSettings(content_type=content_type)) - # Generate a SAS URL to the blob and return it - return blob_client.url + '?' + self.get_blob_sas_with_diff_key(file_name=file_name, auth_type=self.auth_type) + # Generate a SAS URL to the blob and return it, if auth_type is rbac, account_key is None, if not, user_delegation_key is None. + return blob_client.url + '?' + generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) def download_file(self, file_name): blob_client = self.blob_service_client.get_blob_client(container=self.container_name, blob=file_name) @@ -78,7 +68,7 @@ def get_all_files(self): container_client = self.blob_service_client.get_container_client(self.container_name) blob_list = container_client.list_blobs(include='metadata') # sas = generate_blob_sas(account_name, container_name, blob.name,account_key=account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) - sas = self.get_container_sas_with_diff_key(auth_type=self.auth_type) + sas = generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) files = [] converted_files = {} for blob in blob_list: @@ -116,8 +106,7 @@ def upsert_blob_metadata(self, file_name, metadata): def get_container_sas(self): # Generate a SAS URL to the container and return it - return "?" + self.get_container_sas_with_diff_key(auth_type=self.auth_type) - + return "?" + generate_container_sas(self.account_name, self.container_name, user_delegation_key=self.user_delegation_key, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) def get_blob_sas(self, file_name): # Generate a SAS URL to the blob and return it - return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + self.get_blob_sas_with_diff_key(file_name=file_name, auth_type=self.auth_type) + return f"https://{self.account_name}.blob.core.windows.net/{self.container_name}/{file_name}" + "?" + generate_blob_sas(self.account_name, self.container_name, file_name, user_delegation_key=self.user_delegation_key, account_key=self.account_key, permission="r", expiry=datetime.utcnow() + timedelta(hours=3)) diff --git a/infra/core/database/cosmos/cosmos-account.bicep b/infra/core/database/cosmos/cosmos-account.bicep deleted file mode 100644 index 6f8747f5a..000000000 --- a/infra/core/database/cosmos/cosmos-account.bicep +++ /dev/null @@ -1,49 +0,0 @@ -metadata description = 'Creates an Azure Cosmos DB account.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' -param keyVaultName string - -@allowed([ 'GlobalDocumentDB', 'MongoDB', 'Parse' ]) -param kind string - -resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' = { - name: name - kind: kind - location: location - tags: tags - properties: { - consistencyPolicy: { defaultConsistencyLevel: 'Session' } - locations: [ - { - locationName: location - failoverPriority: 0 - isZoneRedundant: false - } - ] - databaseAccountOfferType: 'Standard' - enableAutomaticFailover: false - enableMultipleWriteLocations: false - apiProperties: (kind == 'MongoDB') ? { serverVersion: '4.2' } : {} - capabilities: [ { name: 'EnableServerless' } ] - } -} - -resource cosmosConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: connectionStringKey - properties: { - value: cosmos.listConnectionStrings().connectionStrings[0].connectionString - } -} - -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: keyVaultName -} - -output connectionStringKey string = connectionStringKey -output endpoint string = cosmos.properties.documentEndpoint -output id string = cosmos.id -output name string = cosmos.name diff --git a/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep b/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep deleted file mode 100644 index 4aafbf386..000000000 --- a/infra/core/database/cosmos/mongo/cosmos-mongo-account.bicep +++ /dev/null @@ -1,23 +0,0 @@ -metadata description = 'Creates an Azure Cosmos DB for MongoDB account.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param keyVaultName string -param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' - -module cosmos '../../cosmos/cosmos-account.bicep' = { - name: 'cosmos-account' - params: { - name: name - location: location - connectionStringKey: connectionStringKey - keyVaultName: keyVaultName - kind: 'MongoDB' - tags: tags - } -} - -output connectionStringKey string = cosmos.outputs.connectionStringKey -output endpoint string = cosmos.outputs.endpoint -output id string = cosmos.outputs.id diff --git a/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep b/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep deleted file mode 100644 index 2a670578d..000000000 --- a/infra/core/database/cosmos/mongo/cosmos-mongo-db.bicep +++ /dev/null @@ -1,47 +0,0 @@ -metadata description = 'Creates an Azure Cosmos DB for MongoDB account with a database.' -param accountName string -param databaseName string -param location string = resourceGroup().location -param tags object = {} - -param collections array = [] -param connectionStringKey string = 'AZURE-COSMOS-CONNECTION-STRING' -param keyVaultName string - -module cosmos 'cosmos-mongo-account.bicep' = { - name: 'cosmos-mongo-account' - params: { - name: accountName - location: location - keyVaultName: keyVaultName - tags: tags - connectionStringKey: connectionStringKey - } -} - -resource database 'Microsoft.DocumentDB/databaseAccounts/mongodbDatabases@2022-08-15' = { - name: '${accountName}/${databaseName}' - tags: tags - properties: { - resource: { id: databaseName } - } - - resource list 'collections' = [for collection in collections: { - name: collection.name - properties: { - resource: { - id: collection.id - shardKey: { _id: collection.shardKey } - indexes: [ { key: { keys: [ collection.indexKey ] } } ] - } - } - }] - - dependsOn: [ - cosmos - ] -} - -output connectionStringKey string = connectionStringKey -output databaseName string = databaseName -output endpoint string = cosmos.outputs.endpoint diff --git a/infra/core/database/cosmos/sql/cosmos-sql-account.bicep b/infra/core/database/cosmos/sql/cosmos-sql-account.bicep deleted file mode 100644 index 8431135ee..000000000 --- a/infra/core/database/cosmos/sql/cosmos-sql-account.bicep +++ /dev/null @@ -1,22 +0,0 @@ -metadata description = 'Creates an Azure Cosmos DB for NoSQL account.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param keyVaultName string - -module cosmos '../../cosmos/cosmos-account.bicep' = { - name: 'cosmos-account' - params: { - name: name - location: location - tags: tags - keyVaultName: keyVaultName - kind: 'GlobalDocumentDB' - } -} - -output connectionStringKey string = cosmos.outputs.connectionStringKey -output endpoint string = cosmos.outputs.endpoint -output id string = cosmos.outputs.id -output name string = cosmos.outputs.name diff --git a/infra/core/database/cosmos/sql/cosmos-sql-db.bicep b/infra/core/database/cosmos/sql/cosmos-sql-db.bicep deleted file mode 100644 index 265880dc6..000000000 --- a/infra/core/database/cosmos/sql/cosmos-sql-db.bicep +++ /dev/null @@ -1,74 +0,0 @@ -metadata description = 'Creates an Azure Cosmos DB for NoSQL account with a database.' -param accountName string -param databaseName string -param location string = resourceGroup().location -param tags object = {} - -param containers array = [] -param keyVaultName string -param principalIds array = [] - -module cosmos 'cosmos-sql-account.bicep' = { - name: 'cosmos-sql-account' - params: { - name: accountName - location: location - tags: tags - keyVaultName: keyVaultName - } -} - -resource database 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases@2022-05-15' = { - name: '${accountName}/${databaseName}' - properties: { - resource: { id: databaseName } - } - - resource list 'containers' = [for container in containers: { - name: container.name - properties: { - resource: { - id: container.id - partitionKey: { paths: [ container.partitionKey ] } - } - options: {} - } - }] - - dependsOn: [ - cosmos - ] -} - -module roleDefinition 'cosmos-sql-role-def.bicep' = { - name: 'cosmos-sql-role-definition' - params: { - accountName: accountName - } - dependsOn: [ - cosmos - database - ] -} - -// We need batchSize(1) here because sql role assignments have to be done sequentially -@batchSize(1) -module userRole 'cosmos-sql-role-assign.bicep' = [for principalId in principalIds: if (!empty(principalId)) { - name: 'cosmos-sql-user-role-${uniqueString(principalId)}' - params: { - accountName: accountName - roleDefinitionId: roleDefinition.outputs.id - principalId: principalId - } - dependsOn: [ - cosmos - database - ] -}] - -output accountId string = cosmos.outputs.id -output accountName string = cosmos.outputs.name -output connectionStringKey string = cosmos.outputs.connectionStringKey -output databaseName string = databaseName -output endpoint string = cosmos.outputs.endpoint -output roleDefinitionId string = roleDefinition.outputs.id diff --git a/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep b/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep deleted file mode 100644 index 3949efef0..000000000 --- a/infra/core/database/cosmos/sql/cosmos-sql-role-assign.bicep +++ /dev/null @@ -1,19 +0,0 @@ -metadata description = 'Creates a SQL role assignment under an Azure Cosmos DB account.' -param accountName string - -param roleDefinitionId string -param principalId string = '' - -resource role 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2022-05-15' = { - parent: cosmos - name: guid(roleDefinitionId, principalId, cosmos.id) - properties: { - principalId: principalId - roleDefinitionId: roleDefinitionId - scope: cosmos.id - } -} - -resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { - name: accountName -} diff --git a/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep b/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep deleted file mode 100644 index 778d6dc47..000000000 --- a/infra/core/database/cosmos/sql/cosmos-sql-role-def.bicep +++ /dev/null @@ -1,30 +0,0 @@ -metadata description = 'Creates a SQL role definition under an Azure Cosmos DB account.' -param accountName string - -resource roleDefinition 'Microsoft.DocumentDB/databaseAccounts/sqlRoleDefinitions@2022-08-15' = { - parent: cosmos - name: guid(cosmos.id, accountName, 'sql-role') - properties: { - assignableScopes: [ - cosmos.id - ] - permissions: [ - { - dataActions: [ - 'Microsoft.DocumentDB/databaseAccounts/readMetadata' - 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*' - 'Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*' - ] - notDataActions: [] - } - ] - roleName: 'Reader Writer' - type: 'CustomRole' - } -} - -resource cosmos 'Microsoft.DocumentDB/databaseAccounts@2022-08-15' existing = { - name: accountName -} - -output id string = roleDefinition.id diff --git a/infra/core/database/postgresql/flexibleserver.bicep b/infra/core/database/postgresql/flexibleserver.bicep deleted file mode 100644 index 7e26b1a8d..000000000 --- a/infra/core/database/postgresql/flexibleserver.bicep +++ /dev/null @@ -1,65 +0,0 @@ -metadata description = 'Creates an Azure Database for PostgreSQL - Flexible Server.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param sku object -param storage object -param administratorLogin string -@secure() -param administratorLoginPassword string -param databaseNames array = [] -param allowAzureIPsFirewall bool = false -param allowAllIPsFirewall bool = false -param allowedSingleIPs array = [] - -// PostgreSQL version -param version string - -// Latest official version 2022-12-01 does not have Bicep types available -resource postgresServer 'Microsoft.DBforPostgreSQL/flexibleServers@2022-12-01' = { - location: location - tags: tags - name: name - sku: sku - properties: { - version: version - administratorLogin: administratorLogin - administratorLoginPassword: administratorLoginPassword - storage: storage - highAvailability: { - mode: 'Disabled' - } - } - - resource database 'databases' = [for name in databaseNames: { - name: name - }] - - resource firewall_all 'firewallRules' = if (allowAllIPsFirewall) { - name: 'allow-all-IPs' - properties: { - startIpAddress: '0.0.0.0' - endIpAddress: '255.255.255.255' - } - } - - resource firewall_azure 'firewallRules' = if (allowAzureIPsFirewall) { - name: 'allow-all-azure-internal-IPs' - properties: { - startIpAddress: '0.0.0.0' - endIpAddress: '0.0.0.0' - } - } - - resource firewall_single 'firewallRules' = [for ip in allowedSingleIPs: { - name: 'allow-single-${replace(ip, '.', '')}' - properties: { - startIpAddress: ip - endIpAddress: ip - } - }] - -} - -output POSTGRES_DOMAIN_NAME string = postgresServer.properties.fullyQualifiedDomainName diff --git a/infra/core/database/sqlserver/sqlserver.bicep b/infra/core/database/sqlserver/sqlserver.bicep deleted file mode 100644 index 84f2cc2c8..000000000 --- a/infra/core/database/sqlserver/sqlserver.bicep +++ /dev/null @@ -1,130 +0,0 @@ -metadata description = 'Creates an Azure SQL Server instance.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param appUser string = 'appUser' -param databaseName string -param keyVaultName string -param sqlAdmin string = 'sqlAdmin' -param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' - -@secure() -param sqlAdminPassword string -@secure() -param appUserPassword string - -resource sqlServer 'Microsoft.Sql/servers@2022-05-01-preview' = { - name: name - location: location - tags: tags - properties: { - version: '12.0' - minimalTlsVersion: '1.2' - publicNetworkAccess: 'Enabled' - administratorLogin: sqlAdmin - administratorLoginPassword: sqlAdminPassword - } - - resource database 'databases' = { - name: databaseName - location: location - } - - resource firewall 'firewallRules' = { - name: 'Azure Services' - properties: { - // Allow all clients - // Note: range [0.0.0.0-0.0.0.0] means "allow all Azure-hosted clients only". - // This is not sufficient, because we also want to allow direct access from developer machine, for debugging purposes. - startIpAddress: '0.0.0.1' - endIpAddress: '255.255.255.254' - } - } -} - -resource sqlDeploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = { - name: '${name}-deployment-script' - location: location - kind: 'AzureCLI' - properties: { - azCliVersion: '2.37.0' - retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running - timeout: 'PT5M' // Five minutes - cleanupPreference: 'OnSuccess' - environmentVariables: [ - { - name: 'APPUSERNAME' - value: appUser - } - { - name: 'APPUSERPASSWORD' - secureValue: appUserPassword - } - { - name: 'DBNAME' - value: databaseName - } - { - name: 'DBSERVER' - value: sqlServer.properties.fullyQualifiedDomainName - } - { - name: 'SQLCMDPASSWORD' - secureValue: sqlAdminPassword - } - { - name: 'SQLADMIN' - value: sqlAdmin - } - ] - - scriptContent: ''' -wget https://github.com/microsoft/go-sqlcmd/releases/download/v0.8.1/sqlcmd-v0.8.1-linux-x64.tar.bz2 -tar x -f sqlcmd-v0.8.1-linux-x64.tar.bz2 -C . - -cat < ./initDb.sql -drop user if exists ${APPUSERNAME} -go -create user ${APPUSERNAME} with password = '${APPUSERPASSWORD}' -go -alter role db_owner add member ${APPUSERNAME} -go -SCRIPT_END - -./sqlcmd -S ${DBSERVER} -d ${DBNAME} -U ${SQLADMIN} -i ./initDb.sql - ''' - } -} - -resource sqlAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: 'sqlAdminPassword' - properties: { - value: sqlAdminPassword - } -} - -resource appUserPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: 'appUserPassword' - properties: { - value: appUserPassword - } -} - -resource sqlAzureConnectionStringSercret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { - parent: keyVault - name: connectionStringKey - properties: { - value: '${connectionString}; Password=${appUserPassword}' - } -} - -resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { - name: keyVaultName -} - -var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' -output connectionStringKey string = connectionStringKey -output databaseName string = sqlServer::database.name diff --git a/infra/core/gateway/apim.bicep b/infra/core/gateway/apim.bicep deleted file mode 100644 index be7464f06..000000000 --- a/infra/core/gateway/apim.bicep +++ /dev/null @@ -1,79 +0,0 @@ -metadata description = 'Creates an Azure API Management instance.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('The email address of the owner of the service') -@minLength(1) -param publisherEmail string = 'noreply@microsoft.com' - -@description('The name of the owner of the service') -@minLength(1) -param publisherName string = 'n/a' - -@description('The pricing tier of this API Management service') -@allowed([ - 'Consumption' - 'Developer' - 'Standard' - 'Premium' -]) -param sku string = 'Consumption' - -@description('The instance size of this API Management service.') -@allowed([ 0, 1, 2 ]) -param skuCount int = 0 - -@description('Azure Application Insights Name') -param applicationInsightsName string - -resource apimService 'Microsoft.ApiManagement/service@2021-08-01' = { - name: name - location: location - tags: union(tags, { 'azd-service-name': name }) - sku: { - name: sku - capacity: (sku == 'Consumption') ? 0 : ((sku == 'Developer') ? 1 : skuCount) - } - properties: { - publisherEmail: publisherEmail - publisherName: publisherName - // Custom properties are not supported for Consumption SKU - customProperties: sku == 'Consumption' ? {} : { - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_GCM_SHA256': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA256': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA256': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_256_CBC_SHA': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TLS_RSA_WITH_AES_128_CBC_SHA': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Ciphers.TripleDes168': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Ssl30': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11': 'false' - 'Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30': 'false' - } - } -} - -resource apimLogger 'Microsoft.ApiManagement/service/loggers@2021-12-01-preview' = if (!empty(applicationInsightsName)) { - name: 'app-insights-logger' - parent: apimService - properties: { - credentials: { - instrumentationKey: applicationInsights.properties.InstrumentationKey - } - description: 'Logger to Azure Application Insights' - isBuffered: false - loggerType: 'applicationInsights' - resourceId: applicationInsights.id - } -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (!empty(applicationInsightsName)) { - name: applicationInsightsName -} - -output apimServiceName string = apimService.name diff --git a/infra/core/host/aks-agent-pool.bicep b/infra/core/host/aks-agent-pool.bicep deleted file mode 100644 index ac406005b..000000000 --- a/infra/core/host/aks-agent-pool.bicep +++ /dev/null @@ -1,18 +0,0 @@ -metadata description = 'Adds an agent pool to an Azure Kubernetes Service (AKS) cluster.' -param clusterName string - -@description('The agent pool name') -param name string - -@description('The agent pool configuration') -param config object - -resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' existing = { - name: clusterName -} - -resource nodePool 'Microsoft.ContainerService/managedClusters/agentPools@2023-03-02-preview' = { - parent: aksCluster - name: name - properties: config -} diff --git a/infra/core/host/aks-managed-cluster.bicep b/infra/core/host/aks-managed-cluster.bicep deleted file mode 100644 index 0d4229eaf..000000000 --- a/infra/core/host/aks-managed-cluster.bicep +++ /dev/null @@ -1,140 +0,0 @@ -metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool.' -@description('The name for the AKS managed cluster') -param name string - -@description('The name of the resource group for the managed resources of the AKS cluster') -param nodeResourceGroupName string = '' - -@description('The Azure region/location for the AKS resources') -param location string = resourceGroup().location - -@description('Custom tags to apply to the AKS resources') -param tags object = {} - -@description('Kubernetes Version') -param kubernetesVersion string = '1.25.5' - -@description('Whether RBAC is enabled for local accounts') -param enableRbac bool = true - -// Add-ons -@description('Whether web app routing (preview) add-on is enabled') -param webAppRoutingAddon bool = true - -// AAD Integration -@description('Enable Azure Active Directory integration') -param enableAad bool = false - -@description('Enable RBAC using AAD') -param enableAzureRbac bool = false - -@description('The Tenant ID associated to the Azure Active Directory') -param aadTenantId string = '' - -@description('The load balancer SKU to use for ingress into the AKS cluster') -@allowed([ 'basic', 'standard' ]) -param loadBalancerSku string = 'standard' - -@description('Network plugin used for building the Kubernetes network.') -@allowed([ 'azure', 'kubenet', 'none' ]) -param networkPlugin string = 'azure' - -@description('Network policy used for building the Kubernetes network.') -@allowed([ 'azure', 'calico' ]) -param networkPolicy string = 'azure' - -@description('If set to true, getting static credentials will be disabled for this cluster.') -param disableLocalAccounts bool = false - -@description('The managed cluster SKU.') -@allowed([ 'Free', 'Paid', 'Standard' ]) -param sku string = 'Free' - -@description('Configuration of AKS add-ons') -param addOns object = {} - -@description('The log analytics workspace id used for logging & monitoring') -param workspaceId string = '' - -@description('The node pool configuration for the System agent pool') -param systemPoolConfig object - -@description('The DNS prefix to associate with the AKS cluster') -param dnsPrefix string = '' - -resource aks 'Microsoft.ContainerService/managedClusters@2023-03-02-preview' = { - name: name - location: location - tags: tags - identity: { - type: 'SystemAssigned' - } - sku: { - name: 'Base' - tier: sku - } - properties: { - nodeResourceGroup: !empty(nodeResourceGroupName) ? nodeResourceGroupName : 'rg-mc-${name}' - kubernetesVersion: kubernetesVersion - dnsPrefix: empty(dnsPrefix) ? '${name}-dns' : dnsPrefix - enableRBAC: enableRbac - aadProfile: enableAad ? { - managed: true - enableAzureRBAC: enableAzureRbac - tenantID: aadTenantId - } : null - agentPoolProfiles: [ - systemPoolConfig - ] - networkProfile: { - loadBalancerSku: loadBalancerSku - networkPlugin: networkPlugin - networkPolicy: networkPolicy - } - disableLocalAccounts: disableLocalAccounts && enableAad - addonProfiles: addOns - ingressProfile: { - webAppRouting: { - enabled: webAppRoutingAddon - } - } - } -} - -var aksDiagCategories = [ - 'cluster-autoscaler' - 'kube-controller-manager' - 'kube-audit-admin' - 'guard' -] - -// TODO: Update diagnostics to be its own module -// Blocking issue: https://github.com/Azure/bicep/issues/622 -// Unable to pass in a `resource` scope or unable to use string interpolation in resource types -resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { - name: 'aks-diagnostics' - scope: aks - properties: { - workspaceId: workspaceId - logs: [for category in aksDiagCategories: { - category: category - enabled: true - }] - metrics: [ - { - category: 'AllMetrics' - enabled: true - } - ] - } -} - -@description('The resource name of the AKS cluster') -output clusterName string = aks.name - -@description('The AKS cluster identity') -output clusterIdentity object = { - clientId: aks.properties.identityProfile.kubeletidentity.clientId - objectId: aks.properties.identityProfile.kubeletidentity.objectId - resourceId: aks.properties.identityProfile.kubeletidentity.resourceId -} diff --git a/infra/core/host/aks.bicep b/infra/core/host/aks.bicep deleted file mode 100644 index 452e059b2..000000000 --- a/infra/core/host/aks.bicep +++ /dev/null @@ -1,214 +0,0 @@ -metadata description = 'Creates an Azure Kubernetes Service (AKS) cluster with a system agent pool as well as an additional user agent pool.' -@description('The name for the AKS managed cluster') -param name string - -@description('The name for the Azure container registry (ACR)') -param containerRegistryName string - -@description('The name of the connected log analytics workspace') -param logAnalyticsName string = '' - -@description('The name of the keyvault to grant access') -param keyVaultName string - -@description('The Azure region/location for the AKS resources') -param location string = resourceGroup().location - -@description('Custom tags to apply to the AKS resources') -param tags object = {} - -@description('AKS add-ons configuration') -param addOns object = { - azurePolicy: { - enabled: true - config: { - version: 'v2' - } - } - keyVault: { - enabled: true - config: { - enableSecretRotation: 'true' - rotationPollInterval: '2m' - } - } - openServiceMesh: { - enabled: false - config: {} - } - omsAgent: { - enabled: true - config: {} - } - applicationGateway: { - enabled: false - config: {} - } -} - -@allowed([ - 'CostOptimised' - 'Standard' - 'HighSpec' - 'Custom' -]) -@description('The System Pool Preset sizing') -param systemPoolType string = 'CostOptimised' - -@allowed([ - '' - 'CostOptimised' - 'Standard' - 'HighSpec' - 'Custom' -]) -@description('The User Pool Preset sizing') -param agentPoolType string = '' - -// Configure system / user agent pools -@description('Custom configuration of system node pool') -param systemPoolConfig object = {} -@description('Custom configuration of user node pool') -param agentPoolConfig object = {} - -// Configure AKS add-ons -var omsAgentConfig = (!empty(logAnalyticsName) && !empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? union( - addOns.omsAgent, - { - config: { - logAnalyticsWorkspaceResourceID: logAnalytics.id - } - } -) : {} - -var addOnsConfig = union( - (!empty(addOns.azurePolicy) && addOns.azurePolicy.enabled) ? { azurepolicy: addOns.azurePolicy } : {}, - (!empty(addOns.keyVault) && addOns.keyVault.enabled) ? { azureKeyvaultSecretsProvider: addOns.keyVault } : {}, - (!empty(addOns.openServiceMesh) && addOns.openServiceMesh.enabled) ? { openServiceMesh: addOns.openServiceMesh } : {}, - (!empty(addOns.omsAgent) && addOns.omsAgent.enabled) ? { omsagent: omsAgentConfig } : {}, - (!empty(addOns.applicationGateway) && addOns.applicationGateway.enabled) ? { ingressApplicationGateway: addOns.applicationGateway } : {} -) - -// Link to existing log analytics workspace when available -resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' existing = if (!empty(logAnalyticsName)) { - name: logAnalyticsName -} - -var systemPoolSpec = !empty(systemPoolConfig) ? systemPoolConfig : nodePoolPresets[systemPoolType] - -// Create the primary AKS cluster resources and system node pool -module managedCluster 'aks-managed-cluster.bicep' = { - name: 'managed-cluster' - params: { - name: name - location: location - tags: tags - systemPoolConfig: union( - { name: 'npsystem', mode: 'System' }, - nodePoolBase, - systemPoolSpec - ) - addOns: addOnsConfig - workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' - } -} - -var hasAgentPool = !empty(agentPoolConfig) || !empty(agentPoolType) -var agentPoolSpec = hasAgentPool && !empty(agentPoolConfig) ? agentPoolConfig : empty(agentPoolType) ? {} : nodePoolPresets[agentPoolType] - -// Create additional user agent pool when specified -module agentPool 'aks-agent-pool.bicep' = if (hasAgentPool) { - name: 'aks-node-pool' - params: { - clusterName: managedCluster.outputs.clusterName - name: 'npuserpool' - config: union({ name: 'npuser', mode: 'User' }, nodePoolBase, agentPoolSpec) - } -} - -// Creates container registry (ACR) -module containerRegistry 'container-registry.bicep' = { - name: 'container-registry' - params: { - name: containerRegistryName - location: location - tags: tags - workspaceId: !empty(logAnalyticsName) ? logAnalytics.id : '' - } -} - -// Grant ACR Pull access from cluster managed identity to container registry -module containerRegistryAccess '../security/registry-access.bicep' = { - name: 'cluster-container-registry-access' - params: { - containerRegistryName: containerRegistry.outputs.name - principalId: managedCluster.outputs.clusterIdentity.objectId - } -} - -// Give the AKS Cluster access to KeyVault -module clusterKeyVaultAccess '../security/keyvault-access.bicep' = { - name: 'cluster-keyvault-access' - params: { - keyVaultName: keyVaultName - principalId: managedCluster.outputs.clusterIdentity.objectId - } -} - -// Helpers for node pool configuration -var nodePoolBase = { - osType: 'Linux' - maxPods: 30 - type: 'VirtualMachineScaleSets' - upgradeSettings: { - maxSurge: '33%' - } -} - -var nodePoolPresets = { - CostOptimised: { - vmSize: 'Standard_B4ms' - count: 1 - minCount: 1 - maxCount: 3 - enableAutoScaling: true - availabilityZones: [] - } - Standard: { - vmSize: 'Standard_DS2_v2' - count: 3 - minCount: 3 - maxCount: 5 - enableAutoScaling: true - availabilityZones: [ - '1' - '2' - '3' - ] - } - HighSpec: { - vmSize: 'Standard_D4s_v3' - count: 3 - minCount: 3 - maxCount: 5 - enableAutoScaling: true - availabilityZones: [ - '1' - '2' - '3' - ] - } -} - -// Module outputs -@description('The resource name of the AKS cluster') -output clusterName string = managedCluster.outputs.clusterName - -@description('The AKS cluster identity') -output clusterIdentity object = managedCluster.outputs.clusterIdentity - -@description('The resource name of the ACR') -output containerRegistryName string = containerRegistry.outputs.name - -@description('The login server for the container registry') -output containerRegistryLoginServer string = containerRegistry.outputs.loginServer diff --git a/infra/core/host/container-app-upsert.bicep b/infra/core/host/container-app-upsert.bicep deleted file mode 100644 index 3eec62f29..000000000 --- a/infra/core/host/container-app-upsert.bicep +++ /dev/null @@ -1,105 +0,0 @@ -metadata description = 'Creates or updates an existing Azure Container App.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('The environment name for the container apps') -param containerAppsEnvironmentName string - -@description('The number of CPU cores allocated to a single container instance, e.g., 0.5') -param containerCpuCoreCount string = '0.5' - -@description('The maximum number of replicas to run. Must be at least 1.') -@minValue(1) -param containerMaxReplicas int = 10 - -@description('The amount of memory allocated to a single container instance, e.g., 1Gi') -param containerMemory string = '1.0Gi' - -@description('The minimum number of replicas to run. Must be at least 1.') -@minValue(1) -param containerMinReplicas int = 1 - -@description('The name of the container') -param containerName string = 'main' - -@description('The name of the container registry') -param containerRegistryName string = '' - -@allowed([ 'http', 'grpc' ]) -@description('The protocol used by Dapr to connect to the app, e.g., HTTP or gRPC') -param daprAppProtocol string = 'http' - -@description('Enable or disable Dapr for the container app') -param daprEnabled bool = false - -@description('The Dapr app ID') -param daprAppId string = containerName - -@description('Specifies if the resource already exists') -param exists bool = false - -@description('Specifies if Ingress is enabled for the container app') -param ingressEnabled bool = true - -@description('The type of identity for the resource') -@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) -param identityType string = 'None' - -@description('The name of the user-assigned identity') -param identityName string = '' - -@description('The name of the container image') -param imageName string = '' - -@description('The secrets required for the container') -param secrets array = [] - -@description('The environment variables for the container') -param env array = [] - -@description('Specifies if the resource ingress is exposed externally') -param external bool = true - -@description('The service binds associated with the container') -param serviceBinds array = [] - -@description('The target port for the container') -param targetPort int = 80 - -resource existingApp 'Microsoft.App/containerApps@2023-04-01-preview' existing = if (exists) { - name: name -} - -module app 'container-app.bicep' = { - name: '${deployment().name}-update' - params: { - name: name - location: location - tags: tags - identityType: identityType - identityName: identityName - ingressEnabled: ingressEnabled - containerName: containerName - containerAppsEnvironmentName: containerAppsEnvironmentName - containerRegistryName: containerRegistryName - containerCpuCoreCount: containerCpuCoreCount - containerMemory: containerMemory - containerMinReplicas: containerMinReplicas - containerMaxReplicas: containerMaxReplicas - daprEnabled: daprEnabled - daprAppId: daprAppId - daprAppProtocol: daprAppProtocol - secrets: secrets - external: external - env: env - imageName: !empty(imageName) ? imageName : exists ? existingApp.properties.template.containers[0].image : '' - targetPort: targetPort - serviceBinds: serviceBinds - } -} - -output defaultDomain string = app.outputs.defaultDomain -output imageName string = app.outputs.imageName -output name string = app.outputs.name -output uri string = app.outputs.uri diff --git a/infra/core/host/container-app.bicep b/infra/core/host/container-app.bicep deleted file mode 100644 index 3724086d2..000000000 --- a/infra/core/host/container-app.bicep +++ /dev/null @@ -1,162 +0,0 @@ -metadata description = 'Creates a container app in an Azure Container App environment.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('Allowed origins') -param allowedOrigins array = [] - -@description('Name of the environment for container apps') -param containerAppsEnvironmentName string - -@description('CPU cores allocated to a single container instance, e.g., 0.5') -param containerCpuCoreCount string = '0.5' - -@description('The maximum number of replicas to run. Must be at least 1.') -@minValue(1) -param containerMaxReplicas int = 10 - -@description('Memory allocated to a single container instance, e.g., 1Gi') -param containerMemory string = '1.0Gi' - -@description('The minimum number of replicas to run. Must be at least 1.') -param containerMinReplicas int = 1 - -@description('The name of the container') -param containerName string = 'main' - -@description('The name of the container registry') -param containerRegistryName string = '' - -@description('The protocol used by Dapr to connect to the app, e.g., http or grpc') -@allowed([ 'http', 'grpc' ]) -param daprAppProtocol string = 'http' - -@description('The Dapr app ID') -param daprAppId string = containerName - -@description('Enable Dapr') -param daprEnabled bool = false - -@description('The environment variables for the container') -param env array = [] - -@description('Specifies if the resource ingress is exposed externally') -param external bool = true - -@description('The name of the user-assigned identity') -param identityName string = '' - -@description('The type of identity for the resource') -@allowed([ 'None', 'SystemAssigned', 'UserAssigned' ]) -param identityType string = 'None' - -@description('The name of the container image') -param imageName string = '' - -@description('Specifies if Ingress is enabled for the container app') -param ingressEnabled bool = true - -param revisionMode string = 'Single' - -@description('The secrets required for the container') -param secrets array = [] - -@description('The service binds associated with the container') -param serviceBinds array = [] - -@description('The name of the container apps add-on to use. e.g. redis') -param serviceType string = '' - -@description('The target port for the container') -param targetPort int = 80 - -resource userIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' existing = if (!empty(identityName)) { - name: identityName -} - -// Private registry support requires both an ACR name and a User Assigned managed identity -var usePrivateRegistry = !empty(identityName) && !empty(containerRegistryName) - -// Automatically set to `UserAssigned` when an `identityName` has been set -var normalizedIdentityType = !empty(identityName) ? 'UserAssigned' : identityType - -module containerRegistryAccess '../security/registry-access.bicep' = if (usePrivateRegistry) { - name: '${deployment().name}-registry-access' - params: { - containerRegistryName: containerRegistryName - principalId: usePrivateRegistry ? userIdentity.properties.principalId : '' - } -} - -resource app 'Microsoft.App/containerApps@2023-04-01-preview' = { - name: name - location: location - tags: tags - // It is critical that the identity is granted ACR pull access before the app is created - // otherwise the container app will throw a provision error - // This also forces us to use an user assigned managed identity since there would no way to - // provide the system assigned identity with the ACR pull access before the app is created - dependsOn: usePrivateRegistry ? [ containerRegistryAccess ] : [] - identity: { - type: normalizedIdentityType - userAssignedIdentities: !empty(identityName) && normalizedIdentityType == 'UserAssigned' ? { '${userIdentity.id}': {} } : null - } - properties: { - managedEnvironmentId: containerAppsEnvironment.id - configuration: { - activeRevisionsMode: revisionMode - ingress: ingressEnabled ? { - external: external - targetPort: targetPort - transport: 'auto' - corsPolicy: { - allowedOrigins: union([ 'https://portal.azure.com', 'https://ms.portal.azure.com' ], allowedOrigins) - } - } : null - dapr: daprEnabled ? { - enabled: true - appId: daprAppId - appProtocol: daprAppProtocol - appPort: ingressEnabled ? targetPort : 0 - } : { enabled: false } - secrets: secrets - service: !empty(serviceType) ? { type: serviceType } : null - registries: usePrivateRegistry ? [ - { - server: '${containerRegistryName}.azurecr.io' - identity: userIdentity.id - } - ] : [] - } - template: { - serviceBinds: !empty(serviceBinds) ? serviceBinds : null - containers: [ - { - image: !empty(imageName) ? imageName : 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' - name: containerName - env: env - resources: { - cpu: json(containerCpuCoreCount) - memory: containerMemory - } - } - ] - scale: { - minReplicas: containerMinReplicas - maxReplicas: containerMaxReplicas - } - } - } -} - -resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' existing = { - name: containerAppsEnvironmentName -} - -output defaultDomain string = containerAppsEnvironment.properties.defaultDomain -output identityPrincipalId string = normalizedIdentityType == 'None' ? '' : (empty(identityName) ? app.identity.principalId : userIdentity.properties.principalId) -output imageName string = imageName -output name string = app.name -output serviceBind object = !empty(serviceType) ? { serviceId: app.id, name: name } : {} -output uri string = ingressEnabled ? 'https://${app.properties.configuration.ingress.fqdn}' : '' diff --git a/infra/core/host/container-apps-environment.bicep b/infra/core/host/container-apps-environment.bicep deleted file mode 100644 index 8633ba487..000000000 --- a/infra/core/host/container-apps-environment.bicep +++ /dev/null @@ -1,41 +0,0 @@ -metadata description = 'Creates an Azure Container Apps environment.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('Name of the Application Insights resource') -param applicationInsightsName string = '' - -@description('Specifies if Dapr is enabled') -param daprEnabled bool = false - -@description('Name of the Log Analytics workspace') -param logAnalyticsWorkspaceName string - -resource containerAppsEnvironment 'Microsoft.App/managedEnvironments@2023-04-01-preview' = { - name: name - location: location - tags: tags - properties: { - appLogsConfiguration: { - destination: 'log-analytics' - logAnalyticsConfiguration: { - customerId: logAnalyticsWorkspace.properties.customerId - sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey - } - } - daprAIInstrumentationKey: daprEnabled && !empty(applicationInsightsName) ? applicationInsights.properties.InstrumentationKey : '' - } -} - -resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = { - name: logAnalyticsWorkspaceName -} - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' existing = if (daprEnabled && !empty(applicationInsightsName)) { - name: applicationInsightsName -} - -output defaultDomain string = containerAppsEnvironment.properties.defaultDomain -output id string = containerAppsEnvironment.id -output name string = containerAppsEnvironment.name diff --git a/infra/core/host/container-apps.bicep b/infra/core/host/container-apps.bicep deleted file mode 100644 index f3f7dadd1..000000000 --- a/infra/core/host/container-apps.bicep +++ /dev/null @@ -1,38 +0,0 @@ -metadata description = 'Creates an Azure Container Registry and an Azure Container Apps environment.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param containerAppsEnvironmentName string -param containerRegistryName string -param containerRegistryResourceGroupName string = '' -param logAnalyticsWorkspaceName string -param applicationInsightsName string = '' - -module containerAppsEnvironment 'container-apps-environment.bicep' = { - name: '${name}-container-apps-environment' - params: { - name: containerAppsEnvironmentName - location: location - tags: tags - logAnalyticsWorkspaceName: logAnalyticsWorkspaceName - applicationInsightsName: applicationInsightsName - } -} - -module containerRegistry 'container-registry.bicep' = { - name: '${name}-container-registry' - scope: !empty(containerRegistryResourceGroupName) ? resourceGroup(containerRegistryResourceGroupName) : resourceGroup() - params: { - name: containerRegistryName - location: location - tags: tags - } -} - -output defaultDomain string = containerAppsEnvironment.outputs.defaultDomain -output environmentName string = containerAppsEnvironment.outputs.name -output environmentId string = containerAppsEnvironment.outputs.id - -output registryLoginServer string = containerRegistry.outputs.loginServer -output registryName string = containerRegistry.outputs.name diff --git a/infra/core/host/container-registry.bicep b/infra/core/host/container-registry.bicep deleted file mode 100644 index 9c64531b9..000000000 --- a/infra/core/host/container-registry.bicep +++ /dev/null @@ -1,83 +0,0 @@ -metadata description = 'Creates an Azure Container Registry.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('Indicates whether admin user is enabled') -param adminUserEnabled bool = false - -@description('Indicates whether anonymous pull is enabled') -param anonymousPullEnabled bool = false - -@description('Indicates whether data endpoint is enabled') -param dataEndpointEnabled bool = false - -@description('Encryption settings') -param encryption object = { - status: 'disabled' -} - -@description('Options for bypassing network rules') -param networkRuleBypassOptions string = 'AzureServices' - -@description('Public network access setting') -param publicNetworkAccess string = 'Enabled' - -@description('SKU settings') -param sku object = { - name: 'Basic' -} - -@description('Zone redundancy setting') -param zoneRedundancy string = 'Disabled' - -@description('The log analytics workspace ID used for logging and monitoring') -param workspaceId string = '' - -// 2022-02-01-preview needed for anonymousPullEnabled -resource containerRegistry 'Microsoft.ContainerRegistry/registries@2022-02-01-preview' = { - name: name - location: location - tags: tags - sku: sku - properties: { - adminUserEnabled: adminUserEnabled - anonymousPullEnabled: anonymousPullEnabled - dataEndpointEnabled: dataEndpointEnabled - encryption: encryption - networkRuleBypassOptions: networkRuleBypassOptions - publicNetworkAccess: publicNetworkAccess - zoneRedundancy: zoneRedundancy - } -} - -// TODO: Update diagnostics to be its own module -// Blocking issue: https://github.com/Azure/bicep/issues/622 -// Unable to pass in a `resource` scope or unable to use string interpolation in resource types -resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (!empty(workspaceId)) { - name: 'registry-diagnostics' - scope: containerRegistry - properties: { - workspaceId: workspaceId - logs: [ - { - category: 'ContainerRegistryRepositoryEvents' - enabled: true - } - { - category: 'ContainerRegistryLoginEvents' - enabled: true - } - ] - metrics: [ - { - category: 'AllMetrics' - enabled: true - timeGrain: 'PT1M' - } - ] - } -} - -output loginServer string = containerRegistry.properties.loginServer -output name string = containerRegistry.name diff --git a/infra/core/host/staticwebapp.bicep b/infra/core/host/staticwebapp.bicep deleted file mode 100644 index cedaf906e..000000000 --- a/infra/core/host/staticwebapp.bicep +++ /dev/null @@ -1,22 +0,0 @@ -metadata description = 'Creates an Azure Static Web Apps instance.' -param name string -param location string = resourceGroup().location -param tags object = {} - -param sku object = { - name: 'Free' - tier: 'Free' -} - -resource web 'Microsoft.Web/staticSites@2022-03-01' = { - name: name - location: location - tags: tags - sku: sku - properties: { - provider: 'Custom' - } -} - -output name string = web.name -output uri string = 'https://${web.properties.defaultHostname}' diff --git a/infra/core/networking/cdn-endpoint.bicep b/infra/core/networking/cdn-endpoint.bicep deleted file mode 100644 index 5e8ab695d..000000000 --- a/infra/core/networking/cdn-endpoint.bicep +++ /dev/null @@ -1,52 +0,0 @@ -metadata description = 'Adds an endpoint to an Azure CDN profile.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('The name of the CDN profile resource') -@minLength(1) -param cdnProfileName string - -@description('Delivery policy rules') -param deliveryPolicyRules array = [] - -@description('The origin URL for the endpoint') -@minLength(1) -param originUrl string - -resource endpoint 'Microsoft.Cdn/profiles/endpoints@2022-05-01-preview' = { - parent: cdnProfile - name: name - location: location - tags: tags - properties: { - originHostHeader: originUrl - isHttpAllowed: false - isHttpsAllowed: true - queryStringCachingBehavior: 'UseQueryString' - optimizationType: 'GeneralWebDelivery' - origins: [ - { - name: replace(originUrl, '.', '-') - properties: { - hostName: originUrl - originHostHeader: originUrl - priority: 1 - weight: 1000 - enabled: true - } - } - ] - deliveryPolicy: { - rules: deliveryPolicyRules - } - } -} - -resource cdnProfile 'Microsoft.Cdn/profiles@2022-05-01-preview' existing = { - name: cdnProfileName -} - -output id string = endpoint.id -output name string = endpoint.name -output uri string = 'https://${endpoint.properties.hostName}' diff --git a/infra/core/networking/cdn-profile.bicep b/infra/core/networking/cdn-profile.bicep deleted file mode 100644 index 27669ee2e..000000000 --- a/infra/core/networking/cdn-profile.bicep +++ /dev/null @@ -1,34 +0,0 @@ -metadata description = 'Creates an Azure CDN profile.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@description('The pricing tier of this CDN profile') -@allowed([ - 'Custom_Verizon' - 'Premium_AzureFrontDoor' - 'Premium_Verizon' - 'StandardPlus_955BandWidth_ChinaCdn' - 'StandardPlus_AvgBandWidth_ChinaCdn' - 'StandardPlus_ChinaCdn' - 'Standard_955BandWidth_ChinaCdn' - 'Standard_Akamai' - 'Standard_AvgBandWidth_ChinaCdn' - 'Standard_AzureFrontDoor' - 'Standard_ChinaCdn' - 'Standard_Microsoft' - 'Standard_Verizon' -]) -param sku string = 'Standard_Microsoft' - -resource profile 'Microsoft.Cdn/profiles@2022-05-01-preview' = { - name: name - location: location - tags: tags - sku: { - name: sku - } -} - -output id string = profile.id -output name string = profile.name diff --git a/infra/core/networking/cdn.bicep b/infra/core/networking/cdn.bicep deleted file mode 100644 index de98a1f99..000000000 --- a/infra/core/networking/cdn.bicep +++ /dev/null @@ -1,42 +0,0 @@ -metadata description = 'Creates an Azure CDN profile with a single endpoint.' -param location string = resourceGroup().location -param tags object = {} - -@description('Name of the CDN endpoint resource') -param cdnEndpointName string - -@description('Name of the CDN profile resource') -param cdnProfileName string - -@description('Delivery policy rules') -param deliveryPolicyRules array = [] - -@description('Origin URL for the CDN endpoint') -param originUrl string - -module cdnProfile 'cdn-profile.bicep' = { - name: 'cdn-profile' - params: { - name: cdnProfileName - location: location - tags: tags - } -} - -module cdnEndpoint 'cdn-endpoint.bicep' = { - name: 'cdn-endpoint' - params: { - name: cdnEndpointName - location: location - tags: tags - cdnProfileName: cdnProfile.outputs.name - originUrl: originUrl - deliveryPolicyRules: deliveryPolicyRules - } -} - -output endpointName string = cdnEndpoint.outputs.name -output endpointId string = cdnEndpoint.outputs.id -output profileName string = cdnProfile.outputs.name -output profileId string = cdnProfile.outputs.id -output uri string = cdnEndpoint.outputs.uri diff --git a/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep deleted file mode 100644 index 4b6febbea..000000000 --- a/infra/core/storage/storage-account.bicep +++ /dev/null @@ -1,64 +0,0 @@ -metadata description = 'Creates an Azure storage account.' -param name string -param location string = resourceGroup().location -param tags object = {} - -@allowed([ - 'Cool' - 'Hot' - 'Premium' ]) -param accessTier string = 'Hot' -param allowBlobPublicAccess bool = true -param allowCrossTenantReplication bool = true -param allowSharedKeyAccess bool = true -param containers array = [] -param defaultToOAuthAuthentication bool = false -param deleteRetentionPolicy object = {} -@allowed([ 'AzureDnsZone', 'Standard' ]) -param dnsEndpointType string = 'Standard' -param kind string = 'StorageV2' -param minimumTlsVersion string = 'TLS1_2' -param supportsHttpsTrafficOnly bool = true -param networkAcls object = { - bypass: 'AzureServices' - defaultAction: 'Allow' -} -@allowed([ 'Enabled', 'Disabled' ]) -param publicNetworkAccess string = 'Enabled' -param sku object = { name: 'Standard_LRS' } - -resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { - name: name - location: location - tags: tags - kind: kind - sku: sku - properties: { - accessTier: accessTier - allowBlobPublicAccess: allowBlobPublicAccess - allowCrossTenantReplication: allowCrossTenantReplication - allowSharedKeyAccess: allowSharedKeyAccess - defaultToOAuthAuthentication: defaultToOAuthAuthentication - dnsEndpointType: dnsEndpointType - minimumTlsVersion: minimumTlsVersion - networkAcls: networkAcls - publicNetworkAccess: publicNetworkAccess - supportsHttpsTrafficOnly: supportsHttpsTrafficOnly - } - - resource blobServices 'blobServices' = if (!empty(containers)) { - name: 'default' - properties: { - deleteRetentionPolicy: deleteRetentionPolicy - } - resource container 'containers' = [for container in containers: { - name: container.name - properties: { - publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' - } - }] - } -} - -output name string = storage.name -output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/infra/core/testing/loadtesting.bicep b/infra/core/testing/loadtesting.bicep deleted file mode 100644 index 467810867..000000000 --- a/infra/core/testing/loadtesting.bicep +++ /dev/null @@ -1,15 +0,0 @@ -param name string -param location string = resourceGroup().location -param managedIdentity bool = false -param tags object = {} - -resource loadTest 'Microsoft.LoadTestService/loadTests@2022-12-01' = { - name: name - location: location - tags: tags - identity: { type: managedIdentity ? 'SystemAssigned' : 'None' } - properties: { - } -} - -output loadTestingName string = loadTest.name diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 340c69b11..77ade37da 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -7,6 +7,6 @@ param location = readEnvironmentVariable('AZURE_LOCATION', 'location') param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id') // Please make sure to set this value to false when using rbac with AUTH_TYPE -param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'false')) +param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) -param authType = readEnvironmentVariable('AUTH_TYPE', 'rbac') +param authType = readEnvironmentVariable('AUTH_TYPE', 'keys') From 5406c5a88f83429d2ee6d64efd4d78433672d5dd Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 12 Jan 2024 14:26:11 +0800 Subject: [PATCH 31/37] update readme and storage.bicep in infra/core --- README.md | 6 +- infra/app/storage.bicep | 56 ---------------- infra/core/storage/storage-account.bicep | 81 ++++++++++++++++++++++++ infra/main.bicep | 34 ++++++++-- 4 files changed, 111 insertions(+), 66 deletions(-) delete mode 100644 infra/app/storage.bicep create mode 100644 infra/core/storage/storage-account.bicep diff --git a/README.md b/README.md index b81bb3442..980ae493b 100644 --- a/README.md +++ b/README.md @@ -128,18 +128,18 @@ After logging in with the following command, you will be able to use azd cli to azd auth login ``` -Then, execute the `azd init` command to initialize the environment. +Then, execute the `azd init` command to initialize the environment (You do not need to run this command if you already have the code or have opened this in a Codespace or DevContainer). ``` azd init -t chat-with-your-data-solution-accelerator ``` -According to the prompt, enter an environment name. +Enter an environment name. Run `azd up` to provision all the resources to Azure and deploy the code to those resources. ``` azd up ``` -According to the prompt, select `subscription` and `location`, these are the necessary parameters when you create resources. After that, choose a resource group or create a new resource group. Wait a moment for the resource deployment to complete, click the Website endpoint and you will see the web app page. +Select your desired `subscription` and `location`. Then choose a resource group or create a new resource group. Wait a moment for the resource deployment to complete, click the Website endpoint and you will see the web app page. You can also run the sample directly locally (See below). diff --git a/infra/app/storage.bicep b/infra/app/storage.bicep deleted file mode 100644 index f0221ed4c..000000000 --- a/infra/app/storage.bicep +++ /dev/null @@ -1,56 +0,0 @@ -param name string -param location string -param blobContainerName string - -resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = { - name: name - location: location - kind: 'StorageV2' - sku: { - name: 'Standard_GRS' - } - resource storageAccountNameDefaultBlob 'blobServices' = { - name: 'default' - resource storageAccountNameDefaultBlobContainer 'containers' = { - name: blobContainerName - properties: { - publicAccess: 'None' - } - } - resource storageAccountNameDefaultConfig 'containers' = { - name: 'config' - properties: { - publicAccess: 'None' - } - } - } -} - -resource storageAccountNameDefault 'Microsoft.Storage/storageAccounts/queueServices@2022-09-01' = { - parent: storageAccount - name: 'default' - properties: { - cors: { - corsRules: [] - } - } -} - -resource storageAccountNameDefaultDocProcessing 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { - parent: storageAccountNameDefault - name: 'doc-processing' - properties: { - metadata: {} - } -} - -resource storageAccountNameDefaultDocProcessingPoison 'Microsoft.Storage/storageAccounts/queueServices/queues@2022-09-01' = { - parent: storageAccountNameDefault - name: 'doc-processing-poison' - properties: { - metadata: {} - } -} - -output STORAGE_ACCOUNT_ID string = storageAccount.id -output STORAGE_ACCOUNT_NAME string = storageAccount.name diff --git a/infra/core/storage/storage-account.bicep b/infra/core/storage/storage-account.bicep new file mode 100644 index 000000000..dd398144a --- /dev/null +++ b/infra/core/storage/storage-account.bicep @@ -0,0 +1,81 @@ +metadata description = 'Creates an Azure storage account.' +param name string +param location string = resourceGroup().location +param tags object = {} + +@allowed([ + 'Cool' + 'Hot' + 'Premium' ]) +param accessTier string = 'Hot' +param allowBlobPublicAccess bool = true +param allowCrossTenantReplication bool = true +param allowSharedKeyAccess bool = true +param containers array = [] +param defaultToOAuthAuthentication bool = false +param deleteRetentionPolicy object = {} +@allowed([ 'AzureDnsZone', 'Standard' ]) +param dnsEndpointType string = 'Standard' +param kind string = 'StorageV2' +param minimumTlsVersion string = 'TLS1_2' +param queues array = [] +param supportsHttpsTrafficOnly bool = true +param networkAcls object = { + bypass: 'AzureServices' + defaultAction: 'Allow' +} +@allowed([ 'Enabled', 'Disabled' ]) +param publicNetworkAccess string = 'Enabled' +param sku object = { name: 'Standard_LRS' } + +resource storage 'Microsoft.Storage/storageAccounts@2022-05-01' = { + name: name + location: location + tags: tags + kind: kind + sku: sku + properties: { + accessTier: accessTier + allowBlobPublicAccess: allowBlobPublicAccess + allowCrossTenantReplication: allowCrossTenantReplication + allowSharedKeyAccess: allowSharedKeyAccess + defaultToOAuthAuthentication: defaultToOAuthAuthentication + dnsEndpointType: dnsEndpointType + minimumTlsVersion: minimumTlsVersion + networkAcls: networkAcls + publicNetworkAccess: publicNetworkAccess + supportsHttpsTrafficOnly: supportsHttpsTrafficOnly + } + + resource blobServices 'blobServices' = if (!empty(containers)) { + name: 'default' + properties: { + deleteRetentionPolicy: deleteRetentionPolicy + } + resource container 'containers' = [for container in containers: { + name: container.name + properties: { + publicAccess: contains(container, 'publicAccess') ? container.publicAccess : 'None' + } + }] + } + + resource queueServices 'queueServices' = if (!empty(queues)) { + name: 'default' + properties: { + cors: { + corsRules: [] + } + } + resource queue 'queues' = [for queue in queues: { + name: queue.name + properties: { + metadata: {} + } + }] + } +} + +output name string = storage.name +output id string = storage.id +output primaryEndpoints object = storage.properties.primaryEndpoints diff --git a/infra/main.bicep b/infra/main.bicep index e9e31b000..48cfb540e 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -252,7 +252,7 @@ module storekeys './app/storekeys.bicep' = if (useKeyVault) { keyVaultName: keyVaultName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name - storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + storageAccountName: storage.outputs.name formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name rgName: rgName @@ -303,7 +303,7 @@ module web './app/web.bicep' = { applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name - storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + storageAccountName: storage.outputs.name formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' @@ -359,7 +359,7 @@ module adminweb './app/adminweb.bicep' = { applicationInsightsName: monitoring.outputs.applicationInsightsName azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name - storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + storageAccountName: storage.outputs.name formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' @@ -431,7 +431,7 @@ module function './app/function.bicep' = { appServicePlanId: hostingplan.outputs.name azureOpenAIName: openai.outputs.name azureCognitiveSearchName: search.outputs.name - storageAccountName: storage.outputs.STORAGE_ACCOUNT_NAME + storageAccountName: storage.outputs.name formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name clientKey: clientKey @@ -492,19 +492,39 @@ module eventgrid 'app/eventgrid.bicep' = { params: { name: eventGridSystemTopicName location: location - storageAccountId: storage.outputs.STORAGE_ACCOUNT_ID + storageAccountId: storage.outputs.id queueName: queueName blobContainerName: blobContainerName } } -module storage 'app/storage.bicep' = { +module storage 'core/storage/storage-account.bicep' = { name: storageAccountName scope: rg params: { name: storageAccountName location: location - blobContainerName: blobContainerName + sku:{ + name: 'Standard_GRS' + } + containers:[ + { + name: blobContainerName + publicAccess: 'None' + } + { + name: 'config' + publicAccess: 'None' + } + ] + queues: [ + { + name: 'doc-processing' + } + { + name: 'doc-processing-poison' + } + ] } } From d2145ec39d60ba022fefbcbc455c0a47ac33913e Mon Sep 17 00:00:00 2001 From: zedy Date: Wed, 17 Jan 2024 10:29:31 +0800 Subject: [PATCH 32/37] Update output format --- infra/app/adminweb.bicep | 20 ++++++++++---------- infra/app/function.bicep | 20 ++++++++++---------- infra/app/storekeys.bicep | 10 +++++----- infra/app/web.bicep | 20 ++++++++++---------- infra/main.bicep | 40 +++++++++++++++++++-------------------- 5 files changed, 55 insertions(+), 55 deletions(-) diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index 35df87d10..febb72129 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -15,11 +15,11 @@ param azureCognitiveSearchName string = '' param appSettings object = {} param serviceName string = 'adminweb' param useKeyVault bool -param openAIKey string = '' -param storageAccountKey string = '' -param formRecognizerKey string = '' -param searchKey string = '' -param contentSafetyKey string = '' +param openAIKeyName string = '' +param storageAccountKeyName string = '' +param formRecognizerKeyName string = '' +param searchKeyName string = '' +param contentSafetyKeyName string = '' param keyVaultEndpoint string = '' param authType string @@ -37,11 +37,11 @@ module adminweb '../core/host/appservice.bicep' = { AUTH_TYPE: authType USE_KEY_VAULT: useKeyVault ? useKeyVault : '' AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' - AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AZURE_OPENAI_KEY: useKeyVault ? openAIKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKeyName : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/app/function.bicep b/infra/app/function.bicep index d813ee8e0..1fae3cfd8 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -16,11 +16,11 @@ param azureCognitiveSearchName string = '' param formRecognizerName string = '' param contentSafetyName string = '' param useKeyVault bool -param openAIKey string = '' -param storageAccountKey string = '' -param formRecognizerKey string = '' -param searchKey string = '' -param contentSafetyKey string = '' +param openAIKeyName string = '' +param storageAccountKeyName string = '' +param formRecognizerKeyName string = '' +param searchKeyName string = '' +param contentSafetyKeyName string = '' param keyVaultEndpoint string = '' param authType string @@ -39,11 +39,11 @@ module function '../core/host/functions.bicep' = { AUTH_TYPE: authType USE_KEY_VAULT: useKeyVault ? useKeyVault : '' AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' - AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AZURE_OPENAI_KEY: useKeyVault ? openAIKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKeyName : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) } } diff --git a/infra/app/storekeys.bicep b/infra/app/storekeys.bicep index d4001d9fc..88310a1a2 100644 --- a/infra/app/storekeys.bicep +++ b/infra/app/storekeys.bicep @@ -56,8 +56,8 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { name: keyVaultName } -output CONTENT_SAFETY_KEY string = contentSafetyKeySecret.name -output FORM_RECOGNIZER_KEY string = formRecognizerKeySecret.name -output SEARCH_KEY string = searchKeySecret.name -output OPENAI_KEY string = openAIKeySecret.name -output STORAGE_ACCOUNT_KEY string = storageAccountKeySecret.name +output CONTENT_SAFETY_KEY_NAME string = contentSafetyKeySecret.name +output FORM_RECOGNIZER_KEY_NAME string = formRecognizerKeySecret.name +output SEARCH_KEY_NAME string = searchKeySecret.name +output OPENAI_KEY_NAME string = openAIKeySecret.name +output STORAGE_ACCOUNT_KEY_NAME string = storageAccountKeySecret.name diff --git a/infra/app/web.bicep b/infra/app/web.bicep index 3e3c33bd4..a580b3cb7 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -15,11 +15,11 @@ param contentSafetyName string = '' param appSettings object = {} param serviceName string = 'web' param useKeyVault bool -param openAIKey string = '' -param storageAccountKey string = '' -param formRecognizerKey string = '' -param searchKey string = '' -param contentSafetyKey string = '' +param openAIKeyName string = '' +param storageAccountKeyName string = '' +param formRecognizerKeyName string = '' +param searchKeyName string = '' +param contentSafetyKeyName string = '' param keyVaultEndpoint string = '' param authType string @@ -37,11 +37,11 @@ module web '../core/host/appservice.bicep' = { AUTH_TYPE: authType USE_KEY_VAULT: useKeyVault ? useKeyVault : '' AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' - AZURE_OPENAI_KEY: useKeyVault ? openAIKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 - AZURE_SEARCH_KEY: useKeyVault ? searchKey : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey - AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value - AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 - AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKey : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AZURE_OPENAI_KEY: useKeyVault ? openAIKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 + AZURE_SEARCH_KEY: useKeyVault ? searchKeyName : listAdminKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Search/searchServices', azureCognitiveSearchName), '2021-04-01-preview').primaryKey + AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value + AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 + AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/main.bicep b/infra/main.bicep index 48cfb540e..5abd83d3c 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -306,11 +306,11 @@ module web './app/web.bicep' = { storageAccountName: storage.outputs.name formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name - openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' - storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' - formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' - searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' - contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + openAIKeyName: useKeyVault ? storekeys.outputs.OPENAI_KEY_NAME : '' + storageAccountKeyName: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY_NAME : '' + formRecognizerKeyName: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY_NAME : '' + searchKeyName: useKeyVault ? storekeys.outputs.SEARCH_KEY_NAME : '' + contentSafetyKeyName: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY_NAME : '' useKeyVault: useKeyVault keyVaultName: useKeyVault || authType == 'rbac' ? keyvault.outputs.name : '' keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' @@ -362,11 +362,11 @@ module adminweb './app/adminweb.bicep' = { storageAccountName: storage.outputs.name formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name - openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' - storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' - formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' - searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' - contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + openAIKeyName: useKeyVault ? storekeys.outputs.OPENAI_KEY_NAME : '' + storageAccountKeyName: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY_NAME : '' + formRecognizerKeyName: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY_NAME : '' + searchKeyName: useKeyVault ? storekeys.outputs.SEARCH_KEY_NAME : '' + contentSafetyKeyName: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY_NAME : '' useKeyVault: useKeyVault keyVaultName: useKeyVault || authType == 'rbac' ? keyvault.outputs.name : '' keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' @@ -435,11 +435,11 @@ module function './app/function.bicep' = { formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name clientKey: clientKey - openAIKey: useKeyVault ? storekeys.outputs.OPENAI_KEY : '' - storageAccountKey: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' - formRecognizerKey: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' - searchKey: useKeyVault ? storekeys.outputs.SEARCH_KEY: '' - contentSafetyKey: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' + openAIKeyName: useKeyVault ? storekeys.outputs.OPENAI_KEY_NAME : '' + storageAccountKeyName: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY_NAME : '' + formRecognizerKeyName: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY_NAME : '' + searchKeyName: useKeyVault ? storekeys.outputs.SEARCH_KEY_NAME : '' + contentSafetyKeyName: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY_NAME : '' useKeyVault: useKeyVault keyVaultName: useKeyVault || authType == 'rbac' ? keyvault.outputs.name : '' keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' @@ -716,8 +716,8 @@ output AZURE_OPENAI_RESOURCE string = azureOpenAIResourceName output AZURE_OPENAI_EMBEDDING_MODEL string = azureOpenAIEmbeddingModel output AZURE_OPENAI_MODEL string = azureOpenAIModel output USE_KEY_VAULT bool = useKeyVault -output AZURE_OPENAI_KEY string = useKeyVault ? storekeys.outputs.OPENAI_KEY : '' -output AZURE_BLOB_ACCOUNT_KEY string = useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY: '' -output AZURE_FORM_RECOGNIZER_KEY string = useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY: '' -output AZURE_SEARCH_KEY string = useKeyVault ? storekeys.outputs.SEARCH_KEY: '' -output AZURE_CONTENT_SAFETY_KEY string = useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY: '' +output AZURE_OPENAI_KEY_NAME string = useKeyVault ? storekeys.outputs.OPENAI_KEY_NAME : '' +output AZURE_BLOB_ACCOUNT_KEY_NAME string = useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY_NAME : '' +output AZURE_FORM_RECOGNIZER_KEY_NAME string = useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY_NAME : '' +output AZURE_SEARCH_KEY_NAME string = useKeyVault ? storekeys.outputs.SEARCH_KEY_NAME : '' +output AZURE_CONTENT_SAFETY_KEY_NAME string = useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY_NAME : '' From c83a409c1731105e91909a9f36a8a660d1a68ea0 Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 22 Jan 2024 16:43:46 +0800 Subject: [PATCH 33/37] default to use rbac --- infra/main.bicepparam | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 77ade37da..340c69b11 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -7,6 +7,6 @@ param location = readEnvironmentVariable('AZURE_LOCATION', 'location') param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id') // Please make sure to set this value to false when using rbac with AUTH_TYPE -param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) +param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'false')) -param authType = readEnvironmentVariable('AUTH_TYPE', 'keys') +param authType = readEnvironmentVariable('AUTH_TYPE', 'rbac') From aa86081dc922957cef29b6bf141e22fbda924c67 Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 26 Jan 2024 16:13:27 +0800 Subject: [PATCH 34/37] add some comments --- README.md | 2 +- app.py | 11 ++++++++--- backend/utilities/helpers/EnvHelper.py | 8 ++++---- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 980ae493b..2c57b63d5 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' curl -fsSL https://aka.ms/install-azd.sh | bash ``` -After logging in with the following command, you will be able to use azd cli to quickly provision and deploy the application. +After logging in with the following command, you will be able to use the `azd` cli to quickly provision and deploy the application. ``` azd auth login diff --git a/app.py b/app.py index f9dbd1eca..1b6b76e2a 100644 --- a/app.py +++ b/app.py @@ -26,7 +26,11 @@ def static_file(path): return app.send_static_file(path) AUTH_TYPE = os.environ.get("AUTH_TYPE") - +# Initialize Azure keys based on authentication type and environment settings. +# When AUTH_TYPE is not 'rbac' and USE_KEY_VAULT environment variable is set, +# Azure keys are securely fetched from Azure Key Vault using DefaultAzureCredential. +# Otherwise, keys are obtained from environment variables, with fallbacks to None or +# an empty string depending on the AUTH_TYPE. if not AUTH_TYPE == 'rbac' and os.environ.get("USE_KEY_VAULT"): credential = DefaultAzureCredential() secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) @@ -203,10 +207,11 @@ def stream_without_data(response): def conversation_without_data(request): + azure_endpoint = f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/" if AUTH_TYPE == 'rbac': - openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, azure_ad_token_provider=AZURE_TOKEN_PROVIDER) + openai_client = AzureOpenAI(azure_endpoint=azure_endpoint, api_version=AZURE_OPENAI_API_VERSION, azure_ad_token_provider=AZURE_TOKEN_PROVIDER) else: - openai_client = AzureOpenAI(azure_endpoint=f"https://{AZURE_OPENAI_RESOURCE}.openai.azure.com/", api_version=AZURE_OPENAI_API_VERSION, api_key=AZURE_OPENAI_KEY) + openai_client = AzureOpenAI(azure_endpoint=azure_endpoint, api_version=AZURE_OPENAI_API_VERSION, api_key=AZURE_OPENAI_KEY) request_messages = request.json["messages"] messages = [ diff --git a/backend/utilities/helpers/EnvHelper.py b/backend/utilities/helpers/EnvHelper.py index 20f74d3e4..bf655cca8 100644 --- a/backend/utilities/helpers/EnvHelper.py +++ b/backend/utilities/helpers/EnvHelper.py @@ -56,10 +56,10 @@ def __init__(self, **kwargs) -> None: self.OPENAI_API_BASE = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" self.OPENAI_API_KEY = self.AZURE_OPENAI_KEY self.OPENAI_API_VERSION = self.AZURE_OPENAI_API_VERSION - os.environ["OPENAI_API_TYPE"] = "azuread" if self.AUTH_TYPE == 'rbac' else "azure" - os.environ["OPENAI_API_BASE"] = f"https://{os.getenv('AZURE_OPENAI_RESOURCE')}.openai.azure.com/" - os.environ["OPENAI_API_KEY"] = self.AZURE_OPENAI_KEY - os.environ["OPENAI_API_VERSION"] = self.AZURE_OPENAI_API_VERSION + os.environ["OPENAI_API_TYPE"] = self.OPENAI_API_TYPE + os.environ["OPENAI_API_BASE"] = self.OPENAI_API_BASE + os.environ["OPENAI_API_KEY"] = self.OPENAI_API_KEY + os.environ["OPENAI_API_VERSION"] = self.OPENAI_API_VERSION # Azure Functions - Batch processing self.BACKEND_URL = os.getenv('BACKEND_URL', '') self.AzureWebJobsStorage = os.getenv('AzureWebJobsStorage', '') From 1a7303ad52409411f96004fd35fc30212d0e930d Mon Sep 17 00:00:00 2001 From: zedy Date: Thu, 1 Feb 2024 15:08:33 +0800 Subject: [PATCH 35/37] add readme and speech service related code in main.bicep --- README.md | 43 +++++++++++++++++++++++++++++++++++++++ code/app/app.py | 25 ++++++++++++----------- infra/app/adminweb.bicep | 2 +- infra/app/function.bicep | 2 +- infra/app/storekeys.bicep | 15 ++++++++++++-- infra/app/web.bicep | 5 ++++- infra/main.bicep | 23 +++++++++++++++++++++ infra/main.bicepparam | 6 +++--- 8 files changed, 101 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 165a121e3..544cf21e8 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,49 @@ az deployment group create --resource-group $RESOURCE_GROUP_NAME --template-file ![A screenshot of the chat app.](./media/web-unstructureddata.png) +### Running the sample using the Azure Developer CLI (azd) + +The Azure Developer CLI (`azd`) is a developer-centric command-line interface (CLI) tool for creating Azure applications. + +You need to install it before running and deploying with the Azure Developer CLI. + +### Windows + +```powershell +powershell -ex AllSigned -c "Invoke-RestMethod 'https://aka.ms/install-azd.ps1' | Invoke-Expression" +``` + +### Linux/MacOS + +``` +curl -fsSL https://aka.ms/install-azd.sh | bash +``` + +After logging in with the following command, you will be able to use the `azd` cli to quickly provision and deploy the application. + +``` +azd auth login +``` + +Then, execute the `azd init` command to initialize the environment (You do not need to run this command if you already have the code or have opened this in a Codespace or DevContainer). +``` +azd init -t chat-with-your-data-solution-accelerator +``` +Enter an environment name. + +**Notes:** the default auth type uses keys, if you want to switch to rbac, please run `azd env set AUTH_TYPE rbac`. +``` +azd env set AUTH_TYPE rbac +``` + +Then, run `azd up` to provision all the resources to Azure and deploy the code to those resources. +``` +azd up +``` + +Select your desired `subscription` and `location`. Then choose a resource group or create a new resource group. Wait a moment for the resource deployment to complete, click the Website endpoint and you will see the web app page. + +You can also run the sample directly locally (See below). ### [Local deployment instructions](./docs/LOCAL_DEPLOYMENT.md) To customize the accelerator or run it locally, first, copy the .env.sample file to your development environment's .env file, and edit it according to environment variable values table. Learn more about deploying locally [here](./docs/LOCAL_DEPLOYMENT.md). diff --git a/code/app/app.py b/code/app/app.py index 9d4a56a80..341c5b24a 100644 --- a/code/app/app.py +++ b/code/app/app.py @@ -26,18 +26,6 @@ def static_file(path): return app.send_static_file(path) -@app.route('/api/config', methods=['GET']) -def get_config(): - # Retrieve the environment variables or other configuration data - azure_speech_key = os.getenv('AZURE_SPEECH_SERVICE_KEY') - azure_speech_region = os.getenv('AZURE_SPEECH_SERVICE_REGION') - - # Return the configuration data as JSON - return jsonify({ - 'azureSpeechKey': azure_speech_key, - 'azureSpeechRegion': azure_speech_region - }) - # ACS Integration Settings AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") @@ -76,9 +64,22 @@ def get_config(): secret_client = SecretClient(os.environ.get("AZURE_KEY_VAULT_ENDPOINT"), credential) AZURE_SEARCH_KEY = secret_client.get_secret(os.environ.get("AZURE_SEARCH_KEY")).value AZURE_OPENAI_KEY = secret_client.get_secret(os.environ.get("AZURE_OPENAI_KEY")).value + AZURE_SPEECH_KEY = secret_client.get_secret(os.environ.get("AZURE_SPEECH_SERVICE_KEY")).value else: AZURE_SEARCH_KEY = None if AZURE_AUTH_TYPE == 'rbac' else os.environ.get("AZURE_SEARCH_KEY") AZURE_OPENAI_KEY = "" if AZURE_AUTH_TYPE == 'rbac' else os.environ.get("AZURE_OPENAI_KEY") + AZURE_SPEECH_KEY = None if AZURE_AUTH_TYPE == 'rbac' else os.environ.get("AZURE_SPEECH_SERVICE_KEY") + +@app.route('/api/config', methods=['GET']) +def get_config(): + # Retrieve the environment variables or other configuration data + azure_speech_region = os.getenv('AZURE_SPEECH_SERVICE_REGION') + + # Return the configuration data as JSON + return jsonify({ + 'azureSpeechKey': AZURE_SPEECH_KEY, + 'azureSpeechRegion': azure_speech_region + }) def is_chat_model(): if 'gpt-4' in AZURE_OPENAI_MODEL_NAME.lower(): diff --git a/infra/app/adminweb.bicep b/infra/app/adminweb.bicep index febb72129..b4e533ff3 100644 --- a/infra/app/adminweb.bicep +++ b/infra/app/adminweb.bicep @@ -34,7 +34,7 @@ module adminweb '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AUTH_TYPE: authType + AZURE_AUTH_TYPE: authType USE_KEY_VAULT: useKeyVault ? useKeyVault : '' AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' AZURE_OPENAI_KEY: useKeyVault ? openAIKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 diff --git a/infra/app/function.bicep b/infra/app/function.bicep index 1fae3cfd8..b54a4c057 100644 --- a/infra/app/function.bicep +++ b/infra/app/function.bicep @@ -36,7 +36,7 @@ module function '../core/host/functions.bicep' = { runtimeName: runtimeName runtimeVersion: runtimeVersion appSettings: union(appSettings, { - AUTH_TYPE: authType + AZURE_AUTH_TYPE: authType USE_KEY_VAULT: useKeyVault ? useKeyVault : '' AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' AZURE_OPENAI_KEY: useKeyVault ? openAIKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 diff --git a/infra/app/storekeys.bicep b/infra/app/storekeys.bicep index 88310a1a2..115680c9f 100644 --- a/infra/app/storekeys.bicep +++ b/infra/app/storekeys.bicep @@ -5,11 +5,13 @@ param azureCognitiveSearchName string = '' param rgName string = '' param formRecognizerName string = '' param contentSafetyName string = '' +param speechServiceName string = '' param storageAccountKeyName string = 'AZURE-STORAGE-ACCOUNT-KEY' param openAIKeyName string = 'AZURE-OPEN-AI-KEY' param searchKeyName string = 'AZURE-SEARCH-KEY' param formRecognizerKeyName string = 'AZURE-FORM-RECOGNIZER-KEY' -param contentSafety string = 'AZURE-CONTENT-SAFETY-KEY' +param contentSafetyKeyName string = 'AZURE-CONTENT-SAFETY-KEY' +param speechKeyName string = 'AZURE-SPEECH-KEY' resource storageAccountKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { @@ -46,12 +48,20 @@ resource formRecognizerKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' resource contentSafetyKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { parent: keyVault - name: contentSafety + name: contentSafetyKeyName properties: { value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 } } +resource speechKeySecret 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = { + parent: keyVault + name: speechKeyName + properties: { + value: listKeys(resourceId(subscription().subscriptionId, rgName, 'Microsoft.CognitiveServices/accounts', speechServiceName), '2023-05-01').key1 + } +} + resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = { name: keyVaultName } @@ -61,3 +71,4 @@ output FORM_RECOGNIZER_KEY_NAME string = formRecognizerKeySecret.name output SEARCH_KEY_NAME string = searchKeySecret.name output OPENAI_KEY_NAME string = openAIKeySecret.name output STORAGE_ACCOUNT_KEY_NAME string = storageAccountKeySecret.name +output SPEECH_KEY_NAME string = speechKeySecret.name diff --git a/infra/app/web.bicep b/infra/app/web.bicep index a580b3cb7..a2115005e 100644 --- a/infra/app/web.bicep +++ b/infra/app/web.bicep @@ -11,6 +11,7 @@ param azureCognitiveSearchName string = '' param storageAccountName string = '' param formRecognizerName string = '' param contentSafetyName string = '' +param speechServiceName string = '' @secure() param appSettings object = {} param serviceName string = 'web' @@ -20,6 +21,7 @@ param storageAccountKeyName string = '' param formRecognizerKeyName string = '' param searchKeyName string = '' param contentSafetyKeyName string = '' +param speechKeyName string = '' param keyVaultEndpoint string = '' param authType string @@ -34,7 +36,7 @@ module web '../core/host/appservice.bicep' = { applicationInsightsName: applicationInsightsName appServicePlanId: appServicePlanId appSettings: union(appSettings, { - AUTH_TYPE: authType + AZURE_AUTH_TYPE: authType USE_KEY_VAULT: useKeyVault ? useKeyVault : '' AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' AZURE_OPENAI_KEY: useKeyVault ? openAIKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', azureOpenAIName), '2023-05-01').key1 @@ -42,6 +44,7 @@ module web '../core/host/appservice.bicep' = { AZURE_BLOB_ACCOUNT_KEY: useKeyVault ? storageAccountKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2021-09-01').keys[0].value AZURE_FORM_RECOGNIZER_KEY: useKeyVault ? formRecognizerKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', formRecognizerName), '2023-05-01').key1 AZURE_CONTENT_SAFETY_KEY: useKeyVault ? contentSafetyKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', contentSafetyName), '2023-05-01').key1 + AZURE_SPEECH_SERVICE_KEY: useKeyVault ? speechKeyName: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', speechServiceName), '2023-05-01').key1 }) keyVaultName: keyVaultName runtimeName: 'python' diff --git a/infra/main.bicep b/infra/main.bicep index 5abd83d3c..ffedfc82a 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -142,6 +142,9 @@ param formRecognizerName string = '${environmentName}-formrecog-${resourceToken} @description('Azure Content Safety Name') param contentSafetyName string = '${environmentName}-contentsafety-${resourceToken}' +@description('Azure Speech Service Name') +param speechServiceName string = '${environmentName}-speechservice' + param newGuidString string = newGuid() param searchTag string = 'chatwithyourdata-sa' param useKeyVault bool @@ -245,6 +248,19 @@ module openai 'core/ai/cognitiveservices.bicep' = { } } +module speechService 'core/ai/cognitiveservices.bicep' = { + scope: rg + name: speechServiceName + params:{ + name: speechServiceName + location: location + sku: { + name: 'S0' + } + kind: 'SpeechServices' + } +} + module storekeys './app/storekeys.bicep' = if (useKeyVault) { name: 'storekeys' scope: rg @@ -255,6 +271,7 @@ module storekeys './app/storekeys.bicep' = if (useKeyVault) { storageAccountName: storage.outputs.name formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name + speechServiceName: speechServiceName rgName: rgName } } @@ -306,11 +323,13 @@ module web './app/web.bicep' = { storageAccountName: storage.outputs.name formRecognizerName: formrecognizer.outputs.name contentSafetyName: contentsafety.outputs.name + speechServiceName: speechService.outputs.name openAIKeyName: useKeyVault ? storekeys.outputs.OPENAI_KEY_NAME : '' storageAccountKeyName: useKeyVault ? storekeys.outputs.STORAGE_ACCOUNT_KEY_NAME : '' formRecognizerKeyName: useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY_NAME : '' searchKeyName: useKeyVault ? storekeys.outputs.SEARCH_KEY_NAME : '' contentSafetyKeyName: useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY_NAME : '' + speechKeyName: useKeyVault ? storekeys.outputs.SPEECH_KEY_NAME: '' useKeyVault: useKeyVault keyVaultName: useKeyVault || authType == 'rbac' ? keyvault.outputs.name : '' keyVaultEndpoint: useKeyVault ? keyvault.outputs.endpoint : '' @@ -344,6 +363,8 @@ module web './app/web.bicep' = { ORCHESTRATION_STRATEGY: orchestrationStrategy AZURE_CONTENT_SAFETY_ENDPOINT: 'https://${location}.api.cognitive.microsoft.com/' APPINSIGHTS_CONNECTION_STRING: monitoring.outputs.applicationInsightsConnectionString + AZURE_SPEECH_SERVICE_NAME: speechServiceName + AZURE_SPEECH_SERVICE_REGION: location } } } @@ -721,3 +742,5 @@ output AZURE_BLOB_ACCOUNT_KEY_NAME string = useKeyVault ? storekeys.outputs.STOR output AZURE_FORM_RECOGNIZER_KEY_NAME string = useKeyVault ? storekeys.outputs.FORM_RECOGNIZER_KEY_NAME : '' output AZURE_SEARCH_KEY_NAME string = useKeyVault ? storekeys.outputs.SEARCH_KEY_NAME : '' output AZURE_CONTENT_SAFETY_KEY_NAME string = useKeyVault ? storekeys.outputs.CONTENT_SAFETY_KEY_NAME : '' +output AZURE_SPEECH_SERVICE_REGION string = location +output AZURE_SPEECH_SERVICE_KEY_NAME string = useKeyVault ? storekeys.outputs.SPEECH_KEY_NAME : '' diff --git a/infra/main.bicepparam b/infra/main.bicepparam index 340c69b11..ffade4e20 100644 --- a/infra/main.bicepparam +++ b/infra/main.bicepparam @@ -6,7 +6,7 @@ param location = readEnvironmentVariable('AZURE_LOCATION', 'location') param principalId = readEnvironmentVariable('AZURE_PRINCIPAL_ID', 'principal_id') -// Please make sure to set this value to false when using rbac with AUTH_TYPE -param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'false')) +// Please make sure to set this value to false when using rbac with AZURE_AUTH_TYPE +param useKeyVault = bool(readEnvironmentVariable('USE_KEY_VAULT', 'true')) -param authType = readEnvironmentVariable('AUTH_TYPE', 'rbac') +param authType = readEnvironmentVariable('AZURE_AUTH_TYPE', 'keys') From 2c8abfbfa3ba4dfddbf0a466a436426b82259c8d Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 2 Feb 2024 16:23:25 +0800 Subject: [PATCH 36/37] fix azd deploy problem --- azure.yaml | 18 +++++++++++++----- code/app/requirements.txt | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/azure.yaml b/azure.yaml index 65c81a5ce..88cfeb0ee 100644 --- a/azure.yaml +++ b/azure.yaml @@ -3,25 +3,33 @@ name: chat-with-your-data-solution-accelerator metadata: template: chat-with-your-data-solution-accelerator@0.0.1-beta + +hooks: + prepackage: + windows: + shell: pwsh + run: robocopy -e ./code/utilities ./code/app/utilities; robocopy -e ./code/utilities ./code/admin/utilities; robocopy -e ./code/ ./code/admin/ requirements.txt /lev:1; robocopy -e ./code/utilities ./code/batch/utilities; robocopy -e ./code/ ./code/batch/ requirements.txt /lev:1; + continueOnError: true + services: web: - project: . + project: ./code/app language: py host: appservice hooks: prepackage: windows: shell: pwsh - run: cd ./frontend;npm install;npm run build + run: cd ./frontend;npm install;npm run build; interactive: true continueOnError: false - + adminweb: - project: ./backend + project: ./code/admin language: py host: appservice function: - project: ./backend + project: ./code/batch language: py host: function diff --git a/code/app/requirements.txt b/code/app/requirements.txt index 4e9c7ba79..f5a459a1d 100644 --- a/code/app/requirements.txt +++ b/code/app/requirements.txt @@ -14,6 +14,6 @@ chardet==5.2.0 --extra-index-url https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/ azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.13 -azure-ai-contentsafety==1.0.0b1 +azure-ai-contentsafety==1.0.0 python-docx==1.1.0 azure-keyvault-secrets==4.4.* \ No newline at end of file From 5dcf10ceabc0f27a304f6c1eed1b0cb796348d25 Mon Sep 17 00:00:00 2001 From: zedy Date: Mon, 5 Feb 2024 16:39:10 +0800 Subject: [PATCH 37/37] move code folder structure --- .vscode/launch.json | 6 +- .vscode/tasks.json | 6 +- azure.yaml | 13 +- code/{app => }/app.py | 58 +- code/{admin => backend}/Admin.py | 0 code/{ => backend}/batch/.funcignore | 0 code/{ => backend}/batch/.gitignore | 0 .../batch/.vscode/extensions.json | 0 code/{ => backend}/batch/AddURLEmbeddings.py | 0 code/{ => backend}/batch/BatchPushResults.py | 0 .../batch/BatchStartProcessing.py | 0 code/{ => backend}/batch/function_app.py | 0 code/{ => backend}/batch/host.json | 0 .../batch/local.settings.json.sample | 0 .../{ => backend/batch}/utilities/__init__.py | 0 .../batch}/utilities/common/Answer.py | 0 .../batch}/utilities/common/SourceDocument.py | 0 .../batch}/utilities/common/__init__.py | 0 .../document_chunking/DocumentChunkingBase.py | 0 .../document_chunking/FixedSizeOverlap.py | 0 .../utilities/document_chunking/Layout.py | 0 .../utilities/document_chunking/Page.py | 0 .../utilities/document_chunking/Paragraph.py | 0 .../utilities/document_chunking/Strategies.py | 0 .../utilities/document_chunking/__init__.py | 0 .../document_loading/DocumentLoadingBase.py | 0 .../utilities/document_loading/Layout.py | 0 .../batch}/utilities/document_loading/Read.py | 0 .../utilities/document_loading/Strategies.py | 0 .../batch}/utilities/document_loading/Web.py | 0 .../document_loading/WordDocument.py | 0 .../utilities/document_loading/__init__.py | 0 .../helpers/AzureBlobStorageHelper.py | 0 .../helpers/AzureFormRecognizerHelper.py | 0 .../utilities/helpers/AzureSearchHelper.py | 0 .../batch}/utilities/helpers/ConfigHelper.py | 0 .../helpers/DocumentChunkingHelper.py | 0 .../helpers/DocumentLoadingHelper.py | 0 .../helpers/DocumentProcessorHelper.py | 0 .../batch}/utilities/helpers/EnvHelper.py | 0 .../batch}/utilities/helpers/LLMHelper.py | 0 .../utilities/helpers/OrchestratorHelper.py | 0 .../batch}/utilities/helpers/__init__.py | 0 .../utilities/loggers/ConversationLogger.py | 0 .../batch}/utilities/loggers/TokenLogger.py | 0 .../utilities/orchestrator/LangChainAgent.py | 0 .../utilities/orchestrator/OpenAIFunctions.py | 0 .../orchestrator/OrchestratorBase.py | 0 .../utilities/orchestrator/Strategies.py | 0 .../batch}/utilities/orchestrator/__init__.py | 0 .../utilities/parser/OutputParserTool.py | 0 .../batch}/utilities/parser/ParserBase.py | 0 .../batch}/utilities/parser/__init__.py | 0 .../batch}/utilities/tests/__init__.py | 0 .../utilities/tests/test_AzureBlobStorage.py | 0 .../tests/test_ContentSafetyChecker.py | 0 .../utilities/tests/test_DocumentChunking.py | 0 .../utilities/tests/test_DocumentLoading.py | 0 .../utilities/tests/test_DocumentProcessor.py | 0 .../utilities/tests/test_Orchestrator.py | 0 .../utilities/tests/test_OutputParserTool.py | 0 .../utilities/tools/AnswerProcessingBase.py | 0 .../utilities/tools/AnsweringToolBase.py | 0 .../utilities/tools/ContentSafetyChecker.py | 0 .../batch}/utilities/tools/PostPromptTool.py | 0 .../utilities/tools/QuestionAnswerTool.py | 0 .../utilities/tools/TextProcessingTool.py | 0 .../batch}/utilities/tools/__init__.py | 0 code/{admin => backend}/images/favicon.ico | Bin code/{admin => backend}/images/logo.png | Bin .../pages/01_Ingest_Data.py | 2 +- .../pages/02_Explore_Data.py | 2 +- .../pages/03_Delete_Data.py | 2 +- .../pages/04_Configuration.py | 2 +- code/{app => backend}/requirements.txt | 21 +- code/{app => }/frontend/index.html | 0 code/{app => }/frontend/package-lock.json | 27 +- code/{app => }/frontend/package.json | 1 + code/{app => }/frontend/public/favicon.ico | Bin code/{app => }/frontend/src/api/api.ts | 0 code/{app => }/frontend/src/api/index.ts | 0 code/{app => }/frontend/src/api/models.ts | 0 code/{app => }/frontend/src/assets/Azure.svg | 0 code/{app => }/frontend/src/assets/Send.svg | 0 .../frontend/src/assets/mic-outline.svg | 0 .../src/components/Answer/Answer.module.css | 0 .../frontend/src/components/Answer/Answer.tsx | 0 .../src/components/Answer/AnswerParser.tsx | 0 .../frontend/src/components/Answer/index.ts | 0 .../QuestionInput/QuestionInput.module.css | 0 .../QuestionInput/QuestionInput.tsx | 0 .../src/components/QuestionInput/index.ts | 0 code/{app => }/frontend/src/index.css | 0 code/{app => }/frontend/src/index.tsx | 0 code/{app => }/frontend/src/pages/NoPage.tsx | 0 .../frontend/src/pages/chat/Chat.module.css | 0 .../frontend/src/pages/chat/Chat.tsx | 0 .../src/pages/layout/Layout.module.css | 0 .../frontend/src/pages/layout/Layout.tsx | 0 code/{app => }/frontend/src/vite-env.d.ts | 0 code/{app => }/frontend/tsconfig.json | 0 code/{app => }/frontend/tsconfig.node.json | 0 code/{app => }/frontend/vite.config.ts | 0 code/requirements.txt | 21 +- .../static/assets/Azure-d0a62656.svg | 0 .../{app => }/static/assets/Send-a9ab2367.svg | 0 code/static/assets/index-5e1f6cf7.js | 890 ++++++++++++++++++ code/static/assets/index-5e1f6cf7.js.map | 1 + code/static/assets/index-8b288b52.css | 1 + code/static/assets/mic-outline-75e9b1db.svg | 3 + code/{app => }/static/favicon.ico | Bin code/static/index.html | 15 + docs/LOCAL_DEPLOYMENT.md | 5 +- 113 files changed, 996 insertions(+), 80 deletions(-) rename code/{app => }/app.py (98%) rename code/{admin => backend}/Admin.py (100%) rename code/{ => backend}/batch/.funcignore (100%) rename code/{ => backend}/batch/.gitignore (100%) rename code/{ => backend}/batch/.vscode/extensions.json (100%) rename code/{ => backend}/batch/AddURLEmbeddings.py (100%) rename code/{ => backend}/batch/BatchPushResults.py (100%) rename code/{ => backend}/batch/BatchStartProcessing.py (100%) rename code/{ => backend}/batch/function_app.py (100%) rename code/{ => backend}/batch/host.json (100%) rename code/{ => backend}/batch/local.settings.json.sample (100%) rename code/{ => backend/batch}/utilities/__init__.py (100%) rename code/{ => backend/batch}/utilities/common/Answer.py (100%) rename code/{ => backend/batch}/utilities/common/SourceDocument.py (100%) rename code/{ => backend/batch}/utilities/common/__init__.py (100%) rename code/{ => backend/batch}/utilities/document_chunking/DocumentChunkingBase.py (100%) rename code/{ => backend/batch}/utilities/document_chunking/FixedSizeOverlap.py (100%) rename code/{ => backend/batch}/utilities/document_chunking/Layout.py (100%) rename code/{ => backend/batch}/utilities/document_chunking/Page.py (100%) rename code/{ => backend/batch}/utilities/document_chunking/Paragraph.py (100%) rename code/{ => backend/batch}/utilities/document_chunking/Strategies.py (100%) rename code/{ => backend/batch}/utilities/document_chunking/__init__.py (100%) rename code/{ => backend/batch}/utilities/document_loading/DocumentLoadingBase.py (100%) rename code/{ => backend/batch}/utilities/document_loading/Layout.py (100%) rename code/{ => backend/batch}/utilities/document_loading/Read.py (100%) rename code/{ => backend/batch}/utilities/document_loading/Strategies.py (100%) rename code/{ => backend/batch}/utilities/document_loading/Web.py (100%) rename code/{ => backend/batch}/utilities/document_loading/WordDocument.py (100%) rename code/{ => backend/batch}/utilities/document_loading/__init__.py (100%) rename code/{ => backend/batch}/utilities/helpers/AzureBlobStorageHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/AzureFormRecognizerHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/AzureSearchHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/ConfigHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/DocumentChunkingHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/DocumentLoadingHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/DocumentProcessorHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/EnvHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/LLMHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/OrchestratorHelper.py (100%) rename code/{ => backend/batch}/utilities/helpers/__init__.py (100%) rename code/{ => backend/batch}/utilities/loggers/ConversationLogger.py (100%) rename code/{ => backend/batch}/utilities/loggers/TokenLogger.py (100%) rename code/{ => backend/batch}/utilities/orchestrator/LangChainAgent.py (100%) rename code/{ => backend/batch}/utilities/orchestrator/OpenAIFunctions.py (100%) rename code/{ => backend/batch}/utilities/orchestrator/OrchestratorBase.py (100%) rename code/{ => backend/batch}/utilities/orchestrator/Strategies.py (100%) rename code/{ => backend/batch}/utilities/orchestrator/__init__.py (100%) rename code/{ => backend/batch}/utilities/parser/OutputParserTool.py (100%) rename code/{ => backend/batch}/utilities/parser/ParserBase.py (100%) rename code/{ => backend/batch}/utilities/parser/__init__.py (100%) rename code/{ => backend/batch}/utilities/tests/__init__.py (100%) rename code/{ => backend/batch}/utilities/tests/test_AzureBlobStorage.py (100%) rename code/{ => backend/batch}/utilities/tests/test_ContentSafetyChecker.py (100%) rename code/{ => backend/batch}/utilities/tests/test_DocumentChunking.py (100%) rename code/{ => backend/batch}/utilities/tests/test_DocumentLoading.py (100%) rename code/{ => backend/batch}/utilities/tests/test_DocumentProcessor.py (100%) rename code/{ => backend/batch}/utilities/tests/test_Orchestrator.py (100%) rename code/{ => backend/batch}/utilities/tests/test_OutputParserTool.py (100%) rename code/{ => backend/batch}/utilities/tools/AnswerProcessingBase.py (100%) rename code/{ => backend/batch}/utilities/tools/AnsweringToolBase.py (100%) rename code/{ => backend/batch}/utilities/tools/ContentSafetyChecker.py (100%) rename code/{ => backend/batch}/utilities/tools/PostPromptTool.py (100%) rename code/{ => backend/batch}/utilities/tools/QuestionAnswerTool.py (100%) rename code/{ => backend/batch}/utilities/tools/TextProcessingTool.py (100%) rename code/{ => backend/batch}/utilities/tools/__init__.py (100%) rename code/{admin => backend}/images/favicon.ico (100%) rename code/{admin => backend}/images/logo.png (100%) rename code/{admin => backend}/pages/01_Ingest_Data.py (99%) rename code/{admin => backend}/pages/02_Explore_Data.py (96%) rename code/{admin => backend}/pages/03_Delete_Data.py (97%) rename code/{admin => backend}/pages/04_Configuration.py (99%) rename code/{app => backend}/requirements.txt (64%) rename code/{app => }/frontend/index.html (100%) rename code/{app => }/frontend/package-lock.json (99%) rename code/{app => }/frontend/package.json (95%) rename code/{app => }/frontend/public/favicon.ico (100%) rename code/{app => }/frontend/src/api/api.ts (100%) rename code/{app => }/frontend/src/api/index.ts (100%) rename code/{app => }/frontend/src/api/models.ts (100%) rename code/{app => }/frontend/src/assets/Azure.svg (100%) rename code/{app => }/frontend/src/assets/Send.svg (100%) rename code/{app => }/frontend/src/assets/mic-outline.svg (100%) rename code/{app => }/frontend/src/components/Answer/Answer.module.css (100%) rename code/{app => }/frontend/src/components/Answer/Answer.tsx (100%) rename code/{app => }/frontend/src/components/Answer/AnswerParser.tsx (100%) rename code/{app => }/frontend/src/components/Answer/index.ts (100%) rename code/{app => }/frontend/src/components/QuestionInput/QuestionInput.module.css (100%) rename code/{app => }/frontend/src/components/QuestionInput/QuestionInput.tsx (100%) rename code/{app => }/frontend/src/components/QuestionInput/index.ts (100%) rename code/{app => }/frontend/src/index.css (100%) rename code/{app => }/frontend/src/index.tsx (100%) rename code/{app => }/frontend/src/pages/NoPage.tsx (100%) rename code/{app => }/frontend/src/pages/chat/Chat.module.css (100%) rename code/{app => }/frontend/src/pages/chat/Chat.tsx (100%) rename code/{app => }/frontend/src/pages/layout/Layout.module.css (100%) rename code/{app => }/frontend/src/pages/layout/Layout.tsx (100%) rename code/{app => }/frontend/src/vite-env.d.ts (100%) rename code/{app => }/frontend/tsconfig.json (100%) rename code/{app => }/frontend/tsconfig.node.json (100%) rename code/{app => }/frontend/vite.config.ts (100%) rename code/{app => }/static/assets/Azure-d0a62656.svg (100%) rename code/{app => }/static/assets/Send-a9ab2367.svg (100%) create mode 100644 code/static/assets/index-5e1f6cf7.js create mode 100644 code/static/assets/index-5e1f6cf7.js.map create mode 100644 code/static/assets/index-8b288b52.css create mode 100644 code/static/assets/mic-outline-75e9b1db.svg rename code/{app => }/static/favicon.ico (100%) create mode 100644 code/static/index.html diff --git a/.vscode/launch.json b/.vscode/launch.json index f6b73fe0b..d2c7e358c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,7 +17,7 @@ "run", "Admin.py" ], - "cwd": "${workspaceFolder}/code/admin", + "cwd": "${workspaceFolder}/code/backend", "preLaunchTask": "pip install (code)", }, { @@ -31,14 +31,14 @@ "--debug", "run" ], - "cwd": "${workspaceFolder}/code/app", + "cwd": "${workspaceFolder}/code", "preLaunchTask": "pip install (code)" }, { "name": "Launch Frontend (UI)", "type": "node", "request": "launch", - "cwd": "${workspaceFolder}/code/app/frontend", + "cwd": "${workspaceFolder}/code/frontend", "preLaunchTask": "npm install (code)", "runtimeExecutable": "npm", "runtimeArgs": [ diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 8ddda72a3..202ebec0c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -9,7 +9,7 @@ "isBackground": true, "dependsOn": "pip install (functions)", "options": { - "cwd": "${workspaceFolder}/backend" + "cwd": "${workspaceFolder}/code/backend/batch" } }, { @@ -26,7 +26,7 @@ }, "problemMatcher": [], "options": { - "cwd": "${workspaceFolder}/backend" + "cwd": "${workspaceFolder}/code/backend" } }, { @@ -44,7 +44,7 @@ "command": "npm install", "problemMatcher": [], "options": { - "cwd": "${workspaceFolder}/code/app/frontend" + "cwd": "${workspaceFolder}/code/frontend" } } ] diff --git a/azure.yaml b/azure.yaml index 88cfeb0ee..afb12c502 100644 --- a/azure.yaml +++ b/azure.yaml @@ -4,16 +4,9 @@ name: chat-with-your-data-solution-accelerator metadata: template: chat-with-your-data-solution-accelerator@0.0.1-beta -hooks: - prepackage: - windows: - shell: pwsh - run: robocopy -e ./code/utilities ./code/app/utilities; robocopy -e ./code/utilities ./code/admin/utilities; robocopy -e ./code/ ./code/admin/ requirements.txt /lev:1; robocopy -e ./code/utilities ./code/batch/utilities; robocopy -e ./code/ ./code/batch/ requirements.txt /lev:1; - continueOnError: true - services: web: - project: ./code/app + project: ./code language: py host: appservice hooks: @@ -25,11 +18,11 @@ services: continueOnError: false adminweb: - project: ./code/admin + project: ./code/backend language: py host: appservice function: - project: ./code/batch + project: ./code/backend/batch language: py host: function diff --git a/code/app/app.py b/code/app.py similarity index 98% rename from code/app/app.py rename to code/app.py index 341c5b24a..807541766 100644 --- a/code/app/app.py +++ b/code/app.py @@ -26,34 +26,7 @@ def static_file(path): return app.send_static_file(path) -# ACS Integration Settings -AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") -AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") -AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.environ.get("AZURE_SEARCH_USE_SEMANTIC_SEARCH", "False") -AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.environ.get("AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG", "default") -AZURE_SEARCH_TOP_K = os.environ.get("AZURE_SEARCH_TOP_K", 5) -AZURE_SEARCH_ENABLE_IN_DOMAIN = os.environ.get("AZURE_SEARCH_ENABLE_IN_DOMAIN", "true") -AZURE_SEARCH_CONTENT_COLUMNS = os.environ.get("AZURE_SEARCH_CONTENT_COLUMNS") -AZURE_SEARCH_FILENAME_COLUMN = os.environ.get("AZURE_SEARCH_FILENAME_COLUMN") -AZURE_SEARCH_TITLE_COLUMN = os.environ.get("AZURE_SEARCH_TITLE_COLUMN") -AZURE_SEARCH_URL_COLUMN = os.environ.get("AZURE_SEARCH_URL_COLUMN") - -# AOAI Integration Settings -AZURE_OPENAI_RESOURCE = os.environ.get("AZURE_OPENAI_RESOURCE") -AZURE_OPENAI_MODEL = os.environ.get("AZURE_OPENAI_MODEL") -AZURE_OPENAI_TEMPERATURE = os.environ.get("AZURE_OPENAI_TEMPERATURE", 0) -AZURE_OPENAI_TOP_P = os.environ.get("AZURE_OPENAI_TOP_P", 1.0) -AZURE_OPENAI_MAX_TOKENS = os.environ.get("AZURE_OPENAI_MAX_TOKENS", 1000) -AZURE_OPENAI_STOP_SEQUENCE = os.environ.get("AZURE_OPENAI_STOP_SEQUENCE") -AZURE_OPENAI_SYSTEM_MESSAGE = os.environ.get("AZURE_OPENAI_SYSTEM_MESSAGE", "You are an AI assistant that helps people find information.") -AZURE_OPENAI_API_VERSION = os.environ.get("AZURE_OPENAI_API_VERSION", "2023-06-01-preview") -AZURE_OPENAI_STREAM = os.environ.get("AZURE_OPENAI_STREAM", "true") -AZURE_OPENAI_MODEL_NAME = os.environ.get("AZURE_OPENAI_MODEL_NAME", "gpt-35-turbo") # Name of the model, e.g. 'gpt-35-turbo' or 'gpt-4' -AZURE_TOKEN_PROVIDER = get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default") AZURE_AUTH_TYPE = os.environ.get("AZURE_AUTH_TYPE", "keys") - -SHOULD_STREAM = True if AZURE_OPENAI_STREAM.lower() == "true" else False - # Initialize Azure keys based on authentication type and environment settings. # When AZURE_AUTH_TYPE is not 'rbac' and USE_KEY_VAULT environment variable is set, # Azure keys are securely fetched from Azure Key Vault using DefaultAzureCredential. @@ -81,6 +54,33 @@ def get_config(): 'azureSpeechRegion': azure_speech_region }) +# ACS Integration Settings +AZURE_SEARCH_SERVICE = os.environ.get("AZURE_SEARCH_SERVICE") +AZURE_SEARCH_INDEX = os.environ.get("AZURE_SEARCH_INDEX") +AZURE_SEARCH_USE_SEMANTIC_SEARCH = os.environ.get("AZURE_SEARCH_USE_SEMANTIC_SEARCH", "False") +AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG = os.environ.get("AZURE_SEARCH_SEMANTIC_SEARCH_CONFIG", "default") +AZURE_SEARCH_TOP_K = os.environ.get("AZURE_SEARCH_TOP_K", 5) +AZURE_SEARCH_ENABLE_IN_DOMAIN = os.environ.get("AZURE_SEARCH_ENABLE_IN_DOMAIN", "true") +AZURE_SEARCH_CONTENT_COLUMNS = os.environ.get("AZURE_SEARCH_CONTENT_COLUMNS") +AZURE_SEARCH_FILENAME_COLUMN = os.environ.get("AZURE_SEARCH_FILENAME_COLUMN") +AZURE_SEARCH_TITLE_COLUMN = os.environ.get("AZURE_SEARCH_TITLE_COLUMN") +AZURE_SEARCH_URL_COLUMN = os.environ.get("AZURE_SEARCH_URL_COLUMN") + +# AOAI Integration Settings +AZURE_OPENAI_RESOURCE = os.environ.get("AZURE_OPENAI_RESOURCE") +AZURE_OPENAI_MODEL = os.environ.get("AZURE_OPENAI_MODEL") +AZURE_OPENAI_TEMPERATURE = os.environ.get("AZURE_OPENAI_TEMPERATURE", 0) +AZURE_OPENAI_TOP_P = os.environ.get("AZURE_OPENAI_TOP_P", 1.0) +AZURE_OPENAI_MAX_TOKENS = os.environ.get("AZURE_OPENAI_MAX_TOKENS", 1000) +AZURE_OPENAI_STOP_SEQUENCE = os.environ.get("AZURE_OPENAI_STOP_SEQUENCE") +AZURE_OPENAI_SYSTEM_MESSAGE = os.environ.get("AZURE_OPENAI_SYSTEM_MESSAGE", "You are an AI assistant that helps people find information.") +AZURE_OPENAI_API_VERSION = os.environ.get("AZURE_OPENAI_API_VERSION", "2023-06-01-preview") +AZURE_OPENAI_STREAM = os.environ.get("AZURE_OPENAI_STREAM", "true") +AZURE_OPENAI_MODEL_NAME = os.environ.get("AZURE_OPENAI_MODEL_NAME", "gpt-35-turbo") # Name of the model, e.g. 'gpt-35-turbo' or 'gpt-4' +AZURE_TOKEN_PROVIDER = get_bearer_token_provider(DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default") + +SHOULD_STREAM = True if AZURE_OPENAI_STREAM.lower() == "true" else False + def is_chat_model(): if 'gpt-4' in AZURE_OPENAI_MODEL_NAME.lower(): return True @@ -287,7 +287,7 @@ def conversation_azure_byod(): @app.route("/api/conversation/custom", methods=["GET","POST"]) def conversation_custom(): - from utilities.helpers.OrchestratorHelper import Orchestrator, OrchestrationSettings + from backend.batch.utilities.helpers.OrchestratorHelper import Orchestrator, OrchestrationSettings message_orchestrator = Orchestrator() try: @@ -298,7 +298,7 @@ def conversation_custom(): for i,k in enumerate(user_assistant_messages): if i % 2 == 0: chat_history.append((user_assistant_messages[i]['content'],user_assistant_messages[i+1]['content'])) - from utilities.helpers.ConfigHelper import ConfigHelper + from backend.batch.utilities.helpers.ConfigHelper import ConfigHelper messages = message_orchestrator.handle_message(user_message=user_message, chat_history=chat_history, conversation_id=conversation_id, orchestrator=ConfigHelper.get_active_config_or_default().orchestrator) response_obj = { diff --git a/code/admin/Admin.py b/code/backend/Admin.py similarity index 100% rename from code/admin/Admin.py rename to code/backend/Admin.py diff --git a/code/batch/.funcignore b/code/backend/batch/.funcignore similarity index 100% rename from code/batch/.funcignore rename to code/backend/batch/.funcignore diff --git a/code/batch/.gitignore b/code/backend/batch/.gitignore similarity index 100% rename from code/batch/.gitignore rename to code/backend/batch/.gitignore diff --git a/code/batch/.vscode/extensions.json b/code/backend/batch/.vscode/extensions.json similarity index 100% rename from code/batch/.vscode/extensions.json rename to code/backend/batch/.vscode/extensions.json diff --git a/code/batch/AddURLEmbeddings.py b/code/backend/batch/AddURLEmbeddings.py similarity index 100% rename from code/batch/AddURLEmbeddings.py rename to code/backend/batch/AddURLEmbeddings.py diff --git a/code/batch/BatchPushResults.py b/code/backend/batch/BatchPushResults.py similarity index 100% rename from code/batch/BatchPushResults.py rename to code/backend/batch/BatchPushResults.py diff --git a/code/batch/BatchStartProcessing.py b/code/backend/batch/BatchStartProcessing.py similarity index 100% rename from code/batch/BatchStartProcessing.py rename to code/backend/batch/BatchStartProcessing.py diff --git a/code/batch/function_app.py b/code/backend/batch/function_app.py similarity index 100% rename from code/batch/function_app.py rename to code/backend/batch/function_app.py diff --git a/code/batch/host.json b/code/backend/batch/host.json similarity index 100% rename from code/batch/host.json rename to code/backend/batch/host.json diff --git a/code/batch/local.settings.json.sample b/code/backend/batch/local.settings.json.sample similarity index 100% rename from code/batch/local.settings.json.sample rename to code/backend/batch/local.settings.json.sample diff --git a/code/utilities/__init__.py b/code/backend/batch/utilities/__init__.py similarity index 100% rename from code/utilities/__init__.py rename to code/backend/batch/utilities/__init__.py diff --git a/code/utilities/common/Answer.py b/code/backend/batch/utilities/common/Answer.py similarity index 100% rename from code/utilities/common/Answer.py rename to code/backend/batch/utilities/common/Answer.py diff --git a/code/utilities/common/SourceDocument.py b/code/backend/batch/utilities/common/SourceDocument.py similarity index 100% rename from code/utilities/common/SourceDocument.py rename to code/backend/batch/utilities/common/SourceDocument.py diff --git a/code/utilities/common/__init__.py b/code/backend/batch/utilities/common/__init__.py similarity index 100% rename from code/utilities/common/__init__.py rename to code/backend/batch/utilities/common/__init__.py diff --git a/code/utilities/document_chunking/DocumentChunkingBase.py b/code/backend/batch/utilities/document_chunking/DocumentChunkingBase.py similarity index 100% rename from code/utilities/document_chunking/DocumentChunkingBase.py rename to code/backend/batch/utilities/document_chunking/DocumentChunkingBase.py diff --git a/code/utilities/document_chunking/FixedSizeOverlap.py b/code/backend/batch/utilities/document_chunking/FixedSizeOverlap.py similarity index 100% rename from code/utilities/document_chunking/FixedSizeOverlap.py rename to code/backend/batch/utilities/document_chunking/FixedSizeOverlap.py diff --git a/code/utilities/document_chunking/Layout.py b/code/backend/batch/utilities/document_chunking/Layout.py similarity index 100% rename from code/utilities/document_chunking/Layout.py rename to code/backend/batch/utilities/document_chunking/Layout.py diff --git a/code/utilities/document_chunking/Page.py b/code/backend/batch/utilities/document_chunking/Page.py similarity index 100% rename from code/utilities/document_chunking/Page.py rename to code/backend/batch/utilities/document_chunking/Page.py diff --git a/code/utilities/document_chunking/Paragraph.py b/code/backend/batch/utilities/document_chunking/Paragraph.py similarity index 100% rename from code/utilities/document_chunking/Paragraph.py rename to code/backend/batch/utilities/document_chunking/Paragraph.py diff --git a/code/utilities/document_chunking/Strategies.py b/code/backend/batch/utilities/document_chunking/Strategies.py similarity index 100% rename from code/utilities/document_chunking/Strategies.py rename to code/backend/batch/utilities/document_chunking/Strategies.py diff --git a/code/utilities/document_chunking/__init__.py b/code/backend/batch/utilities/document_chunking/__init__.py similarity index 100% rename from code/utilities/document_chunking/__init__.py rename to code/backend/batch/utilities/document_chunking/__init__.py diff --git a/code/utilities/document_loading/DocumentLoadingBase.py b/code/backend/batch/utilities/document_loading/DocumentLoadingBase.py similarity index 100% rename from code/utilities/document_loading/DocumentLoadingBase.py rename to code/backend/batch/utilities/document_loading/DocumentLoadingBase.py diff --git a/code/utilities/document_loading/Layout.py b/code/backend/batch/utilities/document_loading/Layout.py similarity index 100% rename from code/utilities/document_loading/Layout.py rename to code/backend/batch/utilities/document_loading/Layout.py diff --git a/code/utilities/document_loading/Read.py b/code/backend/batch/utilities/document_loading/Read.py similarity index 100% rename from code/utilities/document_loading/Read.py rename to code/backend/batch/utilities/document_loading/Read.py diff --git a/code/utilities/document_loading/Strategies.py b/code/backend/batch/utilities/document_loading/Strategies.py similarity index 100% rename from code/utilities/document_loading/Strategies.py rename to code/backend/batch/utilities/document_loading/Strategies.py diff --git a/code/utilities/document_loading/Web.py b/code/backend/batch/utilities/document_loading/Web.py similarity index 100% rename from code/utilities/document_loading/Web.py rename to code/backend/batch/utilities/document_loading/Web.py diff --git a/code/utilities/document_loading/WordDocument.py b/code/backend/batch/utilities/document_loading/WordDocument.py similarity index 100% rename from code/utilities/document_loading/WordDocument.py rename to code/backend/batch/utilities/document_loading/WordDocument.py diff --git a/code/utilities/document_loading/__init__.py b/code/backend/batch/utilities/document_loading/__init__.py similarity index 100% rename from code/utilities/document_loading/__init__.py rename to code/backend/batch/utilities/document_loading/__init__.py diff --git a/code/utilities/helpers/AzureBlobStorageHelper.py b/code/backend/batch/utilities/helpers/AzureBlobStorageHelper.py similarity index 100% rename from code/utilities/helpers/AzureBlobStorageHelper.py rename to code/backend/batch/utilities/helpers/AzureBlobStorageHelper.py diff --git a/code/utilities/helpers/AzureFormRecognizerHelper.py b/code/backend/batch/utilities/helpers/AzureFormRecognizerHelper.py similarity index 100% rename from code/utilities/helpers/AzureFormRecognizerHelper.py rename to code/backend/batch/utilities/helpers/AzureFormRecognizerHelper.py diff --git a/code/utilities/helpers/AzureSearchHelper.py b/code/backend/batch/utilities/helpers/AzureSearchHelper.py similarity index 100% rename from code/utilities/helpers/AzureSearchHelper.py rename to code/backend/batch/utilities/helpers/AzureSearchHelper.py diff --git a/code/utilities/helpers/ConfigHelper.py b/code/backend/batch/utilities/helpers/ConfigHelper.py similarity index 100% rename from code/utilities/helpers/ConfigHelper.py rename to code/backend/batch/utilities/helpers/ConfigHelper.py diff --git a/code/utilities/helpers/DocumentChunkingHelper.py b/code/backend/batch/utilities/helpers/DocumentChunkingHelper.py similarity index 100% rename from code/utilities/helpers/DocumentChunkingHelper.py rename to code/backend/batch/utilities/helpers/DocumentChunkingHelper.py diff --git a/code/utilities/helpers/DocumentLoadingHelper.py b/code/backend/batch/utilities/helpers/DocumentLoadingHelper.py similarity index 100% rename from code/utilities/helpers/DocumentLoadingHelper.py rename to code/backend/batch/utilities/helpers/DocumentLoadingHelper.py diff --git a/code/utilities/helpers/DocumentProcessorHelper.py b/code/backend/batch/utilities/helpers/DocumentProcessorHelper.py similarity index 100% rename from code/utilities/helpers/DocumentProcessorHelper.py rename to code/backend/batch/utilities/helpers/DocumentProcessorHelper.py diff --git a/code/utilities/helpers/EnvHelper.py b/code/backend/batch/utilities/helpers/EnvHelper.py similarity index 100% rename from code/utilities/helpers/EnvHelper.py rename to code/backend/batch/utilities/helpers/EnvHelper.py diff --git a/code/utilities/helpers/LLMHelper.py b/code/backend/batch/utilities/helpers/LLMHelper.py similarity index 100% rename from code/utilities/helpers/LLMHelper.py rename to code/backend/batch/utilities/helpers/LLMHelper.py diff --git a/code/utilities/helpers/OrchestratorHelper.py b/code/backend/batch/utilities/helpers/OrchestratorHelper.py similarity index 100% rename from code/utilities/helpers/OrchestratorHelper.py rename to code/backend/batch/utilities/helpers/OrchestratorHelper.py diff --git a/code/utilities/helpers/__init__.py b/code/backend/batch/utilities/helpers/__init__.py similarity index 100% rename from code/utilities/helpers/__init__.py rename to code/backend/batch/utilities/helpers/__init__.py diff --git a/code/utilities/loggers/ConversationLogger.py b/code/backend/batch/utilities/loggers/ConversationLogger.py similarity index 100% rename from code/utilities/loggers/ConversationLogger.py rename to code/backend/batch/utilities/loggers/ConversationLogger.py diff --git a/code/utilities/loggers/TokenLogger.py b/code/backend/batch/utilities/loggers/TokenLogger.py similarity index 100% rename from code/utilities/loggers/TokenLogger.py rename to code/backend/batch/utilities/loggers/TokenLogger.py diff --git a/code/utilities/orchestrator/LangChainAgent.py b/code/backend/batch/utilities/orchestrator/LangChainAgent.py similarity index 100% rename from code/utilities/orchestrator/LangChainAgent.py rename to code/backend/batch/utilities/orchestrator/LangChainAgent.py diff --git a/code/utilities/orchestrator/OpenAIFunctions.py b/code/backend/batch/utilities/orchestrator/OpenAIFunctions.py similarity index 100% rename from code/utilities/orchestrator/OpenAIFunctions.py rename to code/backend/batch/utilities/orchestrator/OpenAIFunctions.py diff --git a/code/utilities/orchestrator/OrchestratorBase.py b/code/backend/batch/utilities/orchestrator/OrchestratorBase.py similarity index 100% rename from code/utilities/orchestrator/OrchestratorBase.py rename to code/backend/batch/utilities/orchestrator/OrchestratorBase.py diff --git a/code/utilities/orchestrator/Strategies.py b/code/backend/batch/utilities/orchestrator/Strategies.py similarity index 100% rename from code/utilities/orchestrator/Strategies.py rename to code/backend/batch/utilities/orchestrator/Strategies.py diff --git a/code/utilities/orchestrator/__init__.py b/code/backend/batch/utilities/orchestrator/__init__.py similarity index 100% rename from code/utilities/orchestrator/__init__.py rename to code/backend/batch/utilities/orchestrator/__init__.py diff --git a/code/utilities/parser/OutputParserTool.py b/code/backend/batch/utilities/parser/OutputParserTool.py similarity index 100% rename from code/utilities/parser/OutputParserTool.py rename to code/backend/batch/utilities/parser/OutputParserTool.py diff --git a/code/utilities/parser/ParserBase.py b/code/backend/batch/utilities/parser/ParserBase.py similarity index 100% rename from code/utilities/parser/ParserBase.py rename to code/backend/batch/utilities/parser/ParserBase.py diff --git a/code/utilities/parser/__init__.py b/code/backend/batch/utilities/parser/__init__.py similarity index 100% rename from code/utilities/parser/__init__.py rename to code/backend/batch/utilities/parser/__init__.py diff --git a/code/utilities/tests/__init__.py b/code/backend/batch/utilities/tests/__init__.py similarity index 100% rename from code/utilities/tests/__init__.py rename to code/backend/batch/utilities/tests/__init__.py diff --git a/code/utilities/tests/test_AzureBlobStorage.py b/code/backend/batch/utilities/tests/test_AzureBlobStorage.py similarity index 100% rename from code/utilities/tests/test_AzureBlobStorage.py rename to code/backend/batch/utilities/tests/test_AzureBlobStorage.py diff --git a/code/utilities/tests/test_ContentSafetyChecker.py b/code/backend/batch/utilities/tests/test_ContentSafetyChecker.py similarity index 100% rename from code/utilities/tests/test_ContentSafetyChecker.py rename to code/backend/batch/utilities/tests/test_ContentSafetyChecker.py diff --git a/code/utilities/tests/test_DocumentChunking.py b/code/backend/batch/utilities/tests/test_DocumentChunking.py similarity index 100% rename from code/utilities/tests/test_DocumentChunking.py rename to code/backend/batch/utilities/tests/test_DocumentChunking.py diff --git a/code/utilities/tests/test_DocumentLoading.py b/code/backend/batch/utilities/tests/test_DocumentLoading.py similarity index 100% rename from code/utilities/tests/test_DocumentLoading.py rename to code/backend/batch/utilities/tests/test_DocumentLoading.py diff --git a/code/utilities/tests/test_DocumentProcessor.py b/code/backend/batch/utilities/tests/test_DocumentProcessor.py similarity index 100% rename from code/utilities/tests/test_DocumentProcessor.py rename to code/backend/batch/utilities/tests/test_DocumentProcessor.py diff --git a/code/utilities/tests/test_Orchestrator.py b/code/backend/batch/utilities/tests/test_Orchestrator.py similarity index 100% rename from code/utilities/tests/test_Orchestrator.py rename to code/backend/batch/utilities/tests/test_Orchestrator.py diff --git a/code/utilities/tests/test_OutputParserTool.py b/code/backend/batch/utilities/tests/test_OutputParserTool.py similarity index 100% rename from code/utilities/tests/test_OutputParserTool.py rename to code/backend/batch/utilities/tests/test_OutputParserTool.py diff --git a/code/utilities/tools/AnswerProcessingBase.py b/code/backend/batch/utilities/tools/AnswerProcessingBase.py similarity index 100% rename from code/utilities/tools/AnswerProcessingBase.py rename to code/backend/batch/utilities/tools/AnswerProcessingBase.py diff --git a/code/utilities/tools/AnsweringToolBase.py b/code/backend/batch/utilities/tools/AnsweringToolBase.py similarity index 100% rename from code/utilities/tools/AnsweringToolBase.py rename to code/backend/batch/utilities/tools/AnsweringToolBase.py diff --git a/code/utilities/tools/ContentSafetyChecker.py b/code/backend/batch/utilities/tools/ContentSafetyChecker.py similarity index 100% rename from code/utilities/tools/ContentSafetyChecker.py rename to code/backend/batch/utilities/tools/ContentSafetyChecker.py diff --git a/code/utilities/tools/PostPromptTool.py b/code/backend/batch/utilities/tools/PostPromptTool.py similarity index 100% rename from code/utilities/tools/PostPromptTool.py rename to code/backend/batch/utilities/tools/PostPromptTool.py diff --git a/code/utilities/tools/QuestionAnswerTool.py b/code/backend/batch/utilities/tools/QuestionAnswerTool.py similarity index 100% rename from code/utilities/tools/QuestionAnswerTool.py rename to code/backend/batch/utilities/tools/QuestionAnswerTool.py diff --git a/code/utilities/tools/TextProcessingTool.py b/code/backend/batch/utilities/tools/TextProcessingTool.py similarity index 100% rename from code/utilities/tools/TextProcessingTool.py rename to code/backend/batch/utilities/tools/TextProcessingTool.py diff --git a/code/utilities/tools/__init__.py b/code/backend/batch/utilities/tools/__init__.py similarity index 100% rename from code/utilities/tools/__init__.py rename to code/backend/batch/utilities/tools/__init__.py diff --git a/code/admin/images/favicon.ico b/code/backend/images/favicon.ico similarity index 100% rename from code/admin/images/favicon.ico rename to code/backend/images/favicon.ico diff --git a/code/admin/images/logo.png b/code/backend/images/logo.png similarity index 100% rename from code/admin/images/logo.png rename to code/backend/images/logo.png diff --git a/code/admin/pages/01_Ingest_Data.py b/code/backend/pages/01_Ingest_Data.py similarity index 99% rename from code/admin/pages/01_Ingest_Data.py rename to code/backend/pages/01_Ingest_Data.py index 6646fc5ae..9dd19b3f4 100644 --- a/code/admin/pages/01_Ingest_Data.py +++ b/code/backend/pages/01_Ingest_Data.py @@ -13,7 +13,7 @@ import urllib.parse import sys sys.path.append(os.path.join(os.path.dirname(__file__), '..')) -from utilities.helpers.ConfigHelper import ConfigHelper +from batch.utilities.helpers.ConfigHelper import ConfigHelper from dotenv import load_dotenv load_dotenv() diff --git a/code/admin/pages/02_Explore_Data.py b/code/backend/pages/02_Explore_Data.py similarity index 96% rename from code/admin/pages/02_Explore_Data.py rename to code/backend/pages/02_Explore_Data.py index 68952c396..f5e14ef2b 100644 --- a/code/admin/pages/02_Explore_Data.py +++ b/code/backend/pages/02_Explore_Data.py @@ -6,7 +6,7 @@ import pandas as pd import sys sys.path.append(os.path.join(os.path.dirname(__file__), '..')) -from utilities.helpers.AzureSearchHelper import AzureSearchHelper +from batch.utilities.helpers.AzureSearchHelper import AzureSearchHelper from dotenv import load_dotenv load_dotenv() diff --git a/code/admin/pages/03_Delete_Data.py b/code/backend/pages/03_Delete_Data.py similarity index 97% rename from code/admin/pages/03_Delete_Data.py rename to code/backend/pages/03_Delete_Data.py index 811eb87a9..abb0d1ddf 100644 --- a/code/admin/pages/03_Delete_Data.py +++ b/code/backend/pages/03_Delete_Data.py @@ -4,7 +4,7 @@ import logging import sys sys.path.append(os.path.join(os.path.dirname(__file__), '..')) -from utilities.helpers.AzureSearchHelper import AzureSearchHelper +from batch.utilities.helpers.AzureSearchHelper import AzureSearchHelper from dotenv import load_dotenv load_dotenv() diff --git a/code/admin/pages/04_Configuration.py b/code/backend/pages/04_Configuration.py similarity index 99% rename from code/admin/pages/04_Configuration.py rename to code/backend/pages/04_Configuration.py index 49b663fcd..302861ef6 100644 --- a/code/admin/pages/04_Configuration.py +++ b/code/backend/pages/04_Configuration.py @@ -6,7 +6,7 @@ from dotenv import load_dotenv import sys sys.path.append(os.path.join(os.path.dirname(__file__), '..')) -from utilities.helpers.ConfigHelper import ConfigHelper +from batch.utilities.helpers.ConfigHelper import ConfigHelper load_dotenv() diff --git a/code/app/requirements.txt b/code/backend/requirements.txt similarity index 64% rename from code/app/requirements.txt rename to code/backend/requirements.txt index f5a459a1d..e5e89ed6f 100644 --- a/code/app/requirements.txt +++ b/code/backend/requirements.txt @@ -1,19 +1,28 @@ -azure-identity==1.15.0 -Flask==2.3.2 +azure-functions==1.18.0 +streamlit==1.30.0 openai==1.6.1 -azure-storage-blob==12.19.0 +matplotlib==3.8.2 +plotly==5.18.0 +scipy==1.11.4 +scikit-learn==1.3.2 +transformers==4.36.2 python-dotenv==1.0.0 -langchain==0.0.354 azure-ai-formrecognizer==3.3.2 +azure-storage-blob==12.19.0 +azure-identity==1.15.0 +azure-ai-contentsafety==1.0.0 requests==2.31.0 tiktoken==0.5.2 azure-storage-queue==12.9.0 +langchain==0.0.354 beautifulsoup4==4.12.2 fake-useragent==1.4.0 chardet==5.2.0 --extra-index-url https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/ azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.13 -azure-ai-contentsafety==1.0.0 +pandas==1.5.1 python-docx==1.1.0 -azure-keyvault-secrets==4.4.* \ No newline at end of file +azure-keyvault-secrets==4.4.* +# Add dev dependencies here - this will be refactored out by Poetry +pytest==7.4.4 diff --git a/code/app/frontend/index.html b/code/frontend/index.html similarity index 100% rename from code/app/frontend/index.html rename to code/frontend/index.html diff --git a/code/app/frontend/package-lock.json b/code/frontend/package-lock.json similarity index 99% rename from code/app/frontend/package-lock.json rename to code/frontend/package-lock.json index 054e27401..6b219ed9d 100644 --- a/code/app/frontend/package-lock.json +++ b/code/frontend/package-lock.json @@ -11,6 +11,7 @@ "@babel/traverse": "^7.23.2", "@fluentui/react": "^8.105.3", "@fluentui/react-icons": "^2.0.195", + "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/react-fontawesome": "github:fortawesome/react-fontawesome", "lodash": "^4.17.21", @@ -955,18 +956,26 @@ } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.0.tgz", - "integrity": "sha512-5DrR+oxQr+ruRQ3CEVV8DSCT/q8Atm56+FzAs0P6eW/epW47OmecSpSwc/YTlJ3u5BfPKUBSGyPR2qjZ+5eIgA==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz", + "integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==", "hasInstallScript": true, - "peer": true, "dependencies": { - "@fortawesome/fontawesome-common-types": "6.5.0" + "@fortawesome/fontawesome-common-types": "6.5.1" }, "engines": { "node": ">=6" } }, + "node_modules/@fortawesome/fontawesome-svg-core/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", + "integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, "node_modules/@fortawesome/free-solid-svg-icons": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.0.tgz", @@ -1150,12 +1159,14 @@ "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true }, "node_modules/@types/react": { "version": "18.2.11", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.11.tgz", "integrity": "sha512-+hsJr9hmwyDecSMQAmX7drgbDpyE+EgSF6t7+5QEBAn1tQK7kl1vWZ4iRf6SjQ8lk7dyEULxUmZOIpN0W5baZA==", + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -1166,6 +1177,7 @@ "version": "18.2.4", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -1173,7 +1185,8 @@ "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true }, "node_modules/@types/unist": { "version": "2.0.6", diff --git a/code/app/frontend/package.json b/code/frontend/package.json similarity index 95% rename from code/app/frontend/package.json rename to code/frontend/package.json index 64147388f..e91fc8de3 100644 --- a/code/app/frontend/package.json +++ b/code/frontend/package.json @@ -12,6 +12,7 @@ "@babel/traverse": "^7.23.2", "@fluentui/react": "^8.105.3", "@fluentui/react-icons": "^2.0.195", + "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.4.2", "@fortawesome/react-fontawesome": "github:fortawesome/react-fontawesome", "lodash": "^4.17.21", diff --git a/code/app/frontend/public/favicon.ico b/code/frontend/public/favicon.ico similarity index 100% rename from code/app/frontend/public/favicon.ico rename to code/frontend/public/favicon.ico diff --git a/code/app/frontend/src/api/api.ts b/code/frontend/src/api/api.ts similarity index 100% rename from code/app/frontend/src/api/api.ts rename to code/frontend/src/api/api.ts diff --git a/code/app/frontend/src/api/index.ts b/code/frontend/src/api/index.ts similarity index 100% rename from code/app/frontend/src/api/index.ts rename to code/frontend/src/api/index.ts diff --git a/code/app/frontend/src/api/models.ts b/code/frontend/src/api/models.ts similarity index 100% rename from code/app/frontend/src/api/models.ts rename to code/frontend/src/api/models.ts diff --git a/code/app/frontend/src/assets/Azure.svg b/code/frontend/src/assets/Azure.svg similarity index 100% rename from code/app/frontend/src/assets/Azure.svg rename to code/frontend/src/assets/Azure.svg diff --git a/code/app/frontend/src/assets/Send.svg b/code/frontend/src/assets/Send.svg similarity index 100% rename from code/app/frontend/src/assets/Send.svg rename to code/frontend/src/assets/Send.svg diff --git a/code/app/frontend/src/assets/mic-outline.svg b/code/frontend/src/assets/mic-outline.svg similarity index 100% rename from code/app/frontend/src/assets/mic-outline.svg rename to code/frontend/src/assets/mic-outline.svg diff --git a/code/app/frontend/src/components/Answer/Answer.module.css b/code/frontend/src/components/Answer/Answer.module.css similarity index 100% rename from code/app/frontend/src/components/Answer/Answer.module.css rename to code/frontend/src/components/Answer/Answer.module.css diff --git a/code/app/frontend/src/components/Answer/Answer.tsx b/code/frontend/src/components/Answer/Answer.tsx similarity index 100% rename from code/app/frontend/src/components/Answer/Answer.tsx rename to code/frontend/src/components/Answer/Answer.tsx diff --git a/code/app/frontend/src/components/Answer/AnswerParser.tsx b/code/frontend/src/components/Answer/AnswerParser.tsx similarity index 100% rename from code/app/frontend/src/components/Answer/AnswerParser.tsx rename to code/frontend/src/components/Answer/AnswerParser.tsx diff --git a/code/app/frontend/src/components/Answer/index.ts b/code/frontend/src/components/Answer/index.ts similarity index 100% rename from code/app/frontend/src/components/Answer/index.ts rename to code/frontend/src/components/Answer/index.ts diff --git a/code/app/frontend/src/components/QuestionInput/QuestionInput.module.css b/code/frontend/src/components/QuestionInput/QuestionInput.module.css similarity index 100% rename from code/app/frontend/src/components/QuestionInput/QuestionInput.module.css rename to code/frontend/src/components/QuestionInput/QuestionInput.module.css diff --git a/code/app/frontend/src/components/QuestionInput/QuestionInput.tsx b/code/frontend/src/components/QuestionInput/QuestionInput.tsx similarity index 100% rename from code/app/frontend/src/components/QuestionInput/QuestionInput.tsx rename to code/frontend/src/components/QuestionInput/QuestionInput.tsx diff --git a/code/app/frontend/src/components/QuestionInput/index.ts b/code/frontend/src/components/QuestionInput/index.ts similarity index 100% rename from code/app/frontend/src/components/QuestionInput/index.ts rename to code/frontend/src/components/QuestionInput/index.ts diff --git a/code/app/frontend/src/index.css b/code/frontend/src/index.css similarity index 100% rename from code/app/frontend/src/index.css rename to code/frontend/src/index.css diff --git a/code/app/frontend/src/index.tsx b/code/frontend/src/index.tsx similarity index 100% rename from code/app/frontend/src/index.tsx rename to code/frontend/src/index.tsx diff --git a/code/app/frontend/src/pages/NoPage.tsx b/code/frontend/src/pages/NoPage.tsx similarity index 100% rename from code/app/frontend/src/pages/NoPage.tsx rename to code/frontend/src/pages/NoPage.tsx diff --git a/code/app/frontend/src/pages/chat/Chat.module.css b/code/frontend/src/pages/chat/Chat.module.css similarity index 100% rename from code/app/frontend/src/pages/chat/Chat.module.css rename to code/frontend/src/pages/chat/Chat.module.css diff --git a/code/app/frontend/src/pages/chat/Chat.tsx b/code/frontend/src/pages/chat/Chat.tsx similarity index 100% rename from code/app/frontend/src/pages/chat/Chat.tsx rename to code/frontend/src/pages/chat/Chat.tsx diff --git a/code/app/frontend/src/pages/layout/Layout.module.css b/code/frontend/src/pages/layout/Layout.module.css similarity index 100% rename from code/app/frontend/src/pages/layout/Layout.module.css rename to code/frontend/src/pages/layout/Layout.module.css diff --git a/code/app/frontend/src/pages/layout/Layout.tsx b/code/frontend/src/pages/layout/Layout.tsx similarity index 100% rename from code/app/frontend/src/pages/layout/Layout.tsx rename to code/frontend/src/pages/layout/Layout.tsx diff --git a/code/app/frontend/src/vite-env.d.ts b/code/frontend/src/vite-env.d.ts similarity index 100% rename from code/app/frontend/src/vite-env.d.ts rename to code/frontend/src/vite-env.d.ts diff --git a/code/app/frontend/tsconfig.json b/code/frontend/tsconfig.json similarity index 100% rename from code/app/frontend/tsconfig.json rename to code/frontend/tsconfig.json diff --git a/code/app/frontend/tsconfig.node.json b/code/frontend/tsconfig.node.json similarity index 100% rename from code/app/frontend/tsconfig.node.json rename to code/frontend/tsconfig.node.json diff --git a/code/app/frontend/vite.config.ts b/code/frontend/vite.config.ts similarity index 100% rename from code/app/frontend/vite.config.ts rename to code/frontend/vite.config.ts diff --git a/code/requirements.txt b/code/requirements.txt index e5e89ed6f..f5a459a1d 100644 --- a/code/requirements.txt +++ b/code/requirements.txt @@ -1,28 +1,19 @@ -azure-functions==1.18.0 -streamlit==1.30.0 +azure-identity==1.15.0 +Flask==2.3.2 openai==1.6.1 -matplotlib==3.8.2 -plotly==5.18.0 -scipy==1.11.4 -scikit-learn==1.3.2 -transformers==4.36.2 +azure-storage-blob==12.19.0 python-dotenv==1.0.0 +langchain==0.0.354 azure-ai-formrecognizer==3.3.2 -azure-storage-blob==12.19.0 -azure-identity==1.15.0 -azure-ai-contentsafety==1.0.0 requests==2.31.0 tiktoken==0.5.2 azure-storage-queue==12.9.0 -langchain==0.0.354 beautifulsoup4==4.12.2 fake-useragent==1.4.0 chardet==5.2.0 --extra-index-url https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-python/pypi/simple/ azure-search-documents==11.4.0b8 opencensus-ext-azure==1.1.13 -pandas==1.5.1 +azure-ai-contentsafety==1.0.0 python-docx==1.1.0 -azure-keyvault-secrets==4.4.* -# Add dev dependencies here - this will be refactored out by Poetry -pytest==7.4.4 +azure-keyvault-secrets==4.4.* \ No newline at end of file diff --git a/code/app/static/assets/Azure-d0a62656.svg b/code/static/assets/Azure-d0a62656.svg similarity index 100% rename from code/app/static/assets/Azure-d0a62656.svg rename to code/static/assets/Azure-d0a62656.svg diff --git a/code/app/static/assets/Send-a9ab2367.svg b/code/static/assets/Send-a9ab2367.svg similarity index 100% rename from code/app/static/assets/Send-a9ab2367.svg rename to code/static/assets/Send-a9ab2367.svg diff --git a/code/static/assets/index-5e1f6cf7.js b/code/static/assets/index-5e1f6cf7.js new file mode 100644 index 000000000..3ee159583 --- /dev/null +++ b/code/static/assets/index-5e1f6cf7.js @@ -0,0 +1,890 @@ +var l3=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var BZ=l3((hr,pr)=>{function u3(e,t){for(var n=0;nr[i]})}}}return Object.freeze(Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}))}(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))r(i);new MutationObserver(i=>{for(const o of i)if(o.type==="childList")for(const a of o.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&r(a)}).observe(document,{childList:!0,subtree:!0});function n(i){const o={};return i.integrity&&(o.integrity=i.integrity),i.referrerPolicy&&(o.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?o.credentials="include":i.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function r(i){if(i.ep)return;i.ep=!0;const o=n(i);fetch(i.href,o)}})();function Ea(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var q4={exports:{}},fh={},Y4={exports:{}},ke={};/** + * @license React + * react.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var J1=Symbol.for("react.element"),c3=Symbol.for("react.portal"),f3=Symbol.for("react.fragment"),d3=Symbol.for("react.strict_mode"),h3=Symbol.for("react.profiler"),p3=Symbol.for("react.provider"),m3=Symbol.for("react.context"),g3=Symbol.for("react.forward_ref"),v3=Symbol.for("react.suspense"),E3=Symbol.for("react.memo"),y3=Symbol.for("react.lazy"),gE=Symbol.iterator;function T3(e){return e===null||typeof e!="object"?null:(e=gE&&e[gE]||e["@@iterator"],typeof e=="function"?e:null)}var Q4={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},X4=Object.assign,Z4={};function jl(e,t,n){this.props=e,this.context=t,this.refs=Z4,this.updater=n||Q4}jl.prototype.isReactComponent={};jl.prototype.setState=function(e,t){if(typeof e!="object"&&typeof e!="function"&&e!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,e,t,"setState")};jl.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this,e,"forceUpdate")};function J4(){}J4.prototype=jl.prototype;function Xg(e,t,n){this.props=e,this.context=t,this.refs=Z4,this.updater=n||Q4}var Zg=Xg.prototype=new J4;Zg.constructor=Xg;X4(Zg,jl.prototype);Zg.isPureReactComponent=!0;var vE=Array.isArray,eC=Object.prototype.hasOwnProperty,Jg={current:null},tC={key:!0,ref:!0,__self:!0,__source:!0};function nC(e,t,n){var r,i={},o=null,a=null;if(t!=null)for(r in t.ref!==void 0&&(a=t.ref),t.key!==void 0&&(o=""+t.key),t)eC.call(t,r)&&!tC.hasOwnProperty(r)&&(i[r]=t[r]);var s=arguments.length-2;if(s===1)i.children=n;else if(1>>1,X=H[ee];if(0>>1;eei(xt,_))pei(it,xt)?(H[ee]=it,H[pe]=_,ee=pe):(H[ee]=xt,H[ge]=_,ee=ge);else if(pei(it,_))H[ee]=it,H[pe]=_,ee=pe;else break e}}return U}function i(H,U){var _=H.sortIndex-U.sortIndex;return _!==0?_:H.id-U.id}if(typeof performance=="object"&&typeof performance.now=="function"){var o=performance;e.unstable_now=function(){return o.now()}}else{var a=Date,s=a.now();e.unstable_now=function(){return a.now()-s}}var l=[],u=[],c=1,f=null,d=3,h=!1,v=!1,g=!1,C=typeof setTimeout=="function"?setTimeout:null,m=typeof clearTimeout=="function"?clearTimeout:null,y=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function T(H){for(var U=n(u);U!==null;){if(U.callback===null)r(u);else if(U.startTime<=H)r(u),U.sortIndex=U.expirationTime,t(l,U);else break;U=n(u)}}function S(H){if(g=!1,T(H),!v)if(n(l)!==null)v=!0,z(w);else{var U=n(u);U!==null&&Z(S,U.startTime-H)}}function w(H,U){v=!1,g&&(g=!1,m(F),F=-1),h=!0;var _=d;try{for(T(U),f=n(l);f!==null&&(!(f.expirationTime>U)||H&&!B());){var ee=f.callback;if(typeof ee=="function"){f.callback=null,d=f.priorityLevel;var X=ee(f.expirationTime<=U);U=e.unstable_now(),typeof X=="function"?f.callback=X:f===n(l)&&r(l),T(U)}else r(l);f=n(l)}if(f!==null)var x=!0;else{var ge=n(u);ge!==null&&Z(S,ge.startTime-U),x=!1}return x}finally{f=null,d=_,h=!1}}var b=!1,A=null,F=-1,D=5,I=-1;function B(){return!(e.unstable_now()-IH||125ee?(H.sortIndex=_,t(u,H),n(l)===null&&H===n(u)&&(g?(m(F),F=-1):g=!0,Z(S,_-ee))):(H.sortIndex=X,t(l,H),v||h||(v=!0,z(w))),H},e.unstable_shouldYield=B,e.unstable_wrapCallback=function(H){var U=d;return function(){var _=d;d=U;try{return H.apply(this,arguments)}finally{d=_}}}})(aC);oC.exports=aC;var F3=oC.exports;/** + * @license React + * react-dom.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var sC=E,vr=F3;function K(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),o0=Object.prototype.hasOwnProperty,R3=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,yE={},TE={};function D3(e){return o0.call(TE,e)?!0:o0.call(yE,e)?!1:R3.test(e)?TE[e]=!0:(yE[e]=!0,!1)}function M3(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function P3(e,t,n,r){if(t===null||typeof t>"u"||M3(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function Fn(e,t,n,r,i,o,a){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=i,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=o,this.removeEmptyString=a}var tn={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){tn[e]=new Fn(e,0,!1,e,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];tn[t]=new Fn(t,1,!1,e[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(e){tn[e]=new Fn(e,2,!1,e.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){tn[e]=new Fn(e,2,!1,e,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){tn[e]=new Fn(e,3,!1,e.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(e){tn[e]=new Fn(e,3,!0,e,null,!1,!1)});["capture","download"].forEach(function(e){tn[e]=new Fn(e,4,!1,e,null,!1,!1)});["cols","rows","size","span"].forEach(function(e){tn[e]=new Fn(e,6,!1,e,null,!1,!1)});["rowSpan","start"].forEach(function(e){tn[e]=new Fn(e,5,!1,e.toLowerCase(),null,!1,!1)});var nv=/[\-:]([a-z])/g;function rv(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(nv,rv);tn[t]=new Fn(t,1,!1,e,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(nv,rv);tn[t]=new Fn(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(nv,rv);tn[t]=new Fn(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(e){tn[e]=new Fn(e,1,!1,e.toLowerCase(),null,!1,!1)});tn.xlinkHref=new Fn("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(e){tn[e]=new Fn(e,1,!1,e.toLowerCase(),null,!0,!0)});function iv(e,t,n,r){var i=tn.hasOwnProperty(t)?tn[t]:null;(i!==null?i.type!==0:r||!(2s||i[a]!==o[s]){var l=` +`+i[a].replace(" at new "," at ");return e.displayName&&l.includes("")&&(l=l.replace("",e.displayName)),l}while(1<=a&&0<=s);break}}}finally{yp=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?Ru(e):""}function O3(e){switch(e.tag){case 5:return Ru(e.type);case 16:return Ru("Lazy");case 13:return Ru("Suspense");case 19:return Ru("SuspenseList");case 0:case 2:case 15:return e=Tp(e.type,!1),e;case 11:return e=Tp(e.type.render,!1),e;case 1:return e=Tp(e.type,!0),e;default:return""}}function u0(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Ys:return"Fragment";case qs:return"Portal";case a0:return"Profiler";case ov:return"StrictMode";case s0:return"Suspense";case l0:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case cC:return(e.displayName||"Context")+".Consumer";case uC:return(e._context.displayName||"Context")+".Provider";case av:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case sv:return t=e.displayName||null,t!==null?t:u0(e.type)||"Memo";case Wo:t=e._payload,e=e._init;try{return u0(e(t))}catch{}}return null}function L3(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return u0(t);case 8:return t===ov?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function da(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function dC(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function B3(e){var t=dC(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var i=n.get,o=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return i.call(this)},set:function(a){r=""+a,o.call(this,a)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(a){r=""+a},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function Uc(e){e._valueTracker||(e._valueTracker=B3(e))}function hC(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=dC(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function md(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function c0(e,t){var n=t.checked;return dt({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function SE(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=da(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function pC(e,t){t=t.checked,t!=null&&iv(e,"checked",t,!1)}function f0(e,t){pC(e,t);var n=da(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?d0(e,t.type,n):t.hasOwnProperty("defaultValue")&&d0(e,t.type,da(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function _E(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function d0(e,t,n){(t!=="number"||md(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Du=Array.isArray;function ml(e,t,n,r){if(e=e.options,t){t={};for(var i=0;i"+t.valueOf().toString()+"",t=Kc.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function h1(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var zu={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},H3=["Webkit","ms","Moz","O"];Object.keys(zu).forEach(function(e){H3.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),zu[t]=zu[e]})});function EC(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||zu.hasOwnProperty(e)&&zu[e]?(""+t).trim():t+"px"}function yC(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,i=EC(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,i):e[n]=i}}var z3=dt({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function m0(e,t){if(t){if(z3[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(K(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(K(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(K(61))}if(t.style!=null&&typeof t.style!="object")throw Error(K(62))}}function g0(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var v0=null;function lv(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var E0=null,gl=null,vl=null;function wE(e){if(e=nc(e)){if(typeof E0!="function")throw Error(K(280));var t=e.stateNode;t&&(t=gh(t),E0(e.stateNode,e.type,t))}}function TC(e){gl?vl?vl.push(e):vl=[e]:gl=e}function CC(){if(gl){var e=gl,t=vl;if(vl=gl=null,wE(e),t)for(e=0;e>>=0,e===0?32:31-(X3(e)/Z3|0)|0}var Wc=64,jc=4194304;function Mu(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function yd(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,i=e.suspendedLanes,o=e.pingedLanes,a=n&268435455;if(a!==0){var s=a&~i;s!==0?r=Mu(s):(o&=a,o!==0&&(r=Mu(o)))}else a=n&~i,a!==0?r=Mu(a):o!==0&&(r=Mu(o));if(r===0)return 0;if(t!==0&&t!==r&&!(t&i)&&(i=r&-r,o=t&-t,i>=o||i===16&&(o&4194240)!==0))return t;if(r&4&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function ec(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-ni(t),e[t]=n}function n7(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=Ku),PE=String.fromCharCode(32),OE=!1;function UC(e,t){switch(e){case"keyup":return N7.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function KC(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var Qs=!1;function R7(e,t){switch(e){case"compositionend":return KC(t);case"keypress":return t.which!==32?null:(OE=!0,PE);case"textInput":return e=t.data,e===PE&&OE?null:e;default:return null}}function D7(e,t){if(Qs)return e==="compositionend"||!gv&&UC(e,t)?(e=HC(),Gf=hv=Xo=null,Qs=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=zE(n)}}function $C(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?$C(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function VC(){for(var e=window,t=md();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=md(e.document)}return t}function vv(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function K7(e){var t=VC(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&$C(n.ownerDocument.documentElement,n)){if(r!==null&&vv(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var i=n.textContent.length,o=Math.min(r.start,i);r=r.end===void 0?o:Math.min(r.end,i),!e.extend&&o>r&&(i=r,r=o,o=i),i=UE(n,o);var a=UE(n,r);i&&a&&(e.rangeCount!==1||e.anchorNode!==i.node||e.anchorOffset!==i.offset||e.focusNode!==a.node||e.focusOffset!==a.offset)&&(t=t.createRange(),t.setStart(i.node,i.offset),e.removeAllRanges(),o>r?(e.addRange(t),e.extend(a.node,a.offset)):(t.setEnd(a.node,a.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,Xs=null,b0=null,ju=null,A0=!1;function KE(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;A0||Xs==null||Xs!==md(r)||(r=Xs,"selectionStart"in r&&vv(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),ju&&y1(ju,r)||(ju=r,r=Sd(b0,"onSelect"),0el||(e.current=F0[el],F0[el]=null,el--)}function Ve(e,t){el++,F0[el]=e.current,e.current=t}var ha={},hn=Ta(ha),Qn=Ta(!1),os=ha;function kl(e,t){var n=e.type.contextTypes;if(!n)return ha;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var i={},o;for(o in n)i[o]=t[o];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=i),i}function Xn(e){return e=e.childContextTypes,e!=null}function bd(){nt(Qn),nt(hn)}function YE(e,t,n){if(hn.current!==ha)throw Error(K(168));Ve(hn,t),Ve(Qn,n)}function nS(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var i in r)if(!(i in t))throw Error(K(108,L3(e)||"Unknown",i));return dt({},n,r)}function Ad(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||ha,os=hn.current,Ve(hn,e),Ve(Qn,Qn.current),!0}function QE(e,t,n){var r=e.stateNode;if(!r)throw Error(K(169));n?(e=nS(e,t,os),r.__reactInternalMemoizedMergedChildContext=e,nt(Qn),nt(hn),Ve(hn,e)):nt(Qn),Ve(Qn,n)}var to=null,vh=!1,Mp=!1;function rS(e){to===null?to=[e]:to.push(e)}function ew(e){vh=!0,rS(e)}function Ca(){if(!Mp&&to!==null){Mp=!0;var e=0,t=Pe;try{var n=to;for(Pe=1;e>=a,i-=a,no=1<<32-ni(t)+i|n<F?(D=A,A=null):D=A.sibling;var I=d(m,A,T[F],S);if(I===null){A===null&&(A=D);break}e&&A&&I.alternate===null&&t(m,A),y=o(I,y,F),b===null?w=I:b.sibling=I,b=I,A=D}if(F===T.length)return n(m,A),at&&za(m,F),w;if(A===null){for(;FF?(D=A,A=null):D=A.sibling;var B=d(m,A,I.value,S);if(B===null){A===null&&(A=D);break}e&&A&&B.alternate===null&&t(m,A),y=o(B,y,F),b===null?w=B:b.sibling=B,b=B,A=D}if(I.done)return n(m,A),at&&za(m,F),w;if(A===null){for(;!I.done;F++,I=T.next())I=f(m,I.value,S),I!==null&&(y=o(I,y,F),b===null?w=I:b.sibling=I,b=I);return at&&za(m,F),w}for(A=r(m,A);!I.done;F++,I=T.next())I=h(A,m,F,I.value,S),I!==null&&(e&&I.alternate!==null&&A.delete(I.key===null?F:I.key),y=o(I,y,F),b===null?w=I:b.sibling=I,b=I);return e&&A.forEach(function(M){return t(m,M)}),at&&za(m,F),w}function C(m,y,T,S){if(typeof T=="object"&&T!==null&&T.type===Ys&&T.key===null&&(T=T.props.children),typeof T=="object"&&T!==null){switch(T.$$typeof){case zc:e:{for(var w=T.key,b=y;b!==null;){if(b.key===w){if(w=T.type,w===Ys){if(b.tag===7){n(m,b.sibling),y=i(b,T.props.children),y.return=m,m=y;break e}}else if(b.elementType===w||typeof w=="object"&&w!==null&&w.$$typeof===Wo&&r5(w)===b.type){n(m,b.sibling),y=i(b,T.props),y.ref=pu(m,b,T),y.return=m,m=y;break e}n(m,b);break}else t(m,b);b=b.sibling}T.type===Ys?(y=ns(T.props.children,m.mode,S,T.key),y.return=m,m=y):(S=Jf(T.type,T.key,T.props,null,m.mode,S),S.ref=pu(m,y,T),S.return=m,m=S)}return a(m);case qs:e:{for(b=T.key;y!==null;){if(y.key===b)if(y.tag===4&&y.stateNode.containerInfo===T.containerInfo&&y.stateNode.implementation===T.implementation){n(m,y.sibling),y=i(y,T.children||[]),y.return=m,m=y;break e}else{n(m,y);break}else t(m,y);y=y.sibling}y=Kp(T,m.mode,S),y.return=m,m=y}return a(m);case Wo:return b=T._init,C(m,y,b(T._payload),S)}if(Du(T))return v(m,y,T,S);if(uu(T))return g(m,y,T,S);Xc(m,T)}return typeof T=="string"&&T!==""||typeof T=="number"?(T=""+T,y!==null&&y.tag===6?(n(m,y.sibling),y=i(y,T),y.return=m,m=y):(n(m,y),y=Up(T,m.mode,S),y.return=m,m=y),a(m)):n(m,y)}return C}var xl=fS(!0),dS=fS(!1),rc={},Li=Ta(rc),_1=Ta(rc),b1=Ta(rc);function Ya(e){if(e===rc)throw Error(K(174));return e}function wv(e,t){switch(Ve(b1,t),Ve(_1,e),Ve(Li,rc),e=t.nodeType,e){case 9:case 11:t=(t=t.documentElement)?t.namespaceURI:p0(null,"");break;default:e=e===8?t.parentNode:t,t=e.namespaceURI||null,e=e.tagName,t=p0(t,e)}nt(Li),Ve(Li,t)}function Nl(){nt(Li),nt(_1),nt(b1)}function hS(e){Ya(b1.current);var t=Ya(Li.current),n=p0(t,e.type);t!==n&&(Ve(_1,e),Ve(Li,n))}function kv(e){_1.current===e&&(nt(Li),nt(_1))}var ct=Ta(0);function Fd(e){for(var t=e;t!==null;){if(t.tag===13){var n=t.memoizedState;if(n!==null&&(n=n.dehydrated,n===null||n.data==="$?"||n.data==="$!"))return t}else if(t.tag===19&&t.memoizedProps.revealOrder!==void 0){if(t.flags&128)return t}else if(t.child!==null){t.child.return=t,t=t.child;continue}if(t===e)break;for(;t.sibling===null;){if(t.return===null||t.return===e)return null;t=t.return}t.sibling.return=t.return,t=t.sibling}return null}var Pp=[];function Iv(){for(var e=0;en?n:4,e(!0);var r=Op.transition;Op.transition={};try{e(!1),t()}finally{Pe=n,Op.transition=r}}function xS(){return Mr().memoizedState}function iw(e,t,n){var r=la(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},NS(e))FS(t,n);else if(n=sS(e,t,n,r),n!==null){var i=kn();ri(n,e,r,i),RS(n,t,r)}}function ow(e,t,n){var r=la(e),i={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(NS(e))FS(t,i);else{var o=e.alternate;if(e.lanes===0&&(o===null||o.lanes===0)&&(o=t.lastRenderedReducer,o!==null))try{var a=t.lastRenderedState,s=o(a,n);if(i.hasEagerState=!0,i.eagerState=s,ai(s,a)){var l=t.interleaved;l===null?(i.next=i,bv(t)):(i.next=l.next,l.next=i),t.interleaved=i;return}}catch{}finally{}n=sS(e,t,i,r),n!==null&&(i=kn(),ri(n,e,r,i),RS(n,t,r))}}function NS(e){var t=e.alternate;return e===ft||t!==null&&t===ft}function FS(e,t){Gu=Rd=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function RS(e,t,n){if(n&4194240){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,cv(e,n)}}var Dd={readContext:Dr,useCallback:nn,useContext:nn,useEffect:nn,useImperativeHandle:nn,useInsertionEffect:nn,useLayoutEffect:nn,useMemo:nn,useReducer:nn,useRef:nn,useState:nn,useDebugValue:nn,useDeferredValue:nn,useTransition:nn,useMutableSource:nn,useSyncExternalStore:nn,useId:nn,unstable_isNewReconciler:!1},aw={readContext:Dr,useCallback:function(e,t){return Ti().memoizedState=[e,t===void 0?null:t],e},useContext:Dr,useEffect:o5,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Yf(4194308,4,bS.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Yf(4194308,4,e,t)},useInsertionEffect:function(e,t){return Yf(4,2,e,t)},useMemo:function(e,t){var n=Ti();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Ti();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=iw.bind(null,ft,e),[r.memoizedState,e]},useRef:function(e){var t=Ti();return e={current:e},t.memoizedState=e},useState:i5,useDebugValue:Dv,useDeferredValue:function(e){return Ti().memoizedState=e},useTransition:function(){var e=i5(!1),t=e[0];return e=rw.bind(null,e[1]),Ti().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=ft,i=Ti();if(at){if(n===void 0)throw Error(K(407));n=n()}else{if(n=t(),$t===null)throw Error(K(349));ss&30||gS(r,t,n)}i.memoizedState=n;var o={value:n,getSnapshot:t};return i.queue=o,o5(ES.bind(null,r,o,e),[e]),r.flags|=2048,k1(9,vS.bind(null,r,o,n,t),void 0,null),n},useId:function(){var e=Ti(),t=$t.identifierPrefix;if(at){var n=ro,r=no;n=(r&~(1<<32-ni(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=A1++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=a.createElement(n,{is:r.is}):(e=a.createElement(n),n==="select"&&(a=e,r.multiple?a.multiple=!0:r.size&&(a.size=r.size))):e=a.createElementNS(e,n),e[ki]=t,e[S1]=r,US(e,t,!1,!1),t.stateNode=e;e:{switch(a=g0(n,r),n){case"dialog":Xe("cancel",e),Xe("close",e),i=r;break;case"iframe":case"object":case"embed":Xe("load",e),i=r;break;case"video":case"audio":for(i=0;iRl&&(t.flags|=128,r=!0,mu(o,!1),t.lanes=4194304)}else{if(!r)if(e=Fd(a),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),mu(o,!0),o.tail===null&&o.tailMode==="hidden"&&!a.alternate&&!at)return rn(t),null}else 2*bt()-o.renderingStartTime>Rl&&n!==1073741824&&(t.flags|=128,r=!0,mu(o,!1),t.lanes=4194304);o.isBackwards?(a.sibling=t.child,t.child=a):(n=o.last,n!==null?n.sibling=a:t.child=a,o.last=a)}return o.tail!==null?(t=o.tail,o.rendering=t,o.tail=t.sibling,o.renderingStartTime=bt(),t.sibling=null,n=ct.current,Ve(ct,r?n&1|2:n&1),t):(rn(t),null);case 22:case 23:return Hv(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&t.mode&1?ur&1073741824&&(rn(t),t.subtreeFlags&6&&(t.flags|=8192)):rn(t),null;case 24:return null;case 25:return null}throw Error(K(156,t.tag))}function pw(e,t){switch(yv(t),t.tag){case 1:return Xn(t.type)&&bd(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Nl(),nt(Qn),nt(hn),Iv(),e=t.flags,e&65536&&!(e&128)?(t.flags=e&-65537|128,t):null;case 5:return kv(t),null;case 13:if(nt(ct),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(K(340));Il()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return nt(ct),null;case 4:return Nl(),null;case 10:return _v(t.type._context),null;case 22:case 23:return Hv(),null;case 24:return null;default:return null}}var Jc=!1,cn=!1,mw=typeof WeakSet=="function"?WeakSet:Set,ne=null;function il(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){vt(e,t,r)}else n.current=null}function W0(e,t,n){try{n()}catch(r){vt(e,t,r)}}var p5=!1;function gw(e,t){if(w0=Td,e=VC(),vv(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var i=r.anchorOffset,o=r.focusNode;r=r.focusOffset;try{n.nodeType,o.nodeType}catch{n=null;break e}var a=0,s=-1,l=-1,u=0,c=0,f=e,d=null;t:for(;;){for(var h;f!==n||i!==0&&f.nodeType!==3||(s=a+i),f!==o||r!==0&&f.nodeType!==3||(l=a+r),f.nodeType===3&&(a+=f.nodeValue.length),(h=f.firstChild)!==null;)d=f,f=h;for(;;){if(f===e)break t;if(d===n&&++u===i&&(s=a),d===o&&++c===r&&(l=a),(h=f.nextSibling)!==null)break;f=d,d=f.parentNode}f=h}n=s===-1||l===-1?null:{start:s,end:l}}else n=null}n=n||{start:0,end:0}}else n=null;for(k0={focusedElem:e,selectionRange:n},Td=!1,ne=t;ne!==null;)if(t=ne,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,ne=e;else for(;ne!==null;){t=ne;try{var v=t.alternate;if(t.flags&1024)switch(t.tag){case 0:case 11:case 15:break;case 1:if(v!==null){var g=v.memoizedProps,C=v.memoizedState,m=t.stateNode,y=m.getSnapshotBeforeUpdate(t.elementType===t.type?g:qr(t.type,g),C);m.__reactInternalSnapshotBeforeUpdate=y}break;case 3:var T=t.stateNode.containerInfo;T.nodeType===1?T.textContent="":T.nodeType===9&&T.documentElement&&T.removeChild(T.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(K(163))}}catch(S){vt(t,t.return,S)}if(e=t.sibling,e!==null){e.return=t.return,ne=e;break}ne=t.return}return v=p5,p5=!1,v}function $u(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var i=r=r.next;do{if((i.tag&e)===e){var o=i.destroy;i.destroy=void 0,o!==void 0&&W0(t,n,o)}i=i.next}while(i!==r)}}function Th(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function j0(e){var t=e.ref;if(t!==null){var n=e.stateNode;switch(e.tag){case 5:e=n;break;default:e=n}typeof t=="function"?t(e):t.current=e}}function jS(e){var t=e.alternate;t!==null&&(e.alternate=null,jS(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[ki],delete t[S1],delete t[N0],delete t[Z7],delete t[J7])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function GS(e){return e.tag===5||e.tag===3||e.tag===4}function m5(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||GS(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function G0(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=_d));else if(r!==4&&(e=e.child,e!==null))for(G0(e,t,n),e=e.sibling;e!==null;)G0(e,t,n),e=e.sibling}function $0(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for($0(e,t,n),e=e.sibling;e!==null;)$0(e,t,n),e=e.sibling}var Xt=null,Yr=!1;function Ro(e,t,n){for(n=n.child;n!==null;)$S(e,t,n),n=n.sibling}function $S(e,t,n){if(Oi&&typeof Oi.onCommitFiberUnmount=="function")try{Oi.onCommitFiberUnmount(dh,n)}catch{}switch(n.tag){case 5:cn||il(n,t);case 6:var r=Xt,i=Yr;Xt=null,Ro(e,t,n),Xt=r,Yr=i,Xt!==null&&(Yr?(e=Xt,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Xt.removeChild(n.stateNode));break;case 18:Xt!==null&&(Yr?(e=Xt,n=n.stateNode,e.nodeType===8?Dp(e.parentNode,n):e.nodeType===1&&Dp(e,n),v1(e)):Dp(Xt,n.stateNode));break;case 4:r=Xt,i=Yr,Xt=n.stateNode.containerInfo,Yr=!0,Ro(e,t,n),Xt=r,Yr=i;break;case 0:case 11:case 14:case 15:if(!cn&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){i=r=r.next;do{var o=i,a=o.destroy;o=o.tag,a!==void 0&&(o&2||o&4)&&W0(n,t,a),i=i.next}while(i!==r)}Ro(e,t,n);break;case 1:if(!cn&&(il(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(s){vt(n,t,s)}Ro(e,t,n);break;case 21:Ro(e,t,n);break;case 22:n.mode&1?(cn=(r=cn)||n.memoizedState!==null,Ro(e,t,n),cn=r):Ro(e,t,n);break;default:Ro(e,t,n)}}function g5(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new mw),t.forEach(function(r){var i=Aw.bind(null,e,r);n.has(r)||(n.add(r),r.then(i,i))})}}function Kr(e,t){var n=t.deletions;if(n!==null)for(var r=0;ri&&(i=a),r&=~o}if(r=i,r=bt()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*Ew(r/1960))-r,10e?16:e,Zo===null)var r=!1;else{if(e=Zo,Zo=null,Od=0,Ne&6)throw Error(K(331));var i=Ne;for(Ne|=4,ne=e.current;ne!==null;){var o=ne,a=o.child;if(ne.flags&16){var s=o.deletions;if(s!==null){for(var l=0;lbt()-Lv?ts(e,0):Ov|=n),Zn(e,t)}function e8(e,t){t===0&&(e.mode&1?(t=jc,jc<<=1,!(jc&130023424)&&(jc=4194304)):t=1);var n=kn();e=uo(e,t),e!==null&&(ec(e,t,n),Zn(e,n))}function bw(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),e8(e,n)}function Aw(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,i=e.memoizedState;i!==null&&(n=i.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(K(314))}r!==null&&r.delete(t),e8(e,n)}var t8;t8=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||Qn.current)Vn=!0;else{if(!(e.lanes&n)&&!(t.flags&128))return Vn=!1,dw(e,t,n);Vn=!!(e.flags&131072)}else Vn=!1,at&&t.flags&1048576&&iS(t,kd,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Qf(e,t),e=t.pendingProps;var i=kl(t,hn.current);yl(t,n),i=Nv(null,t,r,e,i,n);var o=Fv();return t.flags|=1,typeof i=="object"&&i!==null&&typeof i.render=="function"&&i.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Xn(r)?(o=!0,Ad(t)):o=!1,t.memoizedState=i.state!==null&&i.state!==void 0?i.state:null,Av(t),i.updater=Eh,t.stateNode=i,i._reactInternals=t,O0(t,r,e,n),t=H0(null,t,r,!0,o,n)):(t.tag=0,at&&o&&Ev(t),Tn(null,t,i,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Qf(e,t),e=t.pendingProps,i=r._init,r=i(r._payload),t.type=r,i=t.tag=kw(r),e=qr(r,e),i){case 0:t=B0(null,t,r,e,n);break e;case 1:t=f5(null,t,r,e,n);break e;case 11:t=u5(null,t,r,e,n);break e;case 14:t=c5(null,t,r,qr(r.type,e),n);break e}throw Error(K(306,r,""))}return t;case 0:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:qr(r,i),B0(e,t,r,i,n);case 1:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:qr(r,i),f5(e,t,r,i,n);case 3:e:{if(BS(t),e===null)throw Error(K(387));r=t.pendingProps,o=t.memoizedState,i=o.element,lS(e,t),Nd(t,r,null,n);var a=t.memoizedState;if(r=a.element,o.isDehydrated)if(o={element:r,isDehydrated:!1,cache:a.cache,pendingSuspenseBoundaries:a.pendingSuspenseBoundaries,transitions:a.transitions},t.updateQueue.baseState=o,t.memoizedState=o,t.flags&256){i=Fl(Error(K(423)),t),t=d5(e,t,r,n,i);break e}else if(r!==i){i=Fl(Error(K(424)),t),t=d5(e,t,r,n,i);break e}else for(cr=oa(t.stateNode.containerInfo.firstChild),mr=t,at=!0,Qr=null,n=dS(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(Il(),r===i){t=co(e,t,n);break e}Tn(e,t,r,n)}t=t.child}return t;case 5:return hS(t),e===null&&D0(t),r=t.type,i=t.pendingProps,o=e!==null?e.memoizedProps:null,a=i.children,I0(r,i)?a=null:o!==null&&I0(r,o)&&(t.flags|=32),LS(e,t),Tn(e,t,a,n),t.child;case 6:return e===null&&D0(t),null;case 13:return HS(e,t,n);case 4:return wv(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=xl(t,null,r,n):Tn(e,t,r,n),t.child;case 11:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:qr(r,i),u5(e,t,r,i,n);case 7:return Tn(e,t,t.pendingProps,n),t.child;case 8:return Tn(e,t,t.pendingProps.children,n),t.child;case 12:return Tn(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,i=t.pendingProps,o=t.memoizedProps,a=i.value,Ve(Id,r._currentValue),r._currentValue=a,o!==null)if(ai(o.value,a)){if(o.children===i.children&&!Qn.current){t=co(e,t,n);break e}}else for(o=t.child,o!==null&&(o.return=t);o!==null;){var s=o.dependencies;if(s!==null){a=o.child;for(var l=s.firstContext;l!==null;){if(l.context===r){if(o.tag===1){l=ao(-1,n&-n),l.tag=2;var u=o.updateQueue;if(u!==null){u=u.shared;var c=u.pending;c===null?l.next=l:(l.next=c.next,c.next=l),u.pending=l}}o.lanes|=n,l=o.alternate,l!==null&&(l.lanes|=n),M0(o.return,n,t),s.lanes|=n;break}l=l.next}}else if(o.tag===10)a=o.type===t.type?null:o.child;else if(o.tag===18){if(a=o.return,a===null)throw Error(K(341));a.lanes|=n,s=a.alternate,s!==null&&(s.lanes|=n),M0(a,n,t),a=o.sibling}else a=o.child;if(a!==null)a.return=o;else for(a=o;a!==null;){if(a===t){a=null;break}if(o=a.sibling,o!==null){o.return=a.return,a=o;break}a=a.return}o=a}Tn(e,t,i.children,n),t=t.child}return t;case 9:return i=t.type,r=t.pendingProps.children,yl(t,n),i=Dr(i),r=r(i),t.flags|=1,Tn(e,t,r,n),t.child;case 14:return r=t.type,i=qr(r,t.pendingProps),i=qr(r.type,i),c5(e,t,r,i,n);case 15:return PS(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,i=t.pendingProps,i=t.elementType===r?i:qr(r,i),Qf(e,t),t.tag=1,Xn(r)?(e=!0,Ad(t)):e=!1,yl(t,n),cS(t,r,i),O0(t,r,i,n),H0(null,t,r,!0,e,n);case 19:return zS(e,t,n);case 22:return OS(e,t,n)}throw Error(K(156,t.tag))};function n8(e,t){return IC(e,t)}function ww(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Ir(e,t,n,r){return new ww(e,t,n,r)}function Uv(e){return e=e.prototype,!(!e||!e.isReactComponent)}function kw(e){if(typeof e=="function")return Uv(e)?1:0;if(e!=null){if(e=e.$$typeof,e===av)return 11;if(e===sv)return 14}return 2}function ua(e,t){var n=e.alternate;return n===null?(n=Ir(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function Jf(e,t,n,r,i,o){var a=2;if(r=e,typeof e=="function")Uv(e)&&(a=1);else if(typeof e=="string")a=5;else e:switch(e){case Ys:return ns(n.children,i,o,t);case ov:a=8,i|=8;break;case a0:return e=Ir(12,n,t,i|2),e.elementType=a0,e.lanes=o,e;case s0:return e=Ir(13,n,t,i),e.elementType=s0,e.lanes=o,e;case l0:return e=Ir(19,n,t,i),e.elementType=l0,e.lanes=o,e;case fC:return Sh(n,i,o,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case uC:a=10;break e;case cC:a=9;break e;case av:a=11;break e;case sv:a=14;break e;case Wo:a=16,r=null;break e}throw Error(K(130,e==null?e:typeof e,""))}return t=Ir(a,n,t,i),t.elementType=e,t.type=r,t.lanes=o,t}function ns(e,t,n,r){return e=Ir(7,e,r,t),e.lanes=n,e}function Sh(e,t,n,r){return e=Ir(22,e,r,t),e.elementType=fC,e.lanes=n,e.stateNode={isHidden:!1},e}function Up(e,t,n){return e=Ir(6,e,null,t),e.lanes=n,e}function Kp(e,t,n){return t=Ir(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Iw(e,t,n,r,i){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=Sp(0),this.expirationTimes=Sp(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=Sp(0),this.identifierPrefix=r,this.onRecoverableError=i,this.mutableSourceEagerHydrationData=null}function Kv(e,t,n,r,i,o,a,s,l){return e=new Iw(e,t,n,s,l),t===1?(t=1,o===!0&&(t|=8)):t=0,o=Ir(3,null,null,t),e.current=o,o.stateNode=e,o.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Av(o),e}function xw(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(a8)}catch(e){console.error(e)}}a8(),iC.exports=Er;var s8=iC.exports,b5=s8;i0.createRoot=b5.createRoot,i0.hydrateRoot=b5.hydrateRoot;/** + * @remix-run/router v1.6.3 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function x1(){return x1=Object.assign?Object.assign.bind():function(e){for(var t=1;t"u")throw new Error(t)}function kh(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Pw(){return Math.random().toString(36).substr(2,8)}function w5(e,t){return{usr:e.state,key:e.key,idx:t}}function X0(e,t,n,r){return n===void 0&&(n=null),x1({pathname:typeof e=="string"?e:e.pathname,search:"",hash:""},typeof t=="string"?Ts(t):t,{state:n,key:t&&t.key||r||Pw()})}function Hd(e){let{pathname:t="/",search:n="",hash:r=""}=e;return n&&n!=="?"&&(t+=n.charAt(0)==="?"?n:"?"+n),r&&r!=="#"&&(t+=r.charAt(0)==="#"?r:"#"+r),t}function Ts(e){let t={};if(e){let n=e.indexOf("#");n>=0&&(t.hash=e.substr(n),e=e.substr(0,n));let r=e.indexOf("?");r>=0&&(t.search=e.substr(r),e=e.substr(0,r)),e&&(t.pathname=e)}return t}function Ow(e,t,n,r){r===void 0&&(r={});let{window:i=document.defaultView,v5Compat:o=!1}=r,a=i.history,s=Jo.Pop,l=null,u=c();u==null&&(u=0,a.replaceState(x1({},a.state,{idx:u}),""));function c(){return(a.state||{idx:null}).idx}function f(){s=Jo.Pop;let C=c(),m=C==null?null:C-u;u=C,l&&l({action:s,location:g.location,delta:m})}function d(C,m){s=Jo.Push;let y=X0(g.location,C,m);n&&n(y,C),u=c()+1;let T=w5(y,u),S=g.createHref(y);try{a.pushState(T,"",S)}catch(w){if(w instanceof DOMException&&w.name==="DataCloneError")throw w;i.location.assign(S)}o&&l&&l({action:s,location:g.location,delta:1})}function h(C,m){s=Jo.Replace;let y=X0(g.location,C,m);n&&n(y,C),u=c();let T=w5(y,u),S=g.createHref(y);a.replaceState(T,"",S),o&&l&&l({action:s,location:g.location,delta:0})}function v(C){let m=i.location.origin!=="null"?i.location.origin:i.location.href,y=typeof C=="string"?C:Hd(C);return kt(m,"No window.location.(origin|href) available to create URL for href: "+y),new URL(y,m)}let g={get action(){return s},get location(){return e(i,a)},listen(C){if(l)throw new Error("A history only accepts one active listener");return i.addEventListener(A5,f),l=C,()=>{i.removeEventListener(A5,f),l=null}},createHref(C){return t(i,C)},createURL:v,encodeLocation(C){let m=v(C);return{pathname:m.pathname,search:m.search,hash:m.hash}},push:d,replace:h,go(C){return a.go(C)}};return g}var k5;(function(e){e.data="data",e.deferred="deferred",e.redirect="redirect",e.error="error"})(k5||(k5={}));function Lw(e,t,n){n===void 0&&(n="/");let r=typeof t=="string"?Ts(t):t,i=$v(r.pathname||"/",n);if(i==null)return null;let o=l8(e);Bw(o);let a=null;for(let s=0;a==null&&s{let l={relativePath:s===void 0?o.path||"":s,caseSensitive:o.caseSensitive===!0,childrenIndex:a,route:o};l.relativePath.startsWith("/")&&(kt(l.relativePath.startsWith(r),'Absolute route path "'+l.relativePath+'" nested under path '+('"'+r+'" is not valid. An absolute child route path ')+"must start with the combined path of all its parent routes."),l.relativePath=l.relativePath.slice(r.length));let u=ca([r,l.relativePath]),c=n.concat(l);o.children&&o.children.length>0&&(kt(o.index!==!0,"Index routes must not have child routes. Please remove "+('all child routes from route path "'+u+'".')),l8(o.children,t,c,u)),!(o.path==null&&!o.index)&&t.push({path:u,score:Gw(u,o.index),routesMeta:c})};return e.forEach((o,a)=>{var s;if(o.path===""||!((s=o.path)!=null&&s.includes("?")))i(o,a);else for(let l of u8(o.path))i(o,a,l)}),t}function u8(e){let t=e.split("/");if(t.length===0)return[];let[n,...r]=t,i=n.endsWith("?"),o=n.replace(/\?$/,"");if(r.length===0)return i?[o,""]:[o];let a=u8(r.join("/")),s=[];return s.push(...a.map(l=>l===""?o:[o,l].join("/"))),i&&s.push(...a),s.map(l=>e.startsWith("/")&&l===""?"/":l)}function Bw(e){e.sort((t,n)=>t.score!==n.score?n.score-t.score:$w(t.routesMeta.map(r=>r.childrenIndex),n.routesMeta.map(r=>r.childrenIndex)))}const Hw=/^:\w+$/,zw=3,Uw=2,Kw=1,Ww=10,jw=-2,I5=e=>e==="*";function Gw(e,t){let n=e.split("/"),r=n.length;return n.some(I5)&&(r+=jw),t&&(r+=Uw),n.filter(i=>!I5(i)).reduce((i,o)=>i+(Hw.test(o)?zw:o===""?Kw:Ww),r)}function $w(e,t){return e.length===t.length&&e.slice(0,-1).every((r,i)=>r===t[i])?e[e.length-1]-t[t.length-1]:0}function Vw(e,t){let{routesMeta:n}=e,r={},i="/",o=[];for(let a=0;a{if(c==="*"){let d=s[f]||"";a=o.slice(0,o.length-d.length).replace(/(.)\/+$/,"$1")}return u[c]=Xw(s[f]||"",c),u},{}),pathname:o,pathnameBase:a,pattern:e}}function Yw(e,t,n){t===void 0&&(t=!1),n===void 0&&(n=!0),kh(e==="*"||!e.endsWith("*")||e.endsWith("/*"),'Route path "'+e+'" will be treated as if it were '+('"'+e.replace(/\*$/,"/*")+'" because the `*` character must ')+"always follow a `/` in the pattern. To get rid of this warning, "+('please change the route path to "'+e.replace(/\*$/,"/*")+'".'));let r=[],i="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^$?{}|()[\]]/g,"\\$&").replace(/\/:(\w+)/g,(a,s)=>(r.push(s),"/([^\\/]+)"));return e.endsWith("*")?(r.push("*"),i+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):n?i+="\\/*$":e!==""&&e!=="/"&&(i+="(?:(?=\\/|$))"),[new RegExp(i,t?void 0:"i"),r]}function Qw(e){try{return decodeURI(e)}catch(t){return kh(!1,'The URL path "'+e+'" could not be decoded because it is is a malformed URL segment. This is probably due to a bad percent '+("encoding ("+t+").")),e}}function Xw(e,t){try{return decodeURIComponent(e)}catch(n){return kh(!1,'The value for the URL param "'+t+'" will not be decoded because'+(' the string "'+e+'" is a malformed URL segment. This is probably')+(" due to a bad percent encoding ("+n+").")),e}}function $v(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let n=t.endsWith("/")?t.length-1:t.length,r=e.charAt(n);return r&&r!=="/"?null:e.slice(n)||"/"}function Zw(e,t){t===void 0&&(t="/");let{pathname:n,search:r="",hash:i=""}=typeof e=="string"?Ts(e):e;return{pathname:n?n.startsWith("/")?n:Jw(n,t):t,search:tk(r),hash:nk(i)}}function Jw(e,t){let n=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(i=>{i===".."?n.length>1&&n.pop():i!=="."&&n.push(i)}),n.length>1?n.join("/"):"/"}function Wp(e,t,n,r){return"Cannot include a '"+e+"' character in a manually specified "+("`to."+t+"` field ["+JSON.stringify(r)+"]. Please separate it out to the ")+("`to."+n+"` field. Alternatively you may provide the full path as ")+'a string in and the router will parse it for you.'}function c8(e){return e.filter((t,n)=>n===0||t.route.path&&t.route.path.length>0)}function f8(e,t,n,r){r===void 0&&(r=!1);let i;typeof e=="string"?i=Ts(e):(i=x1({},e),kt(!i.pathname||!i.pathname.includes("?"),Wp("?","pathname","search",i)),kt(!i.pathname||!i.pathname.includes("#"),Wp("#","pathname","hash",i)),kt(!i.search||!i.search.includes("#"),Wp("#","search","hash",i)));let o=e===""||i.pathname==="",a=o?"/":i.pathname,s;if(r||a==null)s=n;else{let f=t.length-1;if(a.startsWith("..")){let d=a.split("/");for(;d[0]==="..";)d.shift(),f-=1;i.pathname=d.join("/")}s=f>=0?t[f]:"/"}let l=Zw(i,s),u=a&&a!=="/"&&a.endsWith("/"),c=(o||a===".")&&n.endsWith("/");return!l.pathname.endsWith("/")&&(u||c)&&(l.pathname+="/"),l}const ca=e=>e.join("/").replace(/\/\/+/g,"/"),ek=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),tk=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,nk=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e;function rk(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}const d8=["post","put","patch","delete"];new Set(d8);const ik=["get",...d8];new Set(ik);/** + * React Router v6.12.1 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function zd(){return zd=Object.assign?Object.assign.bind():function(e){for(var t=1;tl.pathnameBase)),a=E.useRef(!1);return p8(()=>{a.current=!0}),E.useCallback(function(l,u){if(u===void 0&&(u={}),!a.current)return;if(typeof l=="number"){n.go(l);return}let c=f8(l,JSON.parse(o),i,u.relative==="path");e==null&&t!=="/"&&(c.pathname=c.pathname==="/"?t:ca([t,c.pathname])),(u.replace?n.replace:n.push)(c,u.state,u)},[t,n,o,i,e])}const uk=E.createContext(null);function ck(e){let t=E.useContext(Sa).outlet;return t&&E.createElement(uk.Provider,{value:e},t)}function m8(e,t){let{relative:n}=t===void 0?{}:t,{matches:r}=E.useContext(Sa),{pathname:i}=xh(),o=JSON.stringify(c8(r).map(a=>a.pathnameBase));return E.useMemo(()=>f8(e,JSON.parse(o),i,n==="path"),[e,o,i,n])}function fk(e,t){return dk(e,t)}function dk(e,t,n){ic()||kt(!1);let{navigator:r}=E.useContext(Vl),{matches:i}=E.useContext(Sa),o=i[i.length-1],a=o?o.params:{};o&&o.pathname;let s=o?o.pathnameBase:"/";o&&o.route;let l=xh(),u;if(t){var c;let g=typeof t=="string"?Ts(t):t;s==="/"||(c=g.pathname)!=null&&c.startsWith(s)||kt(!1),u=g}else u=l;let f=u.pathname||"/",d=s==="/"?f:f.slice(s.length)||"/",h=Lw(e,{pathname:d}),v=vk(h&&h.map(g=>Object.assign({},g,{params:Object.assign({},a,g.params),pathname:ca([s,r.encodeLocation?r.encodeLocation(g.pathname).pathname:g.pathname]),pathnameBase:g.pathnameBase==="/"?s:ca([s,r.encodeLocation?r.encodeLocation(g.pathnameBase).pathname:g.pathnameBase])})),i,n);return t&&v?E.createElement(Ih.Provider,{value:{location:zd({pathname:"/",search:"",hash:"",state:null,key:"default"},u),navigationType:Jo.Pop}},v):v}function hk(){let e=Ck(),t=rk(e)?e.status+" "+e.statusText:e instanceof Error?e.message:JSON.stringify(e),n=e instanceof Error?e.stack:null,i={padding:"0.5rem",backgroundColor:"rgba(200,200,200, 0.5)"},o=null;return E.createElement(E.Fragment,null,E.createElement("h2",null,"Unexpected Application Error!"),E.createElement("h3",{style:{fontStyle:"italic"}},t),n?E.createElement("pre",{style:i},n):null,o)}const pk=E.createElement(hk,null);class mk extends E.Component{constructor(t){super(t),this.state={location:t.location,revalidation:t.revalidation,error:t.error}}static getDerivedStateFromError(t){return{error:t}}static getDerivedStateFromProps(t,n){return n.location!==t.location||n.revalidation!=="idle"&&t.revalidation==="idle"?{error:t.error,location:t.location,revalidation:t.revalidation}:{error:t.error||n.error,location:n.location,revalidation:t.revalidation||n.revalidation}}componentDidCatch(t,n){console.error("React Router caught the following error during render",t,n)}render(){return this.state.error?E.createElement(Sa.Provider,{value:this.props.routeContext},E.createElement(h8.Provider,{value:this.state.error,children:this.props.component})):this.props.children}}function gk(e){let{routeContext:t,match:n,children:r}=e,i=E.useContext(Vv);return i&&i.static&&i.staticContext&&(n.route.errorElement||n.route.ErrorBoundary)&&(i.staticContext._deepestRenderedBoundaryId=n.route.id),E.createElement(Sa.Provider,{value:t},r)}function vk(e,t,n){var r;if(t===void 0&&(t=[]),n===void 0&&(n=null),e==null){var i;if((i=n)!=null&&i.errors)e=n.matches;else return null}let o=e,a=(r=n)==null?void 0:r.errors;if(a!=null){let s=o.findIndex(l=>l.route.id&&(a==null?void 0:a[l.route.id]));s>=0||kt(!1),o=o.slice(0,Math.min(o.length,s+1))}return o.reduceRight((s,l,u)=>{let c=l.route.id?a==null?void 0:a[l.route.id]:null,f=null;n&&(f=l.route.errorElement||pk);let d=t.concat(o.slice(0,u+1)),h=()=>{let v;return c?v=f:l.route.Component?v=E.createElement(l.route.Component,null):l.route.element?v=l.route.element:v=s,E.createElement(gk,{match:l,routeContext:{outlet:s,matches:d,isDataRoute:n!=null},children:v})};return n&&(l.route.ErrorBoundary||l.route.errorElement||u===0)?E.createElement(mk,{location:n.location,revalidation:n.revalidation,component:f,error:c,children:h(),routeContext:{outlet:null,matches:d,isDataRoute:!0}}):h()},null)}var Z0;(function(e){e.UseBlocker="useBlocker",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate"})(Z0||(Z0={}));var N1;(function(e){e.UseBlocker="useBlocker",e.UseLoaderData="useLoaderData",e.UseActionData="useActionData",e.UseRouteError="useRouteError",e.UseNavigation="useNavigation",e.UseRouteLoaderData="useRouteLoaderData",e.UseMatches="useMatches",e.UseRevalidator="useRevalidator",e.UseNavigateStable="useNavigate",e.UseRouteId="useRouteId"})(N1||(N1={}));function Ek(e){let t=E.useContext(Vv);return t||kt(!1),t}function yk(e){let t=E.useContext(ok);return t||kt(!1),t}function Tk(e){let t=E.useContext(Sa);return t||kt(!1),t}function g8(e){let t=Tk(),n=t.matches[t.matches.length-1];return n.route.id||kt(!1),n.route.id}function Ck(){var e;let t=E.useContext(h8),n=yk(N1.UseRouteError),r=g8(N1.UseRouteError);return t||((e=n.errors)==null?void 0:e[r])}function Sk(){let{router:e}=Ek(Z0.UseNavigateStable),t=g8(N1.UseNavigateStable),n=E.useRef(!1);return p8(()=>{n.current=!0}),E.useCallback(function(i,o){o===void 0&&(o={}),n.current&&(typeof i=="number"?e.navigate(i):e.navigate(i,zd({fromRouteId:t},o)))},[e,t])}function _k(e){return ck(e.context)}function ed(e){kt(!1)}function bk(e){let{basename:t="/",children:n=null,location:r,navigationType:i=Jo.Pop,navigator:o,static:a=!1}=e;ic()&&kt(!1);let s=t.replace(/^\/*/,"/"),l=E.useMemo(()=>({basename:s,navigator:o,static:a}),[s,o,a]);typeof r=="string"&&(r=Ts(r));let{pathname:u="/",search:c="",hash:f="",state:d=null,key:h="default"}=r,v=E.useMemo(()=>{let g=$v(u,s);return g==null?null:{location:{pathname:g,search:c,hash:f,state:d,key:h},navigationType:i}},[s,u,c,f,d,h,i]);return v==null?null:E.createElement(Vl.Provider,{value:l},E.createElement(Ih.Provider,{children:n,value:v}))}function Ak(e){let{children:t,location:n}=e;return fk(J0(t),n)}var x5;(function(e){e[e.pending=0]="pending",e[e.success=1]="success",e[e.error=2]="error"})(x5||(x5={}));new Promise(()=>{});function J0(e,t){t===void 0&&(t=[]);let n=[];return E.Children.forEach(e,(r,i)=>{if(!E.isValidElement(r))return;let o=[...t,i];if(r.type===E.Fragment){n.push.apply(n,J0(r.props.children,o));return}r.type!==ed&&kt(!1),!r.props.index||!r.props.children||kt(!1);let a={id:r.props.id||o.join("-"),caseSensitive:r.props.caseSensitive,element:r.props.element,Component:r.props.Component,index:r.props.index,path:r.props.path,loader:r.props.loader,action:r.props.action,errorElement:r.props.errorElement,ErrorBoundary:r.props.ErrorBoundary,hasErrorBoundary:r.props.ErrorBoundary!=null||r.props.errorElement!=null,shouldRevalidate:r.props.shouldRevalidate,handle:r.props.handle,lazy:r.props.lazy};r.props.children&&(a.children=J0(r.props.children,o)),n.push(a)}),n}/** + * React Router DOM v6.12.1 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */function eg(){return eg=Object.assign?Object.assign.bind():function(e){for(var t=1;t=0)&&(n[i]=e[i]);return n}function kk(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Ik(e,t){return e.button===0&&(!t||t==="_self")&&!kk(e)}const xk=["onClick","relative","reloadDocument","replace","state","target","to","preventScrollReset"],N5="startTransition";function Nk(e){let{basename:t,children:n,window:r}=e,i=E.useRef();i.current==null&&(i.current=Mw({window:r,v5Compat:!0}));let o=i.current,[a,s]=E.useState({action:o.action,location:o.location}),l=E.useCallback(u=>{N5 in n0?n0[N5](()=>s(u)):s(u)},[s]);return E.useLayoutEffect(()=>o.listen(l),[o,l]),E.createElement(bk,{basename:t,children:n,location:a.location,navigationType:a.action,navigator:o})}const Fk=typeof window<"u"&&typeof window.document<"u"&&typeof window.document.createElement<"u",Rk=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Dk=E.forwardRef(function(t,n){let{onClick:r,relative:i,reloadDocument:o,replace:a,state:s,target:l,to:u,preventScrollReset:c}=t,f=wk(t,xk),{basename:d}=E.useContext(Vl),h,v=!1;if(typeof u=="string"&&Rk.test(u)&&(h=u,Fk))try{let y=new URL(window.location.href),T=u.startsWith("//")?new URL(y.protocol+u):new URL(u),S=$v(T.pathname,d);T.origin===y.origin&&S!=null?u=S+T.search+T.hash:v=!0}catch{}let g=ak(u,{relative:i}),C=Mk(u,{replace:a,state:s,target:l,preventScrollReset:c,relative:i});function m(y){r&&r(y),y.defaultPrevented||C(y)}return E.createElement("a",eg({},f,{href:h||g,onClick:v||o?r:m,ref:n,target:l}))});var F5;(function(e){e.UseScrollRestoration="useScrollRestoration",e.UseSubmitImpl="useSubmitImpl",e.UseFetcher="useFetcher"})(F5||(F5={}));var R5;(function(e){e.UseFetchers="useFetchers",e.UseScrollRestoration="useScrollRestoration"})(R5||(R5={}));function Mk(e,t){let{target:n,replace:r,state:i,preventScrollReset:o,relative:a}=t===void 0?{}:t,s=sk(),l=xh(),u=m8(e,{relative:a});return E.useCallback(c=>{if(Ik(c,n)){c.preventDefault();let f=r!==void 0?r:Hd(l)===Hd(u);s(e,{replace:f,state:i,preventScrollReset:o,relative:a})}},[l,s,u,r,i,n,e,o,a])}var D5={},td=void 0;try{td=window}catch{}function qv(e,t){if(typeof td<"u"){var n=td.__packages__=td.__packages__||{};if(!n[e]||!D5[e]){D5[e]=t;var r=n[e]=n[e]||[];r.push(t)}}}qv("@fluentui/set-version","6.0.0");var tg=function(e,t){return tg=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,r){n.__proto__=r}||function(n,r){for(var i in r)Object.prototype.hasOwnProperty.call(r,i)&&(n[i]=r[i])},tg(e,t)};function yt(e,t){if(typeof t!="function"&&t!==null)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");tg(e,t);function n(){this.constructor=e}e.prototype=t===null?Object.create(t):(n.prototype=t.prototype,new n)}var R=function(){return R=Object.assign||function(t){for(var n,r=1,i=arguments.length;r=0;s--)(a=e[s])&&(o=(i<3?a(o):i>3?a(t,n,o):a(t,n))||o);return i>3&&o&&Object.defineProperty(t,n,o),o}function si(e,t,n){if(n||arguments.length===2)for(var r=0,i=t.length,o;r"u"?vu.none:vu.insertNode,defaultPrefix:"css",namespace:void 0,cspSettings:void 0},t),this._classNameToArgs=(r=n==null?void 0:n.classNameToArgs)!==null&&r!==void 0?r:this._classNameToArgs,this._counter=(i=n==null?void 0:n.counter)!==null&&i!==void 0?i:this._counter,this._keyToClassName=(a=(o=this._config.classNameCache)!==null&&o!==void 0?o:n==null?void 0:n.keyToClassName)!==null&&a!==void 0?a:this._keyToClassName,this._preservedRules=(s=n==null?void 0:n.preservedRules)!==null&&s!==void 0?s:this._preservedRules,this._rules=(l=n==null?void 0:n.rules)!==null&&l!==void 0?l:this._rules}return e.getInstance=function(){if(Rs=Ws[M5],!Rs||Rs._lastStyleElement&&Rs._lastStyleElement.ownerDocument!==document){var t=(Ws==null?void 0:Ws.FabricConfig)||{},n=new e(t.mergeStyles,t.serializedStylesheet);Rs=n,Ws[M5]=n}return Rs},e.prototype.serialize=function(){return JSON.stringify({classNameToArgs:this._classNameToArgs,counter:this._counter,keyToClassName:this._keyToClassName,preservedRules:this._preservedRules,rules:this._rules})},e.prototype.setConfig=function(t){this._config=R(R({},this._config),t)},e.prototype.onReset=function(t){var n=this;return this._onResetCallbacks.push(t),function(){n._onResetCallbacks=n._onResetCallbacks.filter(function(r){return r!==t})}},e.prototype.onInsertRule=function(t){var n=this;return this._onInsertRuleCallbacks.push(t),function(){n._onInsertRuleCallbacks=n._onInsertRuleCallbacks.filter(function(r){return r!==t})}},e.prototype.getClassName=function(t){var n=this._config.namespace,r=t||this._config.defaultPrefix;return"".concat(n?n+"-":"").concat(r,"-").concat(this._counter++)},e.prototype.cacheClassName=function(t,n,r,i){this._keyToClassName[n]=t,this._classNameToArgs[t]={args:r,rules:i}},e.prototype.classNameFromKey=function(t){return this._keyToClassName[t]},e.prototype.getClassNameCache=function(){return this._keyToClassName},e.prototype.argsFromClassName=function(t){var n=this._classNameToArgs[t];return n&&n.args},e.prototype.insertedRulesFromClassName=function(t){var n=this._classNameToArgs[t];return n&&n.rules},e.prototype.insertRule=function(t,n){var r=this._config.injectionMode,i=r!==vu.none?this._getStyleElement():void 0;if(n&&this._preservedRules.push(t),i)switch(r){case vu.insertNode:var o=i.sheet;try{o.insertRule(t,o.cssRules.length)}catch{}break;case vu.appendChild:i.appendChild(document.createTextNode(t));break}else this._rules.push(t);this._config.onInsertRule&&this._config.onInsertRule(t),this._onInsertRuleCallbacks.forEach(function(a){return a()})},e.prototype.getRules=function(t){return(t?this._preservedRules.join(""):"")+this._rules.join("")},e.prototype.reset=function(){this._rules=[],this._counter=0,this._classNameToArgs={},this._keyToClassName={},this._onResetCallbacks.forEach(function(t){return t()})},e.prototype.resetKeys=function(){this._keyToClassName={}},e.prototype._getStyleElement=function(){var t=this;return!this._styleElement&&typeof document<"u"&&(this._styleElement=this._createStyleElement(),Pk||window.requestAnimationFrame(function(){t._styleElement=void 0})),this._styleElement},e.prototype._createStyleElement=function(){var t=document.head,n=document.createElement("style"),r=null;n.setAttribute("data-merge-styles","true");var i=this._config.cspSettings;if(i&&i.nonce&&n.setAttribute("nonce",i.nonce),this._lastStyleElement)r=this._lastStyleElement.nextElementSibling;else{var o=this._findPlaceholderStyleTag();o?r=o.nextElementSibling:r=t.childNodes[0]}return t.insertBefore(n,t.contains(r)?r:null),this._lastStyleElement=n,n},e.prototype._findPlaceholderStyleTag=function(){var t=document.head;return t?t.querySelector("style[data-merge-styles]"):null},e}();function v8(){for(var e=[],t=0;t=0)o(u.split(" "));else{var c=i.argsFromClassName(u);c?o(c):n.indexOf(u)===-1&&n.push(u)}else Array.isArray(u)?o(u):typeof u=="object"&&r.push(u)}}return o(e),{classes:n,objects:r}}function E8(e){Cl!==e&&(Cl=e)}function y8(){return Cl===void 0&&(Cl=typeof document<"u"&&!!document.documentElement&&document.documentElement.getAttribute("dir")==="rtl"),Cl}var Cl;Cl=y8();function Nh(){return{rtl:y8()}}var P5={};function Ok(e,t){var n=e[t];n.charAt(0)!=="-"&&(e[t]=P5[n]=P5[n]||n.replace(/([A-Z])/g,"-$1").toLowerCase())}var nf;function Lk(){var e;if(!nf){var t=typeof document<"u"?document:void 0,n=typeof navigator<"u"?navigator:void 0,r=(e=n==null?void 0:n.userAgent)===null||e===void 0?void 0:e.toLowerCase();t?nf={isWebkit:!!(t&&"WebkitAppearance"in t.documentElement.style),isMoz:!!(r&&r.indexOf("firefox")>-1),isOpera:!!(r&&r.indexOf("opera")>-1),isMs:!!(n&&(/rv:11.0/i.test(n.userAgent)||/Edge\/\d./i.test(navigator.userAgent)))}:nf={isWebkit:!0,isMoz:!0,isOpera:!0,isMs:!0}}return nf}var O5={"user-select":1};function Bk(e,t){var n=Lk(),r=e[t];if(O5[r]){var i=e[t+1];O5[r]&&(n.isWebkit&&e.push("-webkit-"+r,i),n.isMoz&&e.push("-moz-"+r,i),n.isMs&&e.push("-ms-"+r,i),n.isOpera&&e.push("-o-"+r,i))}}var Hk=["column-count","font-weight","flex","flex-grow","flex-shrink","fill-opacity","opacity","order","z-index","zoom"];function zk(e,t){var n=e[t],r=e[t+1];if(typeof r=="number"){var i=Hk.indexOf(n)>-1,o=n.indexOf("--")>-1,a=i||o?"":"px";e[t+1]="".concat(r).concat(a)}}var rf,Go="left",$o="right",Uk="@noflip",L5=(rf={},rf[Go]=$o,rf[$o]=Go,rf),B5={"w-resize":"e-resize","sw-resize":"se-resize","nw-resize":"ne-resize"};function Kk(e,t,n){if(e.rtl){var r=t[n];if(!r)return;var i=t[n+1];if(typeof i=="string"&&i.indexOf(Uk)>=0)t[n+1]=i.replace(/\s*(?:\/\*\s*)?\@noflip\b(?:\s*\*\/)?\s*?/g,"");else if(r.indexOf(Go)>=0)t[n]=r.replace(Go,$o);else if(r.indexOf($o)>=0)t[n]=r.replace($o,Go);else if(String(i).indexOf(Go)>=0)t[n+1]=i.replace(Go,$o);else if(String(i).indexOf($o)>=0)t[n+1]=i.replace($o,Go);else if(L5[r])t[n]=L5[r];else if(B5[i])t[n+1]=B5[i];else switch(r){case"margin":case"padding":t[n+1]=jk(i);break;case"box-shadow":t[n+1]=Wk(i,0);break}}}function Wk(e,t){var n=e.split(" "),r=parseInt(n[t],10);return n[0]=n[0].replace(String(r),String(r*-1)),n.join(" ")}function jk(e){if(typeof e=="string"){var t=e.split(" ");if(t.length===4)return"".concat(t[0]," ").concat(t[3]," ").concat(t[2]," ").concat(t[1])}return e}function Gk(e){for(var t=[],n=0,r=0,i=0;in&&t.push(e.substring(n,i)),n=i+1);break}return n-1&&t.push([r.index,r.index+r[0].length,r[1].split(",").map(function(i){return":global(".concat(i.trim(),")")}).join(", ")]);return t.reverse().reduce(function(i,o){var a=o[0],s=o[1],l=o[2],u=i.slice(0,a),c=i.slice(s);return u+l+c},e)}function H5(e,t){return e.indexOf(":global(")>=0?e.replace(T8,"$1"):e.indexOf(":")===0?t+e:e.indexOf("&")<0?t+" "+e:e}function z5(e,t,n,r){t===void 0&&(t={__order:[]}),n.indexOf("@")===0?(n=n+"{"+e,Sl([r],t,n)):n.indexOf(",")>-1?qk(n).split(",").map(function(i){return i.trim()}).forEach(function(i){return Sl([r],t,H5(i,e))}):Sl([r],t,H5(n,e))}function Sl(e,t,n){t===void 0&&(t={__order:[]}),n===void 0&&(n="&");var r=Pr.getInstance(),i=t[n];i||(i={},t[n]=i,t.__order.push(n));for(var o=0,a=e;o0){n.subComponentStyles={};var d=n.subComponentStyles,h=function(v){if(r.hasOwnProperty(v)){var g=r[v];d[v]=function(C){return ba.apply(void 0,g.map(function(m){return typeof m=="function"?m(C):m}))}}};for(var u in r)h(u)}return n}function oc(){for(var e=[],t=0;t"u")){var t=e;return t&&t.ownerDocument&&t.ownerDocument.defaultView?t.ownerDocument.defaultView:ng}}var ac=function(){function e(t,n){this._timeoutIds=null,this._immediateIds=null,this._intervalIds=null,this._animationFrameIds=null,this._isDisposed=!1,this._parent=t||null,this._onErrorHandler=n,this._noop=function(){}}return e.prototype.dispose=function(){var t;if(this._isDisposed=!0,this._parent=null,this._timeoutIds){for(t in this._timeoutIds)this._timeoutIds.hasOwnProperty(t)&&this.clearTimeout(parseInt(t,10));this._timeoutIds=null}if(this._immediateIds){for(t in this._immediateIds)this._immediateIds.hasOwnProperty(t)&&this.clearImmediate(parseInt(t,10));this._immediateIds=null}if(this._intervalIds){for(t in this._intervalIds)this._intervalIds.hasOwnProperty(t)&&this.clearInterval(parseInt(t,10));this._intervalIds=null}if(this._animationFrameIds){for(t in this._animationFrameIds)this._animationFrameIds.hasOwnProperty(t)&&this.cancelAnimationFrame(parseInt(t,10));this._animationFrameIds=null}},e.prototype.setTimeout=function(t,n){var r=this,i=0;return this._isDisposed||(this._timeoutIds||(this._timeoutIds={}),i=setTimeout(function(){try{r._timeoutIds&&delete r._timeoutIds[i],t.apply(r._parent)}catch(o){r._logError(o)}},n),this._timeoutIds[i]=!0),i},e.prototype.clearTimeout=function(t){this._timeoutIds&&this._timeoutIds[t]&&(clearTimeout(t),delete this._timeoutIds[t])},e.prototype.setImmediate=function(t,n){var r=this,i=0,o=st(n);if(!this._isDisposed){this._immediateIds||(this._immediateIds={});var a=function(){try{r._immediateIds&&delete r._immediateIds[i],t.apply(r._parent)}catch(s){r._logError(s)}};i=o.setTimeout(a,0),this._immediateIds[i]=!0}return i},e.prototype.clearImmediate=function(t,n){var r=st(n);this._immediateIds&&this._immediateIds[t]&&(r.clearTimeout(t),delete this._immediateIds[t])},e.prototype.setInterval=function(t,n){var r=this,i=0;return this._isDisposed||(this._intervalIds||(this._intervalIds={}),i=setInterval(function(){try{t.apply(r._parent)}catch(o){r._logError(o)}},n),this._intervalIds[i]=!0),i},e.prototype.clearInterval=function(t){this._intervalIds&&this._intervalIds[t]&&(clearInterval(t),delete this._intervalIds[t])},e.prototype.throttle=function(t,n,r){var i=this;if(this._isDisposed)return this._noop;var o=n||0,a=!0,s=!0,l=0,u,c,f=null;r&&typeof r.leading=="boolean"&&(a=r.leading),r&&typeof r.trailing=="boolean"&&(s=r.trailing);var d=function(v){var g=Date.now(),C=g-l,m=a?o-C:o;return C>=o&&(!v||a)?(l=g,f&&(i.clearTimeout(f),f=null),u=t.apply(i._parent,c)):f===null&&s&&(f=i.setTimeout(d,m)),u},h=function(){for(var v=[],g=0;g=a&&(F=!0),c=A);var D=A-c,I=a-D,B=A-f,M=!1;return u!==null&&(B>=u&&v?M=!0:I=Math.min(I,u-B)),D>=a||M||F?C(A):(v===null||!b)&&l&&(v=i.setTimeout(m,I)),d},y=function(){return!!v},T=function(){y()&&g(Date.now())},S=function(){return y()&&C(Date.now()),d},w=function(){for(var b=[],A=0;A-1)for(var a=n.split(/[ ,]+/),s=0;s"u")){var t=e;return t&&t.ownerDocument?t.ownerDocument:document}}var jp,Yu=0,I8=Ii({overflow:"hidden !important"}),U5="data-is-scrollable",eI=function(e,t){if(e){var n=0,r=null,i=function(a){a.targetTouches.length===1&&(n=a.targetTouches[0].clientY)},o=function(a){if(a.targetTouches.length===1&&(a.stopPropagation(),!!r)){var s=a.targetTouches[0].clientY-n,l=N8(a.target);l&&(r=l),r.scrollTop===0&&s>0&&a.preventDefault(),r.scrollHeight-Math.ceil(r.scrollTop)<=r.clientHeight&&s<0&&a.preventDefault()}};t.on(e,"touchstart",i,{passive:!1}),t.on(e,"touchmove",o,{passive:!1}),r=e}},tI=function(e,t){if(e){var n=function(r){r.stopPropagation()};t.on(e,"touchmove",n,{passive:!1})}},x8=function(e){e.preventDefault()};function nI(){var e=xn();e&&e.body&&!Yu&&(e.body.classList.add(I8),e.body.addEventListener("touchmove",x8,{passive:!1,capture:!1})),Yu++}function rI(){if(Yu>0){var e=xn();e&&e.body&&Yu===1&&(e.body.classList.remove(I8),e.body.removeEventListener("touchmove",x8)),Yu--}}function iI(){if(jp===void 0){var e=document.createElement("div");e.style.setProperty("width","100px"),e.style.setProperty("height","100px"),e.style.setProperty("overflow","scroll"),e.style.setProperty("position","absolute"),e.style.setProperty("top","-9999px"),document.body.appendChild(e),jp=e.offsetWidth-e.clientWidth,document.body.removeChild(e)}return jp}function N8(e){for(var t=e,n=xn(e);t&&t!==n.body;){if(t.getAttribute(U5)==="true")return t;t=t.parentElement}for(t=e;t&&t!==n.body;){if(t.getAttribute(U5)!=="false"){var r=getComputedStyle(t),i=r?r.getPropertyValue("overflow-y"):"";if(i&&(i==="scroll"||i==="auto"))return t}t=t.parentElement}return(!t||t===n.body)&&(t=st(e)),t}var oI=void 0;function Jv(e){console&&console.warn&&console.warn(e)}function F8(e,t,n,r,i){if(i===!0&&!1)for(var o,a;o1?r[1]:""}return this.__className},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"_disposables",{get:function(){return this.__disposables||(this.__disposables=[]),this.__disposables},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"_async",{get:function(){return this.__async||(this.__async=new ac(this),this._disposables.push(this.__async)),this.__async},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"_events",{get:function(){return this.__events||(this.__events=new Xr(this),this._disposables.push(this.__events)),this.__events},enumerable:!1,configurable:!0}),t.prototype._resolveRef=function(n){var r=this;return this.__resolves||(this.__resolves={}),this.__resolves[n]||(this.__resolves[n]=function(i){return r[n]=i}),this.__resolves[n]},t.prototype._updateComponentRef=function(n,r){r===void 0&&(r={}),n&&r&&n.componentRef!==r.componentRef&&(this._setComponentRef(n.componentRef,null),this._setComponentRef(r.componentRef,this))},t.prototype._warnDeprecations=function(n){this.className,this.props},t.prototype._warnMutuallyExclusive=function(n){this.className,this.props},t.prototype._warnConditionallyRequiredProps=function(n,r,i){F8(this.className,this.props,n,r,i)},t.prototype._setComponentRef=function(n,r){!this._skipComponentRefResolution&&n&&(typeof n=="function"&&n(r),typeof n=="object"&&(n.current=r))},t})(E.Component);function aI(e,t,n){for(var r=0,i=n.length;r-1&&i._virtual.children.splice(o,1)}n._virtual.parent=r||void 0,r&&(r._virtual||(r._virtual={children:[]}),r._virtual.children.push(n))}var TI="data-is-focusable",CI="data-is-visible",SI="data-focuszone-id",_I="data-is-sub-focuszone";function bI(e,t,n){return Cn(e,t,!0,!1,!1,n)}function AI(e,t,n){return jn(e,t,!0,!1,!0,n)}function wI(e,t,n,r){return r===void 0&&(r=!0),Cn(e,t,r,!1,!1,n,!1,!0)}function kI(e,t,n,r){return r===void 0&&(r=!0),jn(e,t,r,!1,!0,n,!1,!0)}function II(e,t){var n=Cn(e,e,!0,!1,!1,!0,void 0,void 0,t);return n?(B8(n),!0):!1}function jn(e,t,n,r,i,o,a,s){if(!t||!a&&t===e)return null;var l=Fh(t);if(i&&l&&(o||!(eo(t)||n9(t)))){var u=jn(e,t.lastElementChild,!0,!0,!0,o,a,s);if(u){if(s&&Ai(u,!0)||!s)return u;var c=jn(e,u.previousElementSibling,!0,!0,!0,o,a,s);if(c)return c;for(var f=u.parentElement;f&&f!==t;){var d=jn(e,f.previousElementSibling,!0,!0,!0,o,a,s);if(d)return d;f=f.parentElement}}}if(n&&l&&Ai(t,s))return t;var h=jn(e,t.previousElementSibling,!0,!0,!0,o,a,s);return h||(r?null:jn(e,t.parentElement,!0,!1,!1,o,a,s))}function Cn(e,t,n,r,i,o,a,s,l){if(!t||t===e&&i&&!a)return null;var u=l?O8:Fh,c=u(t);if(n&&c&&Ai(t,s))return t;if(!i&&c&&(o||!(eo(t)||n9(t)))){var f=Cn(e,t.firstElementChild,!0,!0,!1,o,a,s,l);if(f)return f}if(t===e)return null;var d=Cn(e,t.nextElementSibling,!0,!0,!1,o,a,s,l);return d||(r?null:Cn(e,t.parentElement,!1,!1,!0,o,a,s,l))}function Fh(e){if(!e||!e.getAttribute)return!1;var t=e.getAttribute(CI);return t!=null?t==="true":e.offsetHeight!==0||e.offsetParent!==null||e.isVisible===!0}function O8(e){return!!e&&Fh(e)&&!e.hidden&&window.getComputedStyle(e).visibility!=="hidden"}function Ai(e,t){if(!e||e.disabled)return!1;var n=0,r=null;e&&e.getAttribute&&(r=e.getAttribute("tabIndex"),r&&(n=parseInt(r,10)));var i=e.getAttribute?e.getAttribute(TI):null,o=r!==null&&n>=0,a=!!e&&i!=="false"&&(e.tagName==="A"||e.tagName==="BUTTON"||e.tagName==="INPUT"||e.tagName==="TEXTAREA"||e.tagName==="SELECT"||i==="true"||o);return t?n!==-1&&a:a}function eo(e){return!!(e&&e.getAttribute&&e.getAttribute(SI))}function n9(e){return!!(e&&e.getAttribute&&e.getAttribute(_I)==="true")}function xI(e){var t=xn(e),n=t&&t.activeElement;return!!(n&&$n(e,n))}function L8(e,t){return vI(e,t)!=="true"}var Ds=void 0;function B8(e){if(e){if(Ds){Ds=e;return}Ds=e;var t=st(e);t&&t.requestAnimationFrame(function(){Ds&&Ds.focus(),Ds=void 0})}}function NI(e,t){for(var n=e,r=0,i=t;r(e.cacheSize||RI)){var h=st();!((l=h==null?void 0:h.FabricConfig)===null||l===void 0)&&l.enableClassNameCacheFullWarning&&(console.warn("Styles are being recalculated too frequently. Cache miss rate is ".concat(n,"/").concat(r,".")),console.trace()),t.clear(),n=0,e.disableCaching=!0}return u[of]};return o}function Vp(e,t){return t=MI(t),e.has(t)||e.set(t,new Map),e.get(t)}function W5(e,t){if(typeof t=="function"){var n=t.__cachedInputs__;if(n)for(var r=0,i=t.__cachedInputs__;r"u"?null:WeakMap;function OI(){rd++}function It(e,t,n){if(t===void 0&&(t=100),n===void 0&&(n=!1),!F1)return e;if(!j5){var r=Pr.getInstance();r&&r.onReset&&Pr.getInstance().onReset(OI),j5=!0}var i,o=0,a=rd;return function(){for(var l=[],u=0;u0&&o>t)&&(i=G5(),o=0,a=rd),c=i;for(var f=0;f=0||l.indexOf("data-")===0||l.indexOf("aria-")===0;u&&(!n||(n==null?void 0:n.indexOf(l))===-1)&&(i[l]=e[l])}return i}var lx=["setState","render","componentWillMount","UNSAFE_componentWillMount","componentDidMount","componentWillReceiveProps","UNSAFE_componentWillReceiveProps","shouldComponentUpdate","componentWillUpdate","getSnapshotBeforeUpdate","UNSAFE_componentWillUpdate","componentDidUpdate","componentWillUnmount"];function ux(e,t,n){n===void 0&&(n=lx);var r=[],i=function(a){typeof t[a]=="function"&&e[a]===void 0&&(!n||n.indexOf(a)===-1)&&(r.push(a),e[a]=function(){for(var s=[],l=0;l=0&&r.splice(l,1)}}},[t,r,i,o]);return E.useEffect(function(){if(a)return a.registerProvider(a.providerRef),function(){return a.unregisterProvider(a.providerRef)}},[a]),a?E.createElement(D1.Provider,{value:a},e.children):E.createElement(E.Fragment,null,e.children)};function vx(e){var t=null;try{var n=st();t=n?n.localStorage.getItem(e):null}catch{}return t}var Da,Z5="language";function Ex(e){if(e===void 0&&(e="sessionStorage"),Da===void 0){var t=xn(),n=e==="localStorage"?vx(Z5):e==="sessionStorage"?D8(Z5):void 0;n&&(Da=n),Da===void 0&&t&&(Da=t.documentElement.getAttribute("lang")),Da===void 0&&(Da="en")}return Da}function J5(e){for(var t=[],n=1;n-1;e[r]=o?i:Y8(e[r]||{},i,n)}else e[r]=i}return n.pop(),e}var ey=function(){return!window||!window.navigator||!window.navigator.userAgent?!1:/iPad|iPhone|iPod/i.test(window.navigator.userAgent)},yx=["TEMPLATE","STYLE","SCRIPT"];function Q8(e){var t=xn(e);if(!t)return function(){};for(var n=[];e!==t.body&&e.parentElement;){for(var r=0,i=e.parentElement.children;r"u"||e){var n=st(),r=(t=n==null?void 0:n.navigator)===null||t===void 0?void 0:t.userAgent;Yp=!!r&&r.indexOf("Macintosh")!==-1}return!!Yp}function Cx(e){var t=Dl(function(n){var r=Dl(function(i){return function(o){return n(o,i)}});return function(i,o){return e(i,o?r(o):n)}});return t}var Sx=Dl(Cx);function _x(e,t){return Sx(e)(t)}var bx=["theme","styles"];function Dn(e,t,n,r,i){r=r||{scope:"",fields:void 0};var o=r.scope,a=r.fields,s=a===void 0?bx:a,l=E.forwardRef(function(c,f){var d=E.useRef(),h=XI(s,o),v=h.styles;h.dir;var g=_a(h,["styles","dir"]),C=n?n(c):void 0,m=d.current&&d.current.__cachedInputs__||[],y=c.styles;if(!d.current||v!==m[1]||y!==m[2]){var T=function(S){return w8(S,t,v,y)};T.__cachedInputs__=[t,v,y],T.__noStyleOverride__=!v&&!y,d.current=T}return E.createElement(e,R({ref:f},g,C,c,{styles:d.current}))});l.displayName="Styled".concat(e.displayName||e.name);var u=i?E.memo(l):l;return l.displayName&&(u.displayName=l.displayName),u}var Ax=function(){var e,t=st();return!((e=t==null?void 0:t.navigator)===null||e===void 0)&&e.userAgent?t.navigator.userAgent.indexOf("rv:11.0")>-1:!1};function lc(e,t){for(var n=R({},t),r=0,i=Object.keys(e);rr?" (+ ".concat(Eu.length-r," more)"):"")),Xp=void 0,Eu=[]},n)))}function Nx(e,t,n,r,i){i===void 0&&(i=!1);var o=R({primaryButtonBorder:"transparent",errorText:r?"#F1707B":"#a4262c",messageText:r?"#F3F2F1":"#323130",messageLink:r?"#6CB8F6":"#005A9E",messageLinkHovered:r?"#82C7FF":"#004578",infoIcon:r?"#C8C6C4":"#605e5c",errorIcon:r?"#F1707B":"#A80000",blockingIcon:r?"#442726":"#FDE7E9",warningIcon:r?"#C8C6C4":"#797775",severeWarningIcon:r?"#FCE100":"#D83B01",successIcon:r?"#92C353":"#107C10",infoBackground:r?"#323130":"#f3f2f1",errorBackground:r?"#442726":"#FDE7E9",blockingBackground:r?"#442726":"#FDE7E9",warningBackground:r?"#433519":"#FFF4CE",severeWarningBackground:r?"#4F2A0F":"#FED9CC",successBackground:r?"#393D1B":"#DFF6DD",warningHighlight:r?"#fff100":"#ffb900",successText:r?"#92c353":"#107C10"},n),a=Z8(e,t,o,r);return Fx(a,i)}function Z8(e,t,n,r,i){var o={},a=e||{},s=a.white,l=a.black,u=a.themePrimary,c=a.themeDark,f=a.themeDarker,d=a.themeDarkAlt,h=a.themeLighter,v=a.neutralLight,g=a.neutralLighter,C=a.neutralDark,m=a.neutralQuaternary,y=a.neutralQuaternaryAlt,T=a.neutralPrimary,S=a.neutralSecondary,w=a.neutralSecondaryAlt,b=a.neutralTertiary,A=a.neutralTertiaryAlt,F=a.neutralLighterAlt,D=a.accent;return s&&(o.bodyBackground=s,o.bodyFrameBackground=s,o.accentButtonText=s,o.buttonBackground=s,o.primaryButtonText=s,o.primaryButtonTextHovered=s,o.primaryButtonTextPressed=s,o.inputBackground=s,o.inputForegroundChecked=s,o.listBackground=s,o.menuBackground=s,o.cardStandoutBackground=s),l&&(o.bodyTextChecked=l,o.buttonTextCheckedHovered=l),u&&(o.link=u,o.primaryButtonBackground=u,o.inputBackgroundChecked=u,o.inputIcon=u,o.inputFocusBorderAlt=u,o.menuIcon=u,o.menuHeader=u,o.accentButtonBackground=u),c&&(o.primaryButtonBackgroundPressed=c,o.inputBackgroundCheckedHovered=c,o.inputIconHovered=c),f&&(o.linkHovered=f),d&&(o.primaryButtonBackgroundHovered=d),h&&(o.inputPlaceholderBackgroundChecked=h),v&&(o.bodyBackgroundChecked=v,o.bodyFrameDivider=v,o.bodyDivider=v,o.variantBorder=v,o.buttonBackgroundCheckedHovered=v,o.buttonBackgroundPressed=v,o.listItemBackgroundChecked=v,o.listHeaderBackgroundPressed=v,o.menuItemBackgroundPressed=v,o.menuItemBackgroundChecked=v),g&&(o.bodyBackgroundHovered=g,o.buttonBackgroundHovered=g,o.buttonBackgroundDisabled=g,o.buttonBorderDisabled=g,o.primaryButtonBackgroundDisabled=g,o.disabledBackground=g,o.listItemBackgroundHovered=g,o.listHeaderBackgroundHovered=g,o.menuItemBackgroundHovered=g),m&&(o.primaryButtonTextDisabled=m,o.disabledSubtext=m),y&&(o.listItemBackgroundCheckedHovered=y),b&&(o.disabledBodyText=b,o.variantBorderHovered=(n==null?void 0:n.variantBorderHovered)||b,o.buttonTextDisabled=b,o.inputIconDisabled=b,o.disabledText=b),T&&(o.bodyText=T,o.actionLink=T,o.buttonText=T,o.inputBorderHovered=T,o.inputText=T,o.listText=T,o.menuItemText=T),F&&(o.bodyStandoutBackground=F,o.defaultStateBackground=F),C&&(o.actionLinkHovered=C,o.buttonTextHovered=C,o.buttonTextChecked=C,o.buttonTextPressed=C,o.inputTextHovered=C,o.menuItemTextHovered=C),S&&(o.bodySubtext=S,o.focusBorder=S,o.inputBorder=S,o.smallInputBorder=S,o.inputPlaceholderText=S),w&&(o.buttonBorder=w),A&&(o.disabledBodySubtext=A,o.disabledBorder=A,o.buttonBackgroundChecked=A,o.menuDivider=A),D&&(o.accentButtonBackground=D),t!=null&&t.elevation4&&(o.cardShadow=t.elevation4),!r&&(t!=null&&t.elevation8)?o.cardShadowHovered=t.elevation8:o.variantBorderHovered&&(o.cardShadowHovered="0 0 1px "+o.variantBorderHovered),o=R(R({},o),n),o}function Fx(e,t){var n="";return t===!0&&(n=" /* @deprecated */"),e.listTextColor=e.listText+n,e.menuItemBackgroundChecked+=n,e.warningHighlight+=n,e.warningText=e.messageText+n,e.successText+=n,e}function Rx(e,t){var n,r,i;t===void 0&&(t={});var o=J5({},e,t,{semanticColors:Z8(t.palette,t.effects,t.semanticColors,t.isInverted===void 0?e.isInverted:t.isInverted)});if(!((n=t.palette)===null||n===void 0)&&n.themePrimary&&!(!((r=t.palette)===null||r===void 0)&&r.accent)&&(o.palette.accent=t.palette.themePrimary),t.defaultFontStyle)for(var a=0,s=Object.keys(o.fonts);a"u"?global:window,ay=Qu&&Qu.CSPSettings&&Qu.CSPSettings.nonce,dr=bN();function bN(){var e=Qu.__themeState__||{theme:void 0,lastStyleElement:void 0,registeredStyles:[]};return e.runState||(e=sl(sl({},e),{perf:{count:0,duration:0},runState:{flushTimer:0,mode:0,buffer:[]}})),e.registeredThemableStyles||(e=sl(sl({},e),{registeredThemableStyles:[]})),Qu.__themeState__=e,e}function AN(e,t){dr.loadStyles?dr.loadStyles(r_(e).styleString,e):xN(e)}function wN(e){dr.theme=e,IN()}function kN(e){e===void 0&&(e=3),(e===3||e===2)&&(sy(dr.registeredStyles),dr.registeredStyles=[]),(e===3||e===1)&&(sy(dr.registeredThemableStyles),dr.registeredThemableStyles=[])}function sy(e){e.forEach(function(t){var n=t&&t.styleElement;n&&n.parentElement&&n.parentElement.removeChild(n)})}function IN(){if(dr.theme){for(var e=[],t=0,n=dr.registeredThemableStyles;t0&&(kN(1),AN([].concat.apply([],e)))}}function r_(e){var t=dr.theme,n=!1,r=(e||[]).map(function(i){var o=i.theme;if(o){n=!0;var a=t?t[o]:void 0,s=i.defaultValue||"inherit";return t&&!a&&console&&!(o in t)&&typeof DEBUG<"u"&&DEBUG&&console.warn('Theming value not provided for "'.concat(o,'". Falling back to "').concat(s,'".')),a||s}else return i.rawString});return{styleString:r.join(""),themable:n}}function xN(e){if(!(typeof document>"u")){var t=document.getElementsByTagName("head")[0],n=document.createElement("style"),r=r_(e),i=r.styleString,o=r.themable;n.setAttribute("data-load-themed-styles","true"),ay&&n.setAttribute("nonce",ay),n.appendChild(document.createTextNode(i)),dr.perf.count++,t.appendChild(n);var a=document.createEvent("HTMLEvents");a.initEvent("styleinsert",!0,!1),a.args={newStyle:n},document.dispatchEvent(a);var s={styleElement:n,themableStyle:e};o?dr.registeredThemableStyles.push(s):dr.registeredStyles.push(s)}}var _r=uc({}),NN=[],lg="theme";function i_(){var e,t,n,r=st();!((t=r==null?void 0:r.FabricConfig)===null||t===void 0)&&t.legacyTheme?RN(r.FabricConfig.legacyTheme):Fr.getSettings([lg]).theme||(!((n=r==null?void 0:r.FabricConfig)===null||n===void 0)&&n.theme&&(_r=uc(r.FabricConfig.theme)),Fr.applySettings((e={},e[lg]=_r,e)))}i_();function FN(e){return e===void 0&&(e=!1),e===!0&&(_r=uc({},e)),_r}function RN(e,t){var n;return t===void 0&&(t=!1),_r=uc(e,t),wN(R(R(R(R({},_r.palette),_r.semanticColors),_r.effects),DN(_r))),Fr.applySettings((n={},n[lg]=_r,n)),NN.forEach(function(r){try{r(_r)}catch{}}),_r}function DN(e){for(var t={},n=0,r=Object.keys(e.fonts);nt.bottom||e.leftt.right)}function Gd(e,t){var n=[];return e.topt.bottom&&n.push(ce.bottom),e.leftt.right&&n.push(ce.right),n}function pn(e,t){return e[ce[t]]}function fy(e,t,n){return e[ce[t]]=n,e}function M1(e,t){var n=ql(t);return(pn(e,n.positiveEdge)+pn(e,n.negativeEdge))/2}function Mh(e,t){return e>0?t:t*-1}function ug(e,t){return Mh(e,pn(t,e))}function io(e,t,n){var r=pn(e,n)-pn(t,n);return Mh(n,r)}function Ol(e,t,n,r){r===void 0&&(r=!0);var i=pn(e,t)-n,o=fy(e,t,n);return r&&(o=fy(e,t*-1,pn(e,t*-1)-i)),o}function P1(e,t,n,r){return r===void 0&&(r=0),Ol(e,n,pn(t,n)+Mh(n,r))}function PN(e,t,n,r){r===void 0&&(r=0);var i=n*-1,o=Mh(i,r);return Ol(e,n*-1,pn(t,n)+o)}function $d(e,t,n){var r=ug(n,e);return r>ug(n,t)}function ON(e,t){for(var n=Gd(e,t),r=0,i=0,o=n;i0&&(o.indexOf(s*-1)>-1?s=s*-1:(l=s,s=o.slice(-1)[0]),a=Vd(e,t,{targetEdge:s,alignmentEdge:l},i))}return a=Vd(e,t,{targetEdge:c,alignmentEdge:f},i),{elementRectangle:a,targetEdge:c,alignmentEdge:f}}function BN(e,t,n,r){var i=e.alignmentEdge,o=e.targetEdge,a=e.elementRectangle,s=i*-1,l=Vd(a,t,{targetEdge:o,alignmentEdge:s},n,r);return{elementRectangle:l,targetEdge:o,alignmentEdge:s}}function HN(e,t,n,r,i,o,a){i===void 0&&(i=0);var s=r.alignmentEdge,l=r.alignTargetEdge,u={elementRectangle:e,targetEdge:r.targetEdge,alignmentEdge:s};!o&&!a&&(u=LN(e,t,n,r,i));var c=Gd(u.elementRectangle,n),f=o?-u.targetEdge:void 0;if(c.length>0)if(l)if(u.alignmentEdge&&c.indexOf(u.alignmentEdge*-1)>-1){var d=BN(u,t,i,a);if(r9(d.elementRectangle,n))return d;u=nm(Gd(d.elementRectangle,n),u,n,f)}else u=nm(c,u,n,f);else u=nm(c,u,n,f);return u}function nm(e,t,n,r){for(var i=0,o=e;iMath.abs(io(e,n,t*-1))?t*-1:t}function zN(e,t,n){return n!==void 0&&pn(e,t)===pn(n,t)}function UN(e,t,n,r,i,o,a,s){var l={},u=i9(t),c=o?n:n*-1,f=i||ql(n).positiveEdge;return(!a||zN(e,tF(f),r))&&(f=a_(e,f,r)),l[ce[c]]=io(e,u,c),l[ce[f]]=io(e,u,f),s&&(l[ce[c*-1]]=io(e,u,c*-1),l[ce[f*-1]]=io(e,u,f*-1)),l}function KN(e){return Math.sqrt(e*e*2)}function WN(e,t,n){if(e===void 0&&(e=_t.bottomAutoEdge),n)return{alignmentEdge:n.alignmentEdge,isAuto:n.isAuto,targetEdge:n.targetEdge};var r=R({},cy[e]);return _n()?(r.alignmentEdge&&r.alignmentEdge%2===0&&(r.alignmentEdge=r.alignmentEdge*-1),t!==void 0?cy[t]:r):r}function jN(e,t,n,r,i){return e.isAuto&&(e.alignmentEdge=s_(e.targetEdge,t,n)),e.alignTargetEdge=i,e}function s_(e,t,n){var r=M1(t,e),i=M1(n,e),o=ql(e),a=o.positiveEdge,s=o.negativeEdge;return r<=i?a:s}function GN(e,t,n,r,i,o,a){var s=Vd(e,t,r,i,a);return r9(s,n)?{elementRectangle:s,targetEdge:r.targetEdge,alignmentEdge:r.alignmentEdge}:HN(s,t,n,r,i,o,a)}function $N(e,t,n){var r=e.targetEdge*-1,i=new Bi(0,e.elementRectangle.width,0,e.elementRectangle.height),o={},a=a_(e.elementRectangle,e.alignmentEdge?e.alignmentEdge:ql(r).positiveEdge,n),s=io(e.elementRectangle,e.targetRectangle,r),l=s>Math.abs(pn(t,r));return o[ce[r]]=pn(t,r),o[ce[a]]=io(t,i,a),{elementPosition:R({},o),closestEdge:s_(e.targetEdge,t,i),targetEdge:r,hideBeak:!l}}function VN(e,t){var n=t.targetRectangle,r=ql(t.targetEdge),i=r.positiveEdge,o=r.negativeEdge,a=M1(n,t.targetEdge),s=new Bi(e/2,t.elementRectangle.width-e/2,e/2,t.elementRectangle.height-e/2),l=new Bi(0,e,0,e);return l=Ol(l,t.targetEdge*-1,-e/2),l=o_(l,t.targetEdge*-1,a-ug(i,t.elementRectangle)),$d(l,s,i)?$d(l,s,o)||(l=P1(l,s,o)):l=P1(l,s,i),l}function i9(e){var t=e.getBoundingClientRect();return new Bi(t.left,t.right,t.top,t.bottom)}function qN(e){return new Bi(e.left,e.right,e.top,e.bottom)}function YN(e,t){var n;if(t){if(t.preventDefault){var r=t;n=new Bi(r.clientX,r.clientX,r.clientY,r.clientY)}else if(t.getBoundingClientRect)n=i9(t);else{var i=t,o=i.left||i.x,a=i.top||i.y,s=i.right||o,l=i.bottom||a;n=new Bi(o,s,a,l)}if(!r9(n,e))for(var u=Gd(n,e),c=0,f=u;c=r&&i&&u.top<=i&&u.bottom>=i&&(a={top:u.top,left:u.left,right:u.right,bottom:u.bottom,width:u.width,height:u.height})}return a}function rF(e,t){return nF(e,t)}function Yl(){var e=E.useRef();return e.current||(e.current=new ac),E.useEffect(function(){return function(){var t;(t=e.current)===null||t===void 0||t.dispose(),e.current=void 0}},[]),e.current}function Hi(e){var t=E.useRef();return t.current===void 0&&(t.current={value:typeof e=="function"?e():e}),t.current.value}function u_(e){var t=E.useState(e),n=t[0],r=t[1],i=Hi(function(){return function(){r(!0)}}),o=Hi(function(){return function(){r(!1)}}),a=Hi(function(){return function(){r(function(s){return!s})}});return[n,{setTrue:i,setFalse:o,toggle:a}]}function rm(e){var t=E.useRef(function(){throw new Error("Cannot call an event handler while rendering")});return Ml(function(){t.current=e},[e]),Hi(function(){return function(){for(var n=[],r=0;r0&&u>l&&(s=u-l>1)}i!==s&&o(s)}}),function(){return n.dispose()}}),i}function sF(e){var t=e.originalElement,n=e.containsFocus;t&&n&&t!==st()&&setTimeout(function(){var r;(r=t.focus)===null||r===void 0||r.call(t)},0)}function lF(e,t){var n=e.onRestoreFocus,r=n===void 0?sF:n,i=E.useRef(),o=E.useRef(!1);E.useEffect(function(){return i.current=xn().activeElement,xI(t.current)&&(o.current=!0),function(){var a;r==null||r({originalElement:i.current,containsFocus:o.current,documentContainsFocus:((a=xn())===null||a===void 0?void 0:a.hasFocus())||!1}),i.current=void 0}},[]),O1(t,"focus",E.useCallback(function(){o.current=!0},[]),!0),O1(t,"blur",E.useCallback(function(a){t.current&&a.relatedTarget&&!t.current.contains(a.relatedTarget)&&(o.current=!1)},[]),!0)}function uF(e,t){var n=String(e["aria-modal"]).toLowerCase()==="true"&&e.enableAriaHiddenSiblings;E.useEffect(function(){if(n&&t.current){var r=Q8(t.current);return r}},[t,n])}var s9=E.forwardRef(function(e,t){var n=lc({shouldRestoreFocus:!0,enableAriaHiddenSiblings:!0},e),r=E.useRef(),i=fo(r,t);uF(n,r),lF(n,r);var o=n.role,a=n.className,s=n.ariaLabel,l=n.ariaLabelledBy,u=n.ariaDescribedBy,c=n.style,f=n.children,d=n.onDismiss,h=aF(n,r),v=E.useCallback(function(C){switch(C.which){case le.escape:d&&(d(C),C.preventDefault(),C.stopPropagation());break}},[d]),g=Oh();return O1(g,"keydown",v),E.createElement("div",R({ref:i},ht(n,Cs),{className:a,role:o,"aria-label":s,"aria-labelledby":l,"aria-describedby":u,onKeyDown:v,style:R({overflowY:h?"scroll":void 0,outline:"none"},c)}),f)});s9.displayName="Popup";var Ms,cF="CalloutContentBase",fF=(Ms={},Ms[ce.top]=ll.slideUpIn10,Ms[ce.bottom]=ll.slideDownIn10,Ms[ce.left]=ll.slideLeftIn10,Ms[ce.right]=ll.slideRightIn10,Ms),dy={top:0,left:0},dF={opacity:0,filter:"opacity(0)",pointerEvents:"none"},hF=["role","aria-roledescription"],d_={preventDismissOnLostFocus:!1,preventDismissOnScroll:!1,preventDismissOnResize:!1,isBeakVisible:!0,beakWidth:16,gapSpace:0,minPagePadding:8,directionalHint:_t.bottomAutoEdge},pF=Rn({disableCaching:!0});function mF(e,t,n){var r=e.bounds,i=e.minPagePadding,o=i===void 0?d_.minPagePadding:i,a=e.target,s=E.useState(!1),l=s[0],u=s[1],c=E.useRef(),f=E.useCallback(function(){if(!c.current||l){var h=typeof r=="function"?n?r(a,n):void 0:r;!h&&n&&(h=rF(t.current,n),h={top:h.top+o,left:h.left+o,right:h.right-o,bottom:h.bottom-o,width:h.width-o*2,height:h.height-o*2}),c.current=h,l&&u(!1)}return c.current},[r,o,a,t,n,l]),d=Yl();return O1(n,"resize",d.debounce(function(){u(!0)},500,{leading:!0})),f}function gF(e,t,n){var r,i=e.calloutMaxHeight,o=e.finalHeight,a=e.directionalHint,s=e.directionalHintFixed,l=e.hidden,u=E.useState(),c=u[0],f=u[1],d=(r=n==null?void 0:n.elementPosition)!==null&&r!==void 0?r:{},h=d.top,v=d.bottom;return E.useEffect(function(){var g,C=(g=t())!==null&&g!==void 0?g:{},m=C.top,y=C.bottom,T;typeof h=="number"&&y?T=y-h:typeof v=="number"&&typeof m=="number"&&y&&(T=y-m-v),!i&&!l||i&&T&&i>T?f(T):f(i||void 0)},[v,i,o,a,s,t,l,n,h]),c}function vF(e,t,n,r,i){var o=E.useState(),a=o[0],s=o[1],l=E.useRef(0),u=E.useRef(),c=Yl(),f=e.hidden,d=e.target,h=e.finalHeight,v=e.calloutMaxHeight,g=e.onPositioned,C=e.directionalHint;return E.useEffect(function(){if(f)s(void 0),l.current=0;else{var m=c.requestAnimationFrame(function(){var y,T;if(t.current&&n){var S=R(R({},e),{target:r.current,bounds:i()}),w=n.cloneNode(!0);w.style.maxHeight=v?"".concat(v):"",w.style.visibility="hidden",(y=n.parentElement)===null||y===void 0||y.appendChild(w);var b=u.current===d?a:void 0,A=h?eF(S,t.current,w,b):JN(S,t.current,w,b);(T=n.parentElement)===null||T===void 0||T.removeChild(w),!a&&A||a&&A&&!CF(a,A)&&l.current<5?(l.current++,s(A)):l.current>0&&(l.current=0,g==null||g(a))}},n);return u.current=d,function(){c.cancelAnimationFrame(m),u.current=void 0}}},[f,C,c,n,v,t,r,h,i,g,a,e,d]),a}function EF(e,t,n){var r=e.hidden,i=e.setInitialFocus,o=Yl(),a=!!t;E.useEffect(function(){if(!r&&i&&a&&n){var s=o.requestAnimationFrame(function(){return II(n)},n);return function(){return o.cancelAnimationFrame(s)}}},[r,a,o,n,i])}function yF(e,t,n,r,i){var o=e.hidden,a=e.onDismiss,s=e.preventDismissOnScroll,l=e.preventDismissOnResize,u=e.preventDismissOnLostFocus,c=e.dismissOnTargetClick,f=e.shouldDismissOnWindowFocus,d=e.preventDismissOnEvent,h=E.useRef(!1),v=Yl(),g=Hi([function(){h.current=!0},function(){h.current=!1}]),C=!!t;return E.useEffect(function(){var m=function(A){C&&!s&&S(A)},y=function(A){!l&&!(d&&d(A))&&(a==null||a(A))},T=function(A){u||S(A)},S=function(A){var F=A.composedPath?A.composedPath():[],D=F.length>0?F[0]:A.target,I=n.current&&!$n(n.current,D);if(I&&h.current){h.current=!1;return}if(!r.current&&I||A.target!==i&&I&&(!r.current||"stopPropagation"in r.current||c||D!==r.current&&!$n(r.current,D))){if(d&&d(A))return;a==null||a(A)}},w=function(A){f&&(d&&!d(A)||!d&&!u)&&!(i!=null&&i.document.hasFocus())&&A.relatedTarget===null&&(a==null||a(A))},b=new Promise(function(A){v.setTimeout(function(){if(!o&&i){var F=[xi(i,"scroll",m,!0),xi(i,"resize",y,!0),xi(i.document.documentElement,"focus",T,!0),xi(i.document.documentElement,"click",T,!0),xi(i,"blur",w,!0)];A(function(){F.forEach(function(D){return D()})})}},0)});return function(){b.then(function(A){return A()})}},[o,v,n,r,i,a,f,c,u,l,s,C,d]),g}var h_=E.memo(E.forwardRef(function(e,t){var n=lc(d_,e),r=n.styles,i=n.style,o=n.ariaLabel,a=n.ariaDescribedBy,s=n.ariaLabelledBy,l=n.className,u=n.isBeakVisible,c=n.children,f=n.beakWidth,d=n.calloutWidth,h=n.calloutMaxWidth,v=n.calloutMinWidth,g=n.doNotLayer,C=n.finalHeight,m=n.hideOverflow,y=m===void 0?!!C:m,T=n.backgroundColor,S=n.calloutMaxHeight,w=n.onScroll,b=n.shouldRestoreFocus,A=b===void 0?!0:b,F=n.target,D=n.hidden,I=n.onLayerMounted,B=n.popupProps,M=E.useRef(null),oe=E.useState(null),V=oe[0],P=oe[1],z=E.useCallback(function(Ao){P(Ao)},[]),Z=fo(M,t),H=c_(n.target,{current:V}),U=H[0],_=H[1],ee=mF(n,U,_),X=vF(n,M,V,U,ee),x=gF(n,ee,X),ge=yF(n,X,M,U,_),xt=ge[0],pe=ge[1],it=(X==null?void 0:X.elementPosition.top)&&(X==null?void 0:X.elementPosition.bottom),Nt=R(R({},X==null?void 0:X.elementPosition),{maxHeight:x});if(it&&(Nt.bottom=void 0),EF(n,X,V),E.useEffect(function(){D||I==null||I()},[D]),!_)return null;var Bt=y,Ct=u&&!!F,mn=pF(r,{theme:n.theme,className:l,overflowYHidden:Bt,calloutWidth:d,positions:X,beakWidth:f,backgroundColor:T,calloutMaxWidth:h,calloutMinWidth:v,doNotLayer:g}),er=R(R({maxHeight:S||"100%"},i),Bt&&{overflowY:"hidden"}),di=n.hidden?{visibility:"hidden"}:void 0;return E.createElement("div",{ref:Z,className:mn.container,style:di},E.createElement("div",R({},ht(n,Cs,hF),{className:qn(mn.root,X&&X.targetEdge&&fF[X.targetEdge]),style:X?R({},Nt):dF,tabIndex:-1,ref:z}),Ct&&E.createElement("div",{className:mn.beak,style:TF(X)}),Ct&&E.createElement("div",{className:mn.beakCurtain}),E.createElement(s9,R({role:n.role,"aria-roledescription":n["aria-roledescription"],ariaDescribedBy:a,ariaLabel:o,ariaLabelledBy:s,className:mn.calloutMain,onDismiss:n.onDismiss,onMouseDown:xt,onMouseUp:pe,onRestoreFocus:n.onRestoreFocus,onScroll:w,shouldRestoreFocus:A,style:er},B),c)))}),function(e,t){return!t.shouldUpdateWhenHidden&&e.hidden&&t.hidden?!0:Zv(e,t)});function TF(e){var t,n,r=R(R({},(t=e==null?void 0:e.beakPosition)===null||t===void 0?void 0:t.elementPosition),{display:!((n=e==null?void 0:e.beakPosition)===null||n===void 0)&&n.hideBeak?"none":void 0});return!r.top&&!r.bottom&&!r.left&&!r.right&&(r.left=dy.left,r.top=dy.top),r}function CF(e,t){return hy(e.elementPosition,t.elementPosition)&&hy(e.beakPosition.elementPosition,t.beakPosition.elementPosition)}function hy(e,t){for(var n in t)if(t.hasOwnProperty(n)){var r=e[n],i=t[n];if(r!==void 0&&i!==void 0){if(r.toFixed(2)!==i.toFixed(2))return!1}else return!1}return!0}h_.displayName=cF;function SF(e){return{height:e,width:e}}var _F={container:"ms-Callout-container",root:"ms-Callout",beak:"ms-Callout-beak",beakCurtain:"ms-Callout-beakCurtain",calloutMain:"ms-Callout-main"},bF=function(e){var t,n=e.theme,r=e.className,i=e.overflowYHidden,o=e.calloutWidth,a=e.beakWidth,s=e.backgroundColor,l=e.calloutMaxWidth,u=e.calloutMinWidth,c=e.doNotLayer,f=Mn(_F,n),d=n.semanticColors,h=n.effects;return{container:[f.container,{position:"relative"}],root:[f.root,n.fonts.medium,{position:"absolute",display:"flex",zIndex:c?Pl.Layer:void 0,boxSizing:"border-box",borderRadius:h.roundedCorner2,boxShadow:h.elevation16,selectors:(t={},t[_e]={borderWidth:1,borderStyle:"solid",borderColor:"WindowText"},t)},SN(),r,!!o&&{width:o},!!l&&{maxWidth:l},!!u&&{minWidth:u}],beak:[f.beak,{position:"absolute",backgroundColor:d.menuBackground,boxShadow:"inherit",border:"inherit",boxSizing:"border-box",transform:"rotate(45deg)"},SF(a),s&&{backgroundColor:s}],beakCurtain:[f.beakCurtain,{position:"absolute",top:0,right:0,bottom:0,left:0,backgroundColor:d.menuBackground,borderRadius:h.roundedCorner2}],calloutMain:[f.calloutMain,{backgroundColor:d.menuBackground,overflowX:"hidden",overflowY:"auto",position:"relative",width:"100%",borderRadius:h.roundedCorner2},i&&{overflowY:"hidden"},s&&{backgroundColor:s}]}},AF=Dn(h_,bF,void 0,{scope:"CalloutContent"});const p_=E.createContext(void 0),wF=()=>()=>{};p_.Provider;function kF(){var e;return(e=E.useContext(p_))!==null&&e!==void 0?e:wF}var IF=Rn(),xF=It(function(e,t){return uc(R(R({},e),{rtl:t}))}),NF=function(e){var t=e.theme,n=e.dir,r=_n(t)?"rtl":"ltr",i=_n()?"rtl":"ltr",o=n||r;return{rootDir:o!==r||o!==i?o:n,needsTheme:o!==r}},m_=E.forwardRef(function(e,t){var n=e.className,r=e.theme,i=e.applyTheme,o=e.applyThemeToBody,a=e.styles,s=IF(a,{theme:r,applyTheme:i,className:n}),l=E.useRef(null);return RF(o,s,l),E.createElement(E.Fragment,null,FF(e,s,l,t))});m_.displayName="FabricBase";function FF(e,t,n,r){var i=t.root,o=e.as,a=o===void 0?"div":o,s=e.dir,l=e.theme,u=ht(e,Cs,["dir"]),c=NF(e),f=c.rootDir,d=c.needsTheme,h=E.createElement(q8,{providerRef:n},E.createElement(a,R({dir:f},u,{className:i,ref:fo(n,r)})));return d&&(h=E.createElement(YI,{settings:{theme:xF(l,s==="rtl")}},h)),h}function RF(e,t,n){var r=t.bodyThemed;return E.useEffect(function(){if(e){var i=xn(n.current);if(i)return i.body.classList.add(r),function(){i.body.classList.remove(r)}}},[r,e,n]),n}var im={fontFamily:"inherit"},DF={root:"ms-Fabric",bodyThemed:"ms-Fabric-bodyThemed"},MF=function(e){var t=e.applyTheme,n=e.className,r=e.preventBlanketFontInheritance,i=e.theme,o=Mn(DF,i);return{root:[o.root,i.fonts.medium,{color:i.palette.neutralPrimary},!r&&{"& button":im,"& input":im,"& textarea":im},t&&{color:i.semanticColors.bodyText,backgroundColor:i.semanticColors.bodyBackground},n],bodyThemed:[{backgroundColor:i.semanticColors.bodyBackground}]}},PF=Dn(m_,MF,void 0,{scope:"Fabric"}),Xu={},l9={},g_="fluent-default-layer-host",OF="#".concat(g_);function LF(e,t){Xu[e]||(Xu[e]=[]),Xu[e].push(t);var n=l9[e];if(n)for(var r=0,i=n;r=0&&(n.splice(r,1),n.length===0&&delete Xu[e])}var i=l9[e];if(i)for(var o=0,a=i;o0&&t.current.naturalHeight>0||t.current.complete&&ZF.test(o):!1;f&&l(Sn.loaded)}}),E.useEffect(function(){n==null||n(s)},[s]);var u=E.useCallback(function(f){r==null||r(f),o&&l(Sn.loaded)},[o,r]),c=E.useCallback(function(f){i==null||i(f),l(Sn.error)},[i]);return[s,u,c]}var T_=E.forwardRef(function(e,t){var n=E.useRef(),r=E.useRef(),i=eR(e,r),o=i[0],a=i[1],s=i[2],l=ht(e,sx,["width","height"]),u=e.src,c=e.alt,f=e.width,d=e.height,h=e.shouldFadeIn,v=h===void 0?!0:h,g=e.shouldStartVisible,C=e.className,m=e.imageFit,y=e.role,T=e.maximizeFrame,S=e.styles,w=e.theme,b=e.loading,A=tR(e,o,r,n),F=XF(S,{theme:w,className:C,width:f,height:d,maximizeFrame:T,shouldFadeIn:v,shouldStartVisible:g,isLoaded:o===Sn.loaded||o===Sn.notLoaded&&e.shouldStartVisible,isLandscape:A===L1.landscape,isCenter:m===Gn.center,isCenterContain:m===Gn.centerContain,isCenterCover:m===Gn.centerCover,isContain:m===Gn.contain,isCover:m===Gn.cover,isNone:m===Gn.none,isError:o===Sn.error,isNotImageFit:m===void 0});return E.createElement("div",{className:F.root,style:{width:f,height:d},ref:n},E.createElement("img",R({},l,{onLoad:a,onError:s,key:JF+e.src||"",className:F.image,ref:fo(r,t),src:u,alt:c,role:y,loading:b})))});T_.displayName="ImageBase";function tR(e,t,n,r){var i=E.useRef(t),o=E.useRef();return(o===void 0||i.current===Sn.notLoaded&&t===Sn.loaded)&&(o.current=nR(e,t,n,r)),i.current=t,o.current}function nR(e,t,n,r){var i=e.imageFit,o=e.width,a=e.height;if(e.coverStyle!==void 0)return e.coverStyle;if(t===Sn.loaded&&(i===Gn.cover||i===Gn.contain||i===Gn.centerContain||i===Gn.centerCover)&&n.current&&r.current){var s=void 0;typeof o=="number"&&typeof a=="number"&&i!==Gn.centerContain&&i!==Gn.centerCover?s=o/a:s=r.current.clientWidth/r.current.clientHeight;var l=n.current.naturalWidth/n.current.naturalHeight;if(l>s)return L1.landscape}return L1.portrait}var rR={root:"ms-Image",rootMaximizeFrame:"ms-Image--maximizeFrame",image:"ms-Image-image",imageCenter:"ms-Image-image--center",imageContain:"ms-Image-image--contain",imageCover:"ms-Image-image--cover",imageCenterContain:"ms-Image-image--centerContain",imageCenterCover:"ms-Image-image--centerCover",imageNone:"ms-Image-image--none",imageLandscape:"ms-Image-image--landscape",imagePortrait:"ms-Image-image--portrait"},iR=function(e){var t=e.className,n=e.width,r=e.height,i=e.maximizeFrame,o=e.isLoaded,a=e.shouldFadeIn,s=e.shouldStartVisible,l=e.isLandscape,u=e.isCenter,c=e.isContain,f=e.isCover,d=e.isCenterContain,h=e.isCenterCover,v=e.isNone,g=e.isError,C=e.isNotImageFit,m=e.theme,y=Mn(rR,m),T={position:"absolute",left:"50% /* @noflip */",top:"50%",transform:"translate(-50%,-50%)"},S=st(),w=S!==void 0&&S.navigator.msMaxTouchPoints===void 0,b=c&&l||f&&!l?{width:"100%",height:"auto"}:{width:"auto",height:"100%"};return{root:[y.root,m.fonts.medium,{overflow:"hidden"},i&&[y.rootMaximizeFrame,{height:"100%",width:"100%"}],o&&a&&!s&&ll.fadeIn400,(u||c||f||d||h)&&{position:"relative"},t],image:[y.image,{display:"block",opacity:0},o&&["is-loaded",{opacity:1}],u&&[y.imageCenter,T],c&&[y.imageContain,w&&{width:"100%",height:"100%",objectFit:"contain"},!w&&b,!w&&T],f&&[y.imageCover,w&&{width:"100%",height:"100%",objectFit:"cover"},!w&&b,!w&&T],d&&[y.imageCenterContain,l&&{maxWidth:"100%"},!l&&{maxHeight:"100%"},T],h&&[y.imageCenterCover,l&&{maxHeight:"100%"},!l&&{maxWidth:"100%"},T],v&&[y.imageNone,{width:"auto",height:"auto"}],C&&[!!n&&!r&&{height:"auto",width:"100%"},!n&&!!r&&{height:"100%",width:"auto"},!!n&&!!r&&{height:"100%",width:"100%"}],l&&y.imageLandscape,!l&&y.imagePortrait,!o&&"is-notLoaded",a&&"is-fadeIn",g&&"is-error"]}},u9=Dn(T_,iR,void 0,{scope:"Image"},!0);u9.displayName="Image";var is=oc({root:{display:"inline-block"},placeholder:["ms-Icon-placeHolder",{width:"1em"}],image:["ms-Icon-imageContainer",{overflow:"hidden"}]}),C_="ms-Icon",oR=function(e){var t=e.className,n=e.iconClassName,r=e.isPlaceholder,i=e.isImage,o=e.styles;return{root:[r&&is.placeholder,is.root,i&&is.image,n,t,o&&o.root,o&&o.imageContainer]}},S_=It(function(e){var t=Ix(e)||{subset:{},code:void 0},n=t.code,r=t.subset;return n?{children:n,iconClassName:r.className,fontFamily:r.fontFace&&r.fontFace.fontFamily,mergeImageProps:r.mergeImageProps}:null},void 0,!0),Yd=function(e){var t=e.iconName,n=e.className,r=e.style,i=r===void 0?{}:r,o=S_(t)||{},a=o.iconClassName,s=o.children,l=o.fontFamily,u=o.mergeImageProps,c=ht(e,pt),f=e["aria-label"]||e.title,d=e["aria-label"]||e["aria-labelledby"]||e.title?{role:u?void 0:"img"}:{"aria-hidden":!0},h=s;return u&&typeof s=="object"&&typeof s.props=="object"&&f&&(h=E.cloneElement(s,{alt:f})),E.createElement("i",R({"data-icon-name":t},d,c,u?{title:void 0,"aria-label":void 0}:{},{className:qn(C_,is.root,a,!t&&is.placeholder,n),style:R({fontFamily:l},i)}),h)};It(function(e,t,n){return Yd({iconName:e,className:t,"aria-label":n})});var aR=Rn({cacheSize:100}),sR=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return r._onImageLoadingStateChange=function(i){r.props.imageProps&&r.props.imageProps.onLoadingStateChange&&r.props.imageProps.onLoadingStateChange(i),i===Sn.error&&r.setState({imageLoadError:!0})},r.state={imageLoadError:!1},r}return t.prototype.render=function(){var n=this.props,r=n.children,i=n.className,o=n.styles,a=n.iconName,s=n.imageErrorAs,l=n.theme,u=typeof a=="string"&&a.length===0,c=!!this.props.imageProps||this.props.iconType===qd.image||this.props.iconType===qd.Image,f=S_(a)||{},d=f.iconClassName,h=f.children,v=f.mergeImageProps,g=aR(o,{theme:l,className:i,iconClassName:d,isImage:c,isPlaceholder:u}),C=c?"span":"i",m=ht(this.props,pt,["aria-label"]),y=this.state.imageLoadError,T=R(R({},this.props.imageProps),{onLoadingStateChange:this._onImageLoadingStateChange}),S=y&&s||u9,w=this.props["aria-label"]||this.props.ariaLabel,b=T.alt||w||this.props.title,A=!!(b||this.props["aria-labelledby"]||T["aria-label"]||T["aria-labelledby"]),F=A?{role:c||v?void 0:"img","aria-label":c||v?void 0:b}:{"aria-hidden":!0},D=h;return v&&h&&typeof h=="object"&&b&&(D=E.cloneElement(h,{alt:b})),E.createElement(C,R({"data-icon-name":a},F,m,v?{title:void 0,"aria-label":void 0}:{},{className:g.root}),c?E.createElement(S,R({},T)):r||D)},t}(E.Component),ho=Dn(sR,oR,void 0,{scope:"Icon"},!0);ho.displayName="Icon";var lR=function(e){var t=e.className,n=e.imageProps,r=ht(e,pt,["aria-label","aria-labelledby","title","aria-describedby"]),i=n.alt||e["aria-label"],o=i||e["aria-labelledby"]||e.title||n["aria-label"]||n["aria-labelledby"]||n.title,a={"aria-labelledby":e["aria-labelledby"],"aria-describedby":e["aria-describedby"],title:e.title},s=o?{}:{"aria-hidden":!0};return E.createElement("div",R({},s,r,{className:qn(C_,is.root,is.image,t)}),E.createElement(u9,R({},a,n,{alt:o?i:""})))},cg={none:0,all:1,inputOnly:2},yn;(function(e){e[e.vertical=0]="vertical",e[e.horizontal=1]="horizontal",e[e.bidirectional=2]="bidirectional",e[e.domOrder=3]="domOrder"})(yn||(yn={}));var lf="data-is-focusable",uR="data-disable-click-on-enter",om="data-focuszone-id",Ei="tabindex",am="data-no-vertical-wrap",sm="data-no-horizontal-wrap",lm=999999999,yu=-999999999,um,cR="ms-FocusZone";function fR(e,t){var n;typeof MouseEvent=="function"?n=new MouseEvent("click",{ctrlKey:t==null?void 0:t.ctrlKey,metaKey:t==null?void 0:t.metaKey,shiftKey:t==null?void 0:t.shiftKey,altKey:t==null?void 0:t.altKey,bubbles:t==null?void 0:t.bubbles,cancelable:t==null?void 0:t.cancelable}):(n=document.createEvent("MouseEvents"),n.initMouseEvent("click",t?t.bubbles:!1,t?t.cancelable:!1,window,0,0,0,0,0,t?t.ctrlKey:!1,t?t.altKey:!1,t?t.shiftKey:!1,t?t.metaKey:!1,0,null)),e.dispatchEvent(n)}function dR(){return um||(um=Ii({selectors:{":focus":{outline:"none"}}},cR)),um}var Tu={},uf=new Set,hR=["text","number","password","email","tel","url","search","textarea"],Wi=!1,pR=function(e){yt(t,e);function t(n){var r=this,i,o,a,s;r=e.call(this,n)||this,r._root=E.createRef(),r._mergedRef=X8(),r._onFocus=function(u){if(!r._portalContainsElement(u.target)){var c=r.props,f=c.onActiveElementChanged,d=c.doNotAllowFocusEventToPropagate,h=c.stopFocusPropagation,v=c.onFocusNotification,g=c.onFocus,C=c.shouldFocusInnerElementWhenReceivedFocus,m=c.defaultTabbableElement,y=r._isImmediateDescendantOfZone(u.target),T;if(y)T=u.target;else for(var S=u.target;S&&S!==r._root.current;){if(Ai(S)&&r._isImmediateDescendantOfZone(S)){T=S;break}S=bi(S,Wi)}if(C&&u.target===r._root.current){var w=m&&typeof m=="function"&&r._root.current&&m(r._root.current);w&&Ai(w)?(T=w,w.focus()):(r.focus(!0),r._activeElement&&(T=null))}var b=!r._activeElement;T&&T!==r._activeElement&&((y||b)&&r._setFocusAlignment(T,!0,!0),r._activeElement=T,b&&r._updateTabIndexes()),f&&f(r._activeElement,u),(h||d)&&u.stopPropagation(),g?g(u):v&&v()}},r._onBlur=function(){r._setParkedFocus(!1)},r._onMouseDown=function(u){if(!r._portalContainsElement(u.target)){var c=r.props.disabled;if(!c){for(var f=u.target,d=[];f&&f!==r._root.current;)d.push(f),f=bi(f,Wi);for(;d.length&&(f=d.pop(),f&&Ai(f)&&r._setActiveElement(f,!0),!eo(f)););}}},r._onKeyDown=function(u,c){if(!r._portalContainsElement(u.target)){var f=r.props,d=f.direction,h=f.disabled,v=f.isInnerZoneKeystroke,g=f.pagingSupportDisabled,C=f.shouldEnterInnerZone;if(!h&&(r.props.onKeyDown&&r.props.onKeyDown(u),!u.isDefaultPrevented()&&!(r._getDocument().activeElement===r._root.current&&r._isInnerZone))){if((C&&C(u)||v&&v(u))&&r._isImmediateDescendantOfZone(u.target)){var m=r._getFirstInnerZone();if(m){if(!m.focus(!0))return}else if(n9(u.target)){if(!r.focusElement(Cn(u.target,u.target.firstChild,!0)))return}else return}else{if(u.altKey)return;switch(u.which){case le.space:if(r._shouldRaiseClicksOnSpace&&r._tryInvokeClickForFocusable(u.target,u))break;return;case le.left:if(d!==yn.vertical&&(r._preventDefaultWhenHandled(u),r._moveFocusLeft(c)))break;return;case le.right:if(d!==yn.vertical&&(r._preventDefaultWhenHandled(u),r._moveFocusRight(c)))break;return;case le.up:if(d!==yn.horizontal&&(r._preventDefaultWhenHandled(u),r._moveFocusUp()))break;return;case le.down:if(d!==yn.horizontal&&(r._preventDefaultWhenHandled(u),r._moveFocusDown()))break;return;case le.pageDown:if(!g&&r._moveFocusPaging(!0))break;return;case le.pageUp:if(!g&&r._moveFocusPaging(!1))break;return;case le.tab:if(r.props.allowTabKey||r.props.handleTabKey===cg.all||r.props.handleTabKey===cg.inputOnly&&r._isElementInput(u.target)){var y=!1;if(r._processingTabKey=!0,d===yn.vertical||!r._shouldWrapFocus(r._activeElement,sm))y=u.shiftKey?r._moveFocusUp():r._moveFocusDown();else{var T=_n(c)?!u.shiftKey:u.shiftKey;y=T?r._moveFocusLeft(c):r._moveFocusRight(c)}if(r._processingTabKey=!1,y)break;r.props.shouldResetActiveElementWhenTabFromZone&&(r._activeElement=null)}return;case le.home:if(r._isContentEditableElement(u.target)||r._isElementInput(u.target)&&!r._shouldInputLoseFocus(u.target,!1))return!1;var S=r._root.current&&r._root.current.firstChild;if(r._root.current&&S&&r.focusElement(Cn(r._root.current,S,!0)))break;return;case le.end:if(r._isContentEditableElement(u.target)||r._isElementInput(u.target)&&!r._shouldInputLoseFocus(u.target,!0))return!1;var w=r._root.current&&r._root.current.lastChild;if(r._root.current&&r.focusElement(jn(r._root.current,w,!0,!0,!0)))break;return;case le.enter:if(r._shouldRaiseClicksOnEnter&&r._tryInvokeClickForFocusable(u.target,u))break;return;default:return}}u.preventDefault(),u.stopPropagation()}}},r._getHorizontalDistanceFromCenter=function(u,c,f){var d=r._focusAlignment.left||r._focusAlignment.x||0,h=Math.floor(f.top),v=Math.floor(c.bottom),g=Math.floor(f.bottom),C=Math.floor(c.top),m=u&&h>v,y=!u&&g=f.left&&d<=f.left+f.width?0:Math.abs(f.left+f.width/2-d):r._shouldWrapFocus(r._activeElement,am)?lm:yu},Aa(r),r._id=bn("FocusZone"),r._focusAlignment={left:0,top:0},r._processingTabKey=!1;var l=(o=(i=n.shouldRaiseClicks)!==null&&i!==void 0?i:t.defaultProps.shouldRaiseClicks)!==null&&o!==void 0?o:!0;return r._shouldRaiseClicksOnEnter=(a=n.shouldRaiseClicksOnEnter)!==null&&a!==void 0?a:l,r._shouldRaiseClicksOnSpace=(s=n.shouldRaiseClicksOnSpace)!==null&&s!==void 0?s:l,r}return t.getOuterZones=function(){return uf.size},t._onKeyDownCapture=function(n){n.which===le.tab&&uf.forEach(function(r){return r._updateTabIndexes()})},t.prototype.componentDidMount=function(){var n=this._root.current;if(Tu[this._id]=this,n){for(var r=bi(n,Wi);r&&r!==this._getDocument().body&&r.nodeType===1;){if(eo(r)){this._isInnerZone=!0;break}r=bi(r,Wi)}this._isInnerZone||(uf.add(this),this._root.current&&this._root.current.addEventListener("keydown",t._onKeyDownCapture,!0)),this._root.current&&this._root.current.addEventListener("blur",this._onBlur,!0),this._updateTabIndexes(),this.props.defaultTabbableElement&&typeof this.props.defaultTabbableElement=="string"?this._activeElement=this._getDocument().querySelector(this.props.defaultTabbableElement):this.props.defaultActiveElement&&(this._activeElement=this._getDocument().querySelector(this.props.defaultActiveElement)),this.props.shouldFocusOnMount&&this.focus()}},t.prototype.componentDidUpdate=function(){var n=this._root.current,r=this._getDocument();if((this._activeElement&&!$n(this._root.current,this._activeElement,Wi)||this._defaultFocusElement&&!$n(this._root.current,this._defaultFocusElement,Wi))&&(this._activeElement=null,this._defaultFocusElement=null,this._updateTabIndexes()),!this.props.preventFocusRestoration&&r&&this._lastIndexPath&&(r.activeElement===r.body||r.activeElement===null||r.activeElement===n)){var i=NI(n,this._lastIndexPath);i?(this._setActiveElement(i,!0),i.focus(),this._setParkedFocus(!1)):this._setParkedFocus(!0)}},t.prototype.componentWillUnmount=function(){delete Tu[this._id],this._isInnerZone||(uf.delete(this),this._root.current&&this._root.current.removeEventListener("keydown",t._onKeyDownCapture,!0)),this._root.current&&this._root.current.removeEventListener("blur",this._onBlur,!0),this._activeElement=null,this._defaultFocusElement=null},t.prototype.render=function(){var n=this,r=this.props,i=r.as,o=r.elementType,a=r.rootProps,s=r.ariaDescribedBy,l=r.ariaLabelledBy,u=r.className,c=ht(this.props,pt),f=i||o||"div";this._evaluateFocusBeforeRender();var d=FN();return E.createElement(f,R({"aria-labelledby":l,"aria-describedby":s},c,a,{className:qn(dR(),u),ref:this._mergedRef(this.props.elementRef,this._root),"data-focuszone-id":this._id,onKeyDown:function(h){return n._onKeyDown(h,d)},onFocus:this._onFocus,onMouseDownCapture:this._onMouseDown}),this.props.children)},t.prototype.focus=function(n,r){if(n===void 0&&(n=!1),r===void 0&&(r=!1),this._root.current)if(!n&&this._root.current.getAttribute(lf)==="true"&&this._isInnerZone){var i=this._getOwnerZone(this._root.current);if(i!==this._root.current){var o=Tu[i.getAttribute(om)];return!!o&&o.focusElement(this._root.current)}return!1}else{if(!n&&this._activeElement&&$n(this._root.current,this._activeElement)&&Ai(this._activeElement)&&(!r||O8(this._activeElement)))return this._activeElement.focus(),!0;var a=this._root.current.firstChild;return this.focusElement(Cn(this._root.current,a,!0,void 0,void 0,void 0,void 0,void 0,r))}return!1},t.prototype.focusLast=function(){if(this._root.current){var n=this._root.current&&this._root.current.lastChild;return this.focusElement(jn(this._root.current,n,!0,!0,!0))}return!1},t.prototype.focusElement=function(n,r){var i=this.props,o=i.onBeforeFocus,a=i.shouldReceiveFocus;return a&&!a(n)||o&&!o(n)?!1:n?(this._setActiveElement(n,r),this._activeElement&&this._activeElement.focus(),!0):!1},t.prototype.setFocusAlignment=function(n){this._focusAlignment=n},Object.defineProperty(t.prototype,"defaultFocusElement",{get:function(){return this._defaultFocusElement},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"activeElement",{get:function(){return this._activeElement},enumerable:!1,configurable:!0}),t.prototype._evaluateFocusBeforeRender=function(){var n=this._root.current,r=this._getDocument();if(r){var i=r.activeElement;if(i!==n){var o=$n(n,i,!1);this._lastIndexPath=o?FI(n,i):void 0}}},t.prototype._setParkedFocus=function(n){var r=this._root.current;r&&this._isParked!==n&&(this._isParked=n,n?(this.props.allowFocusRoot||(this._parkedTabIndex=r.getAttribute("tabindex"),r.setAttribute("tabindex","-1")),r.focus()):this.props.allowFocusRoot||(this._parkedTabIndex?(r.setAttribute("tabindex",this._parkedTabIndex),this._parkedTabIndex=void 0):r.removeAttribute("tabindex")))},t.prototype._setActiveElement=function(n,r){var i=this._activeElement;this._activeElement=n,i&&(eo(i)&&this._updateTabIndexes(i),i.tabIndex=-1),this._activeElement&&((!this._focusAlignment||r)&&this._setFocusAlignment(n,!0,!0),this._activeElement.tabIndex=0)},t.prototype._preventDefaultWhenHandled=function(n){this.props.preventDefaultWhenHandled&&n.preventDefault()},t.prototype._tryInvokeClickForFocusable=function(n,r){var i=n;if(i===this._root.current)return!1;do{if(i.tagName==="BUTTON"||i.tagName==="A"||i.tagName==="INPUT"||i.tagName==="TEXTAREA"||i.tagName==="SUMMARY")return!1;if(this._isImmediateDescendantOfZone(i)&&i.getAttribute(lf)==="true"&&i.getAttribute(uR)!=="true")return fR(i,r),!0;i=bi(i,Wi)}while(i!==this._root.current);return!1},t.prototype._getFirstInnerZone=function(n){if(n=n||this._activeElement||this._root.current,!n)return null;if(eo(n))return Tu[n.getAttribute(om)];for(var r=n.firstElementChild;r;){if(eo(r))return Tu[r.getAttribute(om)];var i=this._getFirstInnerZone(r);if(i)return i;r=r.nextElementSibling}return null},t.prototype._moveFocus=function(n,r,i,o){o===void 0&&(o=!0);var a=this._activeElement,s=-1,l=void 0,u=!1,c=this.props.direction===yn.bidirectional;if(!a||!this._root.current||this._isElementInput(a)&&!this._shouldInputLoseFocus(a,n))return!1;var f=c?a.getBoundingClientRect():null;do if(a=n?Cn(this._root.current,a):jn(this._root.current,a),c){if(a){var d=a.getBoundingClientRect(),h=r(f,d);if(h===-1&&s===-1){l=a;break}if(h>-1&&(s===-1||h=0&&h<0)break}}else{l=a;break}while(a);if(l&&l!==this._activeElement)u=!0,this.focusElement(l);else if(this.props.isCircularNavigation&&o)return n?this.focusElement(Cn(this._root.current,this._root.current.firstElementChild,!0)):this.focusElement(jn(this._root.current,this._root.current.lastElementChild,!0,!0,!0));return u},t.prototype._moveFocusDown=function(){var n=this,r=-1,i=this._focusAlignment.left||this._focusAlignment.x||0;return this._moveFocus(!0,function(o,a){var s=-1,l=Math.floor(a.top),u=Math.floor(o.bottom);return l=u||l===r)&&(r=l,i>=a.left&&i<=a.left+a.width?s=0:s=Math.abs(a.left+a.width/2-i)),s)})?(this._setFocusAlignment(this._activeElement,!1,!0),!0):!1},t.prototype._moveFocusUp=function(){var n=this,r=-1,i=this._focusAlignment.left||this._focusAlignment.x||0;return this._moveFocus(!1,function(o,a){var s=-1,l=Math.floor(a.bottom),u=Math.floor(a.top),c=Math.floor(o.top);return l>c?n._shouldWrapFocus(n._activeElement,am)?lm:yu:((r===-1&&l<=c||u===r)&&(r=u,i>=a.left&&i<=a.left+a.width?s=0:s=Math.abs(a.left+a.width/2-i)),s)})?(this._setFocusAlignment(this._activeElement,!1,!0),!0):!1},t.prototype._moveFocusLeft=function(n){var r=this,i=this._shouldWrapFocus(this._activeElement,sm);return this._moveFocus(_n(n),function(o,a){var s=-1,l;return _n(n)?l=parseFloat(a.top.toFixed(3))parseFloat(o.top.toFixed(3)),l&&a.right<=o.right&&r.props.direction!==yn.vertical?s=o.right-a.right:i||(s=yu),s},void 0,i)?(this._setFocusAlignment(this._activeElement,!0,!1),!0):!1},t.prototype._moveFocusRight=function(n){var r=this,i=this._shouldWrapFocus(this._activeElement,sm);return this._moveFocus(!_n(n),function(o,a){var s=-1,l;return _n(n)?l=parseFloat(a.bottom.toFixed(3))>parseFloat(o.top.toFixed(3)):l=parseFloat(a.top.toFixed(3))=o.left&&r.props.direction!==yn.vertical?s=a.left-o.left:i||(s=yu),s},void 0,i)?(this._setFocusAlignment(this._activeElement,!0,!1),!0):!1},t.prototype._moveFocusPaging=function(n,r){r===void 0&&(r=!0);var i=this._activeElement;if(!i||!this._root.current||this._isElementInput(i)&&!this._shouldInputLoseFocus(i,n))return!1;var o=N8(i);if(!o)return!1;var a=-1,s=void 0,l=-1,u=-1,c=o.clientHeight,f=i.getBoundingClientRect();do if(i=n?Cn(this._root.current,i):jn(this._root.current,i),i){var d=i.getBoundingClientRect(),h=Math.floor(d.top),v=Math.floor(f.bottom),g=Math.floor(d.bottom),C=Math.floor(f.top),m=this._getHorizontalDistanceFromCenter(n,f,d),y=n&&h>v+c,T=!n&&g-1&&(n&&h>l?(l=h,a=m,s=i):!n&&g-1){var i=n.selectionStart,o=n.selectionEnd,a=i!==o,s=n.value,l=n.readOnly;if(a||i>0&&!r&&!l||i!==s.length&&r&&!l||this.props.handleTabKey&&!(this.props.shouldInputLoseFocusOnArrowKey&&this.props.shouldInputLoseFocusOnArrowKey(n)))return!1}return!0},t.prototype._shouldWrapFocus=function(n,r){return this.props.checkForNoWrap?L8(n,r):!0},t.prototype._portalContainsElement=function(n){return n&&!!this._root.current&&P8(n,this._root.current)},t.prototype._getDocument=function(){return xn(this._root.current)},t.defaultProps={isCircularNavigation:!1,direction:yn.bidirectional,shouldRaiseClicks:!0},t}(E.Component),sn;(function(e){e[e.Normal=0]="Normal",e[e.Divider=1]="Divider",e[e.Header=2]="Header",e[e.Section=3]="Section"})(sn||(sn={}));function Ll(e){return e.canCheck?!!(e.isChecked||e.checked):typeof e.isChecked=="boolean"?e.isChecked:typeof e.checked=="boolean"?e.checked:null}function po(e){return!!(e.subMenuProps||e.items)}function Di(e){return!!(e.isDisabled||e.disabled)}function __(e){var t=Ll(e),n=t!==null;return n?"menuitemcheckbox":"menuitem"}var py=function(e){var t=e.item,n=e.classNames,r=t.iconProps;return E.createElement(ho,R({},r,{className:n.icon}))},mR=function(e){var t=e.item,n=e.hasIcons;return n?t.onRenderIcon?t.onRenderIcon(e,py):py(e):null},gR=function(e){var t=e.onCheckmarkClick,n=e.item,r=e.classNames,i=Ll(n);if(t){var o=function(a){return t(n,a)};return E.createElement(ho,{iconName:n.canCheck!==!1&&i?"CheckMark":"",className:r.checkmarkIcon,onClick:o})}return null},vR=function(e){var t=e.item,n=e.classNames;return t.text||t.name?E.createElement("span",{className:n.label},t.text||t.name):null},ER=function(e){var t=e.item,n=e.classNames;return t.secondaryText?E.createElement("span",{className:n.secondaryText},t.secondaryText):null},yR=function(e){var t=e.item,n=e.classNames,r=e.theme;return po(t)?E.createElement(ho,R({iconName:_n(r)?"ChevronLeft":"ChevronRight"},t.submenuIconProps,{className:n.subMenuIcon})):null},TR=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return r.openSubMenu=function(){var i=r.props,o=i.item,a=i.openSubMenu,s=i.getSubmenuTarget;if(s){var l=s();po(o)&&a&&l&&a(o,l)}},r.dismissSubMenu=function(){var i=r.props,o=i.item,a=i.dismissSubMenu;po(o)&&a&&a()},r.dismissMenu=function(i){var o=r.props.dismissMenu;o&&o(void 0,i)},Aa(r),r}return t.prototype.render=function(){var n=this.props,r=n.item,i=n.classNames,o=r.onRenderContent||this._renderLayout;return E.createElement("div",{className:r.split?i.linkContentMenu:i.linkContent},o(this.props,{renderCheckMarkIcon:gR,renderItemIcon:mR,renderItemName:vR,renderSecondaryText:ER,renderSubMenuIcon:yR}))},t.prototype._renderLayout=function(n,r){return E.createElement(E.Fragment,null,r.renderCheckMarkIcon(n),r.renderItemIcon(n),r.renderItemName(n),r.renderSecondaryText(n),r.renderSubMenuIcon(n))},t}(E.Component),CR=It(function(e){return oc({wrapper:{display:"inline-flex",height:"100%",alignItems:"center"},divider:{width:1,height:"100%",backgroundColor:e.palette.neutralTertiaryAlt}})}),Vo=36,my=t_(0,e_),SR=It(function(e){var t,n,r,i,o,a=e.semanticColors,s=e.fonts,l=e.palette,u=a.menuItemBackgroundHovered,c=a.menuItemTextHovered,f=a.menuItemBackgroundPressed,d=a.bodyDivider,h={item:[s.medium,{color:a.bodyText,position:"relative",boxSizing:"border-box"}],divider:{display:"block",height:"1px",backgroundColor:d,position:"relative"},root:[fs(e),s.medium,{color:a.bodyText,backgroundColor:"transparent",border:"none",width:"100%",height:Vo,lineHeight:Vo,display:"block",cursor:"pointer",padding:"0px 8px 0 4px",textAlign:"left"}],rootDisabled:{color:a.disabledBodyText,cursor:"default",pointerEvents:"none",selectors:(t={},t[_e]={color:"GrayText",opacity:1},t)},rootHovered:{backgroundColor:u,color:c,selectors:{".ms-ContextualMenu-icon":{color:l.themeDarkAlt},".ms-ContextualMenu-submenuIcon":{color:l.neutralPrimary}}},rootFocused:{backgroundColor:l.white},rootChecked:{selectors:{".ms-ContextualMenu-checkmarkIcon":{color:l.neutralPrimary}}},rootPressed:{backgroundColor:f,selectors:{".ms-ContextualMenu-icon":{color:l.themeDark},".ms-ContextualMenu-submenuIcon":{color:l.neutralPrimary}}},rootExpanded:{backgroundColor:f,color:a.bodyTextChecked,selectors:(n={".ms-ContextualMenu-submenuIcon":(r={},r[_e]={color:"inherit"},r)},n[_e]=R({},Ri()),n)},linkContent:{whiteSpace:"nowrap",height:"inherit",display:"flex",alignItems:"center",maxWidth:"100%"},anchorLink:{padding:"0px 8px 0 4px",textRendering:"auto",color:"inherit",letterSpacing:"normal",wordSpacing:"normal",textTransform:"none",textIndent:"0px",textShadow:"none",textDecoration:"none",boxSizing:"border-box"},label:{margin:"0 4px",verticalAlign:"middle",display:"inline-block",flexGrow:"1",textOverflow:"ellipsis",whiteSpace:"nowrap",overflow:"hidden"},secondaryText:{color:e.palette.neutralSecondary,paddingLeft:"20px",textAlign:"right"},icon:{display:"inline-block",minHeight:"1px",maxHeight:Vo,fontSize:Zr.medium,width:Zr.medium,margin:"0 4px",verticalAlign:"middle",flexShrink:"0",selectors:(i={},i[my]={fontSize:Zr.large,width:Zr.large},i)},iconColor:{color:a.menuIcon},iconDisabled:{color:a.disabledBodyText},checkmarkIcon:{color:a.bodySubtext},subMenuIcon:{height:Vo,lineHeight:Vo,color:l.neutralSecondary,textAlign:"center",display:"inline-block",verticalAlign:"middle",flexShrink:"0",fontSize:Zr.small,selectors:(o={":hover":{color:l.neutralPrimary},":active":{color:l.neutralPrimary}},o[my]={fontSize:Zr.medium},o)},splitButtonFlexContainer:[fs(e),{display:"flex",height:Vo,flexWrap:"nowrap",justifyContent:"center",alignItems:"flex-start"}]};return ba(h)}),gy="28px",_R=t_(0,e_),bR=It(function(e){var t;return oc(CR(e),{wrapper:{position:"absolute",right:28,selectors:(t={},t[_R]={right:32},t)},divider:{height:16,width:1}})}),AR={item:"ms-ContextualMenu-item",divider:"ms-ContextualMenu-divider",root:"ms-ContextualMenu-link",isChecked:"is-checked",isExpanded:"is-expanded",isDisabled:"is-disabled",linkContent:"ms-ContextualMenu-linkContent",linkContentMenu:"ms-ContextualMenu-linkContent",icon:"ms-ContextualMenu-icon",iconColor:"ms-ContextualMenu-iconColor",checkmarkIcon:"ms-ContextualMenu-checkmarkIcon",subMenuIcon:"ms-ContextualMenu-submenuIcon",label:"ms-ContextualMenu-itemText",secondaryText:"ms-ContextualMenu-secondaryText",splitMenu:"ms-ContextualMenu-splitMenu",screenReaderText:"ms-ContextualMenu-screenReaderText"},wR=It(function(e,t,n,r,i,o,a,s,l,u,c,f){var d,h,v,g,C=SR(e),m=Mn(AR,e);return oc({item:[m.item,C.item,a],divider:[m.divider,C.divider,s],root:[m.root,C.root,r&&[m.isChecked,C.rootChecked],i&&C.anchorLink,n&&[m.isExpanded,C.rootExpanded],t&&[m.isDisabled,C.rootDisabled],!t&&!n&&[{selectors:(d={":hover":C.rootHovered,":active":C.rootPressed},d[".".concat(an," &:focus, .").concat(an," &:focus:hover")]=C.rootFocused,d[".".concat(an," &:hover")]={background:"inherit;"},d)}],f],splitPrimary:[C.root,{width:"calc(100% - ".concat(gy,")")},r&&["is-checked",C.rootChecked],(t||c)&&["is-disabled",C.rootDisabled],!(t||c)&&!r&&[{selectors:(h={":hover":C.rootHovered},h[":hover ~ .".concat(m.splitMenu)]=C.rootHovered,h[":active"]=C.rootPressed,h[".".concat(an," &:focus, .").concat(an," &:focus:hover")]=C.rootFocused,h[".".concat(an," &:hover")]={background:"inherit;"},h)}]],splitMenu:[m.splitMenu,C.root,{flexBasis:"0",padding:"0 8px",minWidth:gy},n&&["is-expanded",C.rootExpanded],t&&["is-disabled",C.rootDisabled],!t&&!n&&[{selectors:(v={":hover":C.rootHovered,":active":C.rootPressed},v[".".concat(an," &:focus, .").concat(an," &:focus:hover")]=C.rootFocused,v[".".concat(an," &:hover")]={background:"inherit;"},v)}]],anchorLink:C.anchorLink,linkContent:[m.linkContent,C.linkContent],linkContentMenu:[m.linkContentMenu,C.linkContent,{justifyContent:"center"}],icon:[m.icon,o&&C.iconColor,C.icon,l,t&&[m.isDisabled,C.iconDisabled]],iconColor:C.iconColor,checkmarkIcon:[m.checkmarkIcon,o&&C.checkmarkIcon,C.icon,l],subMenuIcon:[m.subMenuIcon,C.subMenuIcon,u,n&&{color:e.palette.neutralPrimary},t&&[C.iconDisabled]],label:[m.label,C.label],secondaryText:[m.secondaryText,C.secondaryText],splitContainer:[C.splitButtonFlexContainer,!t&&!r&&[{selectors:(g={},g[".".concat(an," &:focus, .").concat(an," &:focus:hover")]=C.rootFocused,g)}]],screenReaderText:[m.screenReaderText,C.screenReaderText,n_,{visibility:"hidden"}]})}),b_=function(e){var t=e.theme,n=e.disabled,r=e.expanded,i=e.checked,o=e.isAnchorLink,a=e.knownIcon,s=e.itemClassName,l=e.dividerClassName,u=e.iconClassName,c=e.subMenuClassName,f=e.primaryDisabled,d=e.className;return wR(t,n,r,i,o,a,s,l,u,c,f,d)},B1=Dn(TR,b_,void 0,{scope:"ContextualMenuItem"}),c9=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return r._onItemMouseEnter=function(i){var o=r.props,a=o.item,s=o.onItemMouseEnter;s&&s(a,i,i.currentTarget)},r._onItemClick=function(i){var o=r.props,a=o.item,s=o.onItemClickBase;s&&s(a,i,i.currentTarget)},r._onItemMouseLeave=function(i){var o=r.props,a=o.item,s=o.onItemMouseLeave;s&&s(a,i)},r._onItemKeyDown=function(i){var o=r.props,a=o.item,s=o.onItemKeyDown;s&&s(a,i)},r._onItemMouseMove=function(i){var o=r.props,a=o.item,s=o.onItemMouseMove;s&&s(a,i,i.currentTarget)},r._getSubmenuTarget=function(){},Aa(r),r}return t.prototype.shouldComponentUpdate=function(n){return!Zv(n,this.props)},t}(E.Component),kR="ktp",vy="-",IR="data-ktp-target",xR="data-ktp-execute-target",NR="ktp-layer-id",Ci;(function(e){e.KEYTIP_ADDED="keytipAdded",e.KEYTIP_REMOVED="keytipRemoved",e.KEYTIP_UPDATED="keytipUpdated",e.PERSISTED_KEYTIP_ADDED="persistedKeytipAdded",e.PERSISTED_KEYTIP_REMOVED="persistedKeytipRemoved",e.PERSISTED_KEYTIP_EXECUTE="persistedKeytipExecute",e.ENTER_KEYTIP_MODE="enterKeytipMode",e.EXIT_KEYTIP_MODE="exitKeytipMode"})(Ci||(Ci={}));var FR=function(){function e(){this.keytips={},this.persistedKeytips={},this.sequenceMapping={},this.inKeytipMode=!1,this.shouldEnterKeytipMode=!0,this.delayUpdatingKeytipChange=!1}return e.getInstance=function(){return this._instance},e.prototype.init=function(t){this.delayUpdatingKeytipChange=t},e.prototype.register=function(t,n){n===void 0&&(n=!1);var r=t;n||(r=this.addParentOverflow(t),this.sequenceMapping[r.keySequences.toString()]=r);var i=this._getUniqueKtp(r);if(n?this.persistedKeytips[i.uniqueID]=i:this.keytips[i.uniqueID]=i,this.inKeytipMode||!this.delayUpdatingKeytipChange){var o=n?Ci.PERSISTED_KEYTIP_ADDED:Ci.KEYTIP_ADDED;Xr.raise(this,o,{keytip:r,uniqueID:i.uniqueID})}return i.uniqueID},e.prototype.update=function(t,n){var r=this.addParentOverflow(t),i=this._getUniqueKtp(r,n),o=this.keytips[n];o&&(i.keytip.visible=o.keytip.visible,this.keytips[n]=i,delete this.sequenceMapping[o.keytip.keySequences.toString()],this.sequenceMapping[i.keytip.keySequences.toString()]=i.keytip,(this.inKeytipMode||!this.delayUpdatingKeytipChange)&&Xr.raise(this,Ci.KEYTIP_UPDATED,{keytip:i.keytip,uniqueID:i.uniqueID}))},e.prototype.unregister=function(t,n,r){r===void 0&&(r=!1),r?delete this.persistedKeytips[n]:delete this.keytips[n],!r&&delete this.sequenceMapping[t.keySequences.toString()];var i=r?Ci.PERSISTED_KEYTIP_REMOVED:Ci.KEYTIP_REMOVED;(this.inKeytipMode||!this.delayUpdatingKeytipChange)&&Xr.raise(this,i,{keytip:t,uniqueID:n})},e.prototype.enterKeytipMode=function(){Xr.raise(this,Ci.ENTER_KEYTIP_MODE)},e.prototype.exitKeytipMode=function(){Xr.raise(this,Ci.EXIT_KEYTIP_MODE)},e.prototype.getKeytips=function(){var t=this;return Object.keys(this.keytips).map(function(n){return t.keytips[n].keytip})},e.prototype.addParentOverflow=function(t){var n=si([],t.keySequences,!0);if(n.pop(),n.length!==0){var r=this.sequenceMapping[n.toString()];if(r&&r.overflowSetSequence)return R(R({},t),{overflowSetSequence:r.overflowSetSequence})}return t},e.prototype.menuExecute=function(t,n){Xr.raise(this,Ci.PERSISTED_KEYTIP_EXECUTE,{overflowButtonSequences:t,keytipSequences:n})},e.prototype._getUniqueKtp=function(t,n){return n===void 0&&(n=bn()),{keytip:R({},t),uniqueID:n}},e._instance=new e,e}();function A_(e){return e.reduce(function(t,n){return t+vy+n.split("").join(vy)},kR)}function RR(e,t){var n=t.length,r=si([],t,!0).pop(),i=si([],e,!0);return fI(i,n-1,r)}function DR(e){var t=" "+NR;return e.length?t+" "+A_(e):t}function MR(e){var t=E.useRef(),n=e.keytipProps?R({disabled:e.disabled},e.keytipProps):void 0,r=Hi(FR.getInstance()),i=o9(e);Ml(function(){t.current&&n&&((i==null?void 0:i.keytipProps)!==e.keytipProps||(i==null?void 0:i.disabled)!==e.disabled)&&r.update(n,t.current)}),Ml(function(){return n&&(t.current=r.register(n)),function(){n&&r.unregister(n,t.current)}},[]);var o={ariaDescribedBy:void 0,keytipId:void 0};return n&&(o=PR(r,n,e.ariaDescribedBy)),o}function PR(e,t,n){var r=e.addParentOverflow(t),i=sc(n,DR(r.keySequences)),o=si([],r.keySequences,!0);r.overflowSetSequence&&(o=RR(o,r.overflowSetSequence));var a=A_(o);return{ariaDescribedBy:i,keytipId:a}}var H1=function(e){var t,n=e.children,r=_a(e,["children"]),i=MR(r),o=i.keytipId,a=i.ariaDescribedBy;return n((t={},t[IR]=o,t[xR]=o,t["aria-describedby"]=a,t))},OR=function(e){yt(t,e);function t(){var n=e!==null&&e.apply(this,arguments)||this;return n._anchor=E.createRef(),n._getMemoizedMenuButtonKeytipProps=It(function(r){return R(R({},r),{hasMenu:!0})}),n._getSubmenuTarget=function(){return n._anchor.current?n._anchor.current:void 0},n._onItemClick=function(r){var i=n.props,o=i.item,a=i.onItemClick;a&&a(o,r)},n._renderAriaDescription=function(r,i){return r?E.createElement("span",{id:n._ariaDescriptionId,className:i},r):null},n}return t.prototype.render=function(){var n=this,r=this.props,i=r.item,o=r.classNames,a=r.index,s=r.focusableElementIndex,l=r.totalItemCount,u=r.hasCheckmarks,c=r.hasIcons,f=r.contextualMenuItemAs,d=f===void 0?B1:f,h=r.expandedMenuItemKey,v=r.onItemClick,g=r.openSubMenu,C=r.dismissSubMenu,m=r.dismissMenu,y=i.rel;i.target&&i.target.toLowerCase()==="_blank"&&(y=y||"nofollow noopener noreferrer");var T=po(i),S=ht(i,U8),w=Di(i),b=i.itemProps,A=i.ariaDescription,F=i.keytipProps;F&&T&&(F=this._getMemoizedMenuButtonKeytipProps(F)),A&&(this._ariaDescriptionId=bn());var D=sc(i.ariaDescribedBy,A?this._ariaDescriptionId:void 0,S["aria-describedby"]),I={"aria-describedby":D};return E.createElement("div",null,E.createElement(H1,{keytipProps:i.keytipProps,ariaDescribedBy:D,disabled:w},function(B){return E.createElement("a",R({},I,S,B,{ref:n._anchor,href:i.href,target:i.target,rel:y,className:o.root,role:"menuitem","aria-haspopup":T||void 0,"aria-expanded":T?i.key===h:void 0,"aria-posinset":s+1,"aria-setsize":l,"aria-disabled":Di(i),style:i.style,onClick:n._onItemClick,onMouseEnter:n._onItemMouseEnter,onMouseLeave:n._onItemMouseLeave,onMouseMove:n._onItemMouseMove,onKeyDown:T?n._onItemKeyDown:void 0}),E.createElement(d,R({componentRef:i.componentRef,item:i,classNames:o,index:a,onCheckmarkClick:u&&v?v:void 0,hasIcons:c,openSubMenu:g,dismissSubMenu:C,dismissMenu:m,getSubmenuTarget:n._getSubmenuTarget},b)),n._renderAriaDescription(A,o.screenReaderText))}))},t}(c9),LR=function(e){yt(t,e);function t(){var n=e!==null&&e.apply(this,arguments)||this;return n._btn=E.createRef(),n._getMemoizedMenuButtonKeytipProps=It(function(r){return R(R({},r),{hasMenu:!0})}),n._renderAriaDescription=function(r,i){return r?E.createElement("span",{id:n._ariaDescriptionId,className:i},r):null},n._getSubmenuTarget=function(){return n._btn.current?n._btn.current:void 0},n}return t.prototype.render=function(){var n=this,r=this.props,i=r.item,o=r.classNames,a=r.index,s=r.focusableElementIndex,l=r.totalItemCount,u=r.hasCheckmarks,c=r.hasIcons,f=r.contextualMenuItemAs,d=f===void 0?B1:f,h=r.expandedMenuItemKey,v=r.onItemMouseDown,g=r.onItemClick,C=r.openSubMenu,m=r.dismissSubMenu,y=r.dismissMenu,T=Ll(i),S=T!==null,w=__(i),b=po(i),A=i.itemProps,F=i.ariaLabel,D=i.ariaDescription,I=ht(i,cs);delete I.disabled;var B=i.role||w;D&&(this._ariaDescriptionId=bn());var M=sc(i.ariaDescribedBy,D?this._ariaDescriptionId:void 0,I["aria-describedby"]),oe={className:o.root,onClick:this._onItemClick,onKeyDown:b?this._onItemKeyDown:void 0,onMouseEnter:this._onItemMouseEnter,onMouseLeave:this._onItemMouseLeave,onMouseDown:function(P){return v?v(i,P):void 0},onMouseMove:this._onItemMouseMove,href:i.href,title:i.title,"aria-label":F,"aria-describedby":M,"aria-haspopup":b||void 0,"aria-expanded":b?i.key===h:void 0,"aria-posinset":s+1,"aria-setsize":l,"aria-disabled":Di(i),"aria-checked":(B==="menuitemcheckbox"||B==="menuitemradio")&&S?!!T:void 0,"aria-selected":B==="menuitem"&&S?!!T:void 0,role:B,style:i.style},V=i.keytipProps;return V&&b&&(V=this._getMemoizedMenuButtonKeytipProps(V)),E.createElement(H1,{keytipProps:V,ariaDescribedBy:M,disabled:Di(i)},function(P){return E.createElement("button",R({ref:n._btn},I,oe,P),E.createElement(d,R({componentRef:i.componentRef,item:i,classNames:o,index:a,onCheckmarkClick:u&&g?g:void 0,hasIcons:c,openSubMenu:C,dismissSubMenu:m,dismissMenu:y,getSubmenuTarget:n._getSubmenuTarget},A)),n._renderAriaDescription(D,o.screenReaderText))})},t}(c9),BR=function(e){var t=e.theme,n=e.getClassNames,r=e.className;if(!t)throw new Error("Theme is undefined or null.");if(n){var i=n(t);return{wrapper:[i.wrapper],divider:[i.divider]}}return{wrapper:[{display:"inline-flex",height:"100%",alignItems:"center"},r],divider:[{width:1,height:"100%",backgroundColor:t.palette.neutralTertiaryAlt}]}},HR=Rn(),w_=E.forwardRef(function(e,t){var n=e.styles,r=e.theme,i=e.getClassNames,o=e.className,a=HR(n,{theme:r,getClassNames:i,className:o});return E.createElement("span",{className:a.wrapper,ref:t},E.createElement("span",{className:a.divider}))});w_.displayName="VerticalDividerBase";var zR=Dn(w_,BR,void 0,{scope:"VerticalDivider"}),UR=500,KR=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return r._getMemoizedMenuButtonKeytipProps=It(function(i){return R(R({},i),{hasMenu:!0})}),r._onItemKeyDown=function(i){var o=r.props,a=o.item,s=o.onItemKeyDown;i.which===le.enter?(r._executeItemClick(i),i.preventDefault(),i.stopPropagation()):s&&s(a,i)},r._getSubmenuTarget=function(){return r._splitButton},r._renderAriaDescription=function(i,o){return i?E.createElement("span",{id:r._ariaDescriptionId,className:o},i):null},r._onItemMouseEnterPrimary=function(i){var o=r.props,a=o.item,s=o.onItemMouseEnter;s&&s(R(R({},a),{subMenuProps:void 0,items:void 0}),i,r._splitButton)},r._onItemMouseEnterIcon=function(i){var o=r.props,a=o.item,s=o.onItemMouseEnter;s&&s(a,i,r._splitButton)},r._onItemMouseMovePrimary=function(i){var o=r.props,a=o.item,s=o.onItemMouseMove;s&&s(R(R({},a),{subMenuProps:void 0,items:void 0}),i,r._splitButton)},r._onItemMouseMoveIcon=function(i){var o=r.props,a=o.item,s=o.onItemMouseMove;s&&s(a,i,r._splitButton)},r._onIconItemClick=function(i){var o=r.props,a=o.item,s=o.onItemClickBase;s&&s(a,i,r._splitButton?r._splitButton:i.currentTarget)},r._executeItemClick=function(i){var o=r.props,a=o.item,s=o.executeItemClick,l=o.onItemClick;if(!(a.disabled||a.isDisabled)){if(r._processingTouch&&l)return l(a,i);s&&s(a,i)}},r._onTouchStart=function(i){r._splitButton&&!("onpointerdown"in r._splitButton)&&r._handleTouchAndPointerEvent(i)},r._onPointerDown=function(i){i.pointerType==="touch"&&(r._handleTouchAndPointerEvent(i),i.preventDefault(),i.stopImmediatePropagation())},r._async=new ac(r),r._events=new Xr(r),r}return t.prototype.componentDidMount=function(){this._splitButton&&"onpointerdown"in this._splitButton&&this._events.on(this._splitButton,"pointerdown",this._onPointerDown,!0)},t.prototype.componentWillUnmount=function(){this._async.dispose(),this._events.dispose()},t.prototype.render=function(){var n=this,r,i=this.props,o=i.item,a=i.classNames,s=i.index,l=i.focusableElementIndex,u=i.totalItemCount,c=i.hasCheckmarks,f=i.hasIcons,d=i.onItemMouseLeave,h=i.expandedMenuItemKey,v=po(o),g=o.keytipProps;g&&(g=this._getMemoizedMenuButtonKeytipProps(g));var C=o.ariaDescription;C&&(this._ariaDescriptionId=bn());var m=(r=Ll(o))!==null&&r!==void 0?r:void 0;return E.createElement(H1,{keytipProps:g,disabled:Di(o)},function(y){return E.createElement("div",{"data-ktp-target":y["data-ktp-target"],ref:function(T){return n._splitButton=T},role:__(o),"aria-label":o.ariaLabel,className:a.splitContainer,"aria-disabled":Di(o),"aria-expanded":v?o.key===h:void 0,"aria-haspopup":!0,"aria-describedby":sc(o.ariaDescribedBy,C?n._ariaDescriptionId:void 0,y["aria-describedby"]),"aria-checked":m,"aria-posinset":l+1,"aria-setsize":u,onMouseEnter:n._onItemMouseEnterPrimary,onMouseLeave:d?d.bind(n,R(R({},o),{subMenuProps:null,items:null})):void 0,onMouseMove:n._onItemMouseMovePrimary,onKeyDown:n._onItemKeyDown,onClick:n._executeItemClick,onTouchStart:n._onTouchStart,tabIndex:0,"data-is-focusable":!0,"aria-roledescription":o["aria-roledescription"]},n._renderSplitPrimaryButton(o,a,s,c,f),n._renderSplitDivider(o),n._renderSplitIconButton(o,a,s,y),n._renderAriaDescription(C,a.screenReaderText))})},t.prototype._renderSplitPrimaryButton=function(n,r,i,o,a){var s=this.props,l=s.contextualMenuItemAs,u=l===void 0?B1:l,c=s.onItemClick,f={key:n.key,disabled:Di(n)||n.primaryDisabled,name:n.name,text:n.text||n.name,secondaryText:n.secondaryText,className:r.splitPrimary,canCheck:n.canCheck,isChecked:n.isChecked,checked:n.checked,iconProps:n.iconProps,onRenderIcon:n.onRenderIcon,data:n.data,"data-is-focusable":!1},d=n.itemProps;return E.createElement("button",R({},ht(f,cs)),E.createElement(u,R({"data-is-focusable":!1,item:f,classNames:r,index:i,onCheckmarkClick:o&&c?c:void 0,hasIcons:a},d)))},t.prototype._renderSplitDivider=function(n){var r=n.getSplitButtonVerticalDividerClassNames||bR;return E.createElement(zR,{getClassNames:r})},t.prototype._renderSplitIconButton=function(n,r,i,o){var a=this.props,s=a.contextualMenuItemAs,l=s===void 0?B1:s,u=a.onItemMouseLeave,c=a.onItemMouseDown,f=a.openSubMenu,d=a.dismissSubMenu,h=a.dismissMenu,v={onClick:this._onIconItemClick,disabled:Di(n),className:r.splitMenu,subMenuProps:n.subMenuProps,submenuIconProps:n.submenuIconProps,split:!0,key:n.key},g=R(R({},ht(v,cs)),{onMouseEnter:this._onItemMouseEnterIcon,onMouseLeave:u?u.bind(this,n):void 0,onMouseDown:function(m){return c?c(n,m):void 0},onMouseMove:this._onItemMouseMoveIcon,"data-is-focusable":!1,"data-ktp-execute-target":o["data-ktp-execute-target"],"aria-hidden":!0}),C=n.itemProps;return E.createElement("button",R({},g),E.createElement(l,R({componentRef:n.componentRef,item:v,classNames:r,index:i,hasIcons:!1,openSubMenu:f,dismissSubMenu:d,dismissMenu:h,getSubmenuTarget:this._getSubmenuTarget},C)))},t.prototype._handleTouchAndPointerEvent=function(n){var r=this,i=this.props.onTap;i&&i(n),this._lastTouchTimeoutId&&(this._async.clearTimeout(this._lastTouchTimeoutId),this._lastTouchTimeoutId=void 0),this._processingTouch=!0,this._lastTouchTimeoutId=this._async.setTimeout(function(){r._processingTouch=!1,r._lastTouchTimeoutId=void 0},UR)},t}(c9),WR=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return r._updateComposedComponentRef=r._updateComposedComponentRef.bind(r),r}return t.prototype._updateComposedComponentRef=function(n){this._composedComponentInstance=n,n?this._hoisted=ux(this,n):this._hoisted&&cx(this,this._hoisted)},t}(E.Component),ds;(function(e){e[e.small=0]="small",e[e.medium=1]="medium",e[e.large=2]="large",e[e.xLarge=3]="xLarge",e[e.xxLarge=4]="xxLarge",e[e.xxxLarge=5]="xxxLarge",e[e.unknown=999]="unknown"})(ds||(ds={}));var jR=[479,639,1023,1365,1919,99999999],k_;function f9(){var e;return(e=k_)!==null&&e!==void 0?e:ds.large}function I_(e){var t,n=(t=function(r){yt(i,r);function i(o){var a=r.call(this,o)||this;return a._onResize=function(){var s=x_(a.context.window);s!==a.state.responsiveMode&&a.setState({responsiveMode:s})},a._events=new Xr(a),a._updateComposedComponentRef=a._updateComposedComponentRef.bind(a),a.state={responsiveMode:f9()},a}return i.prototype.componentDidMount=function(){this._events.on(this.context.window,"resize",this._onResize),this._onResize()},i.prototype.componentWillUnmount=function(){this._events.dispose()},i.prototype.render=function(){var o=this.state.responsiveMode;return o===ds.unknown?null:E.createElement(e,R({ref:this._updateComposedComponentRef,responsiveMode:o},this.props))},i}(WR),t.contextType=a9,t);return z8(e,n)}function GR(e){try{return e.document.documentElement.clientWidth}catch{return e.innerWidth}}function x_(e){var t=ds.small;if(e){try{for(;GR(e)>jR[t];)t++}catch{t=f9()}k_=t}else throw new Error("Content was rendered in a server environment without providing a default responsive mode. Call setResponsiveMode to define what the responsive mode is.");return t}var N_=function(e,t){var n=E.useState(f9()),r=n[0],i=n[1],o=E.useCallback(function(){var s=x_(st(e.current));r!==s&&i(s)},[e,r]),a=Oh();return O1(a,"resize",o),E.useEffect(function(){t===void 0&&o()},[t]),t??r},$R=E.createContext({}),VR=Rn(),qR=Rn(),YR={items:[],shouldFocusOnMount:!0,gapSpace:0,directionalHint:_t.bottomAutoEdge,beakWidth:16};function Ey(e){for(var t=0,n=0,r=e;n0){var lu=0;return E.createElement("li",{role:"presentation",key:Re.key||Y.key||"section-".concat(Le)},E.createElement("div",R({},lt),E.createElement("ul",{className:Oe.list,role:"presentation"},Re.topDivider&&di(Le,ye,!0,!0),rr&&er(rr,Y.key||Le,ye,Y.title),Re.items.map(function(Fo,Ur){var mp=Bt(Fo,Ur,lu,Ey(Re.items),vn,Bn,Oe);if(Fo.itemType!==sn.Divider&&Fo.itemType!==sn.Header){var gp=Fo.customOnRenderListLength?Fo.customOnRenderListLength:1;lu+=gp}return mp}),Re.bottomDivider&&di(Le,ye,!1,!0))))}}},er=function(Y,ye,Oe,Le){return E.createElement("li",{role:"presentation",title:Le,key:ye,className:Oe.item},Y)},di=function(Y,ye,Oe,Le){return Le||Y>0?E.createElement("li",{role:"separator",key:"separator-"+Y+(Oe===void 0?"":Oe?"-top":"-bottom"),className:ye.divider,"aria-hidden":"true"}):null},Ao=function(Y,ye,Oe,Le,vn,Bn,Re){if(Y.onRender)return Y.onRender(R({"aria-posinset":Le+1,"aria-setsize":vn},Y),l);var rr=i.contextualMenuItemAs,lt={item:Y,classNames:ye,index:Oe,focusableElementIndex:Le,totalItemCount:vn,hasCheckmarks:Bn,hasIcons:Re,contextualMenuItemAs:rr,onItemMouseEnter:_,onItemMouseLeave:X,onItemMouseMove:ee,onItemMouseDown:sD,executeItemClick:xt,onItemKeyDown:H,expandedMenuItemKey:v,openSubMenu:g,dismissSubMenu:m,dismissMenu:l};return Y.href?E.createElement(OR,R({},lt,{onItemClick:ge})):Y.split&&po(Y)?E.createElement(KR,R({},lt,{onItemClick:x,onItemClickBase:pe,onTap:I})):E.createElement(LR,R({},lt,{onItemClick:x,onItemClickBase:pe}))},wo=function(Y,ye,Oe,Le,vn,Bn){var Re=i.contextualMenuItemAs,rr=Re===void 0?B1:Re,lt=Y.itemProps,ir=Y.id,gi=lt&&ht(lt,Cs);return E.createElement("div",R({id:ir,className:Oe.header},gi,{style:Y.style}),E.createElement(rr,R({item:Y,classNames:ye,index:Le,onCheckmarkClick:vn?x:void 0,hasIcons:Bn},lt)))},hi=i.isBeakVisible,On=i.items,Na=i.labelElementId,Is=i.id,ko=i.className,Fa=i.beakWidth,zr=i.directionalHint,xs=i.directionalHintForRTL,ou=i.alignTargetEdge,au=i.gapSpace,Ns=i.coverTarget,O=i.ariaLabel,G=i.doNotLayer,de=i.target,Ce=i.bounds,Q=i.useTargetWidth,Ht=i.useTargetAsMinWidth,Cr=i.directionalHintFixed,tr=i.shouldFocusOnMount,Ln=i.shouldFocusOnContainer,pi=i.title,qe=i.styles,Io=i.theme,St=i.calloutProps,su=i.onRenderSubMenu,dp=su===void 0?Ty:su,Pc=i.onRenderMenuList,hp=Pc===void 0?function(Y,ye){return it(Y,mi)}:Pc,pp=i.focusZoneProps,Oc=i.getMenuClassNames,mi=Oc?Oc(Io,ko):VR(qe,{theme:Io,className:ko}),Lc=Ue(On);function Ue(Y){for(var ye=0,Oe=Y;ye0){var o3=Ey(On),a3=mi.subComponentStyles?mi.subComponentStyles.callout:void 0;return E.createElement($R.Consumer,null,function(Y){return E.createElement(y_,R({styles:a3,onRestoreFocus:d},St,{target:de||Y.target,isBeakVisible:hi,beakWidth:Fa,directionalHint:zr,directionalHintForRTL:xs,gapSpace:au,coverTarget:Ns,doNotLayer:G,className:qn("ms-ContextualMenu-Callout",St&&St.className),setInitialFocus:tr,onDismiss:i.onDismiss||Y.onDismiss,onScroll:A,bounds:Ce,directionalHintFixed:Cr,alignTargetEdge:ou,hidden:i.hidden||Y.hidden,ref:t}),E.createElement("div",{style:No,ref:o,id:Is,className:mi.container,tabIndex:Ln?0:-1,onKeyDown:Z,onKeyUp:z,onFocusCapture:w,"aria-label":O,"aria-labelledby":Na,role:"menu"},pi&&E.createElement("div",{className:mi.title}," ",pi," "),On&&On.length?Nt(hp({ariaLabel:O,items:On,totalItemCount:o3,hasCheckmarks:nr,hasIcons:Lc,defaultMenuItemRenderer:function(ye){return Ct(ye,mi)},labelElementId:Na},function(ye,Oe){return it(ye,mi)}),me):null,xo&&dp(xo,Ty)),E.createElement(W8,null))})}else return null}),function(e,t){return!t.shouldUpdateWhenHidden&&e.hidden&&t.hidden?!0:Zv(e,t)});M_.displayName="ContextualMenuBase";function yy(e){return e.which===le.alt||e.key==="Meta"}function sD(e,t){var n;(n=e.onMouseDown)===null||n===void 0||n.call(e,e,t)}function Ty(e,t){throw Error("ContextualMenuBase: onRenderSubMenu callback is null or undefined. Please ensure to set `onRenderSubMenu` property either manually or with `styled` helper.")}function P_(e,t){for(var n=0,r=t;n span":{position:"relative",left:0,top:0}}}],rootDisabled:[fs(e,{inset:1,highContrastStyle:u,borderColor:"transparent"}),{backgroundColor:s,borderColor:s,color:l,cursor:"default",selectors:{":hover":_y,":focus":_y}}],iconDisabled:{color:l,selectors:(t={},t[_e]={color:"GrayText"},t)},menuIconDisabled:{color:l,selectors:(n={},n[_e]={color:"GrayText"},n)},flexContainer:{display:"flex",height:"100%",flexWrap:"nowrap",justifyContent:"center",alignItems:"center"},description:{display:"block"},textContainer:{flexGrow:1,display:"block"},icon:by(o.mediumPlus.fontSize),menuIcon:by(o.small.fontSize),label:{margin:"0 4px",lineHeight:"100%",display:"block"},screenReaderText:n_}}),gD=It(function(e,t){var n,r,i,o,a,s,l,u,c,f,d,h,v,g=e.effects,C=e.palette,m=e.semanticColors,y={left:-2,top:-2,bottom:-2,right:-2,border:"none"},T={position:"absolute",width:1,right:31,top:8,bottom:8},S={splitButtonContainer:[fs(e,{highContrastStyle:y,inset:2,pointerEvents:"none"}),{display:"inline-flex",selectors:{".ms-Button--default":{borderTopRightRadius:"0",borderBottomRightRadius:"0",borderRight:"none",flexGrow:"1"},".ms-Button--primary":{borderTopRightRadius:"0",borderBottomRightRadius:"0",border:"none",flexGrow:"1",selectors:(n={},n[_e]=R({color:"WindowText",backgroundColor:"Window",border:"1px solid WindowText",borderRightWidth:"0"},Ri()),n[":hover"]={border:"none"},n[":active"]={border:"none"},n)},".ms-Button--primary + .ms-Button":{border:"none",selectors:(r={},r[_e]={border:"1px solid WindowText",borderLeftWidth:"0"},r)}}}],splitButtonContainerHovered:{selectors:{".ms-Button--primary":{selectors:(i={},i[_e]={color:"Window",backgroundColor:"Highlight"},i)},".ms-Button.is-disabled":{color:m.buttonTextDisabled,selectors:(o={},o[_e]={color:"GrayText",borderColor:"GrayText",backgroundColor:"Window"},o)}}},splitButtonContainerChecked:{selectors:{".ms-Button--primary":{selectors:(a={},a[_e]=R({color:"Window",backgroundColor:"WindowText"},Ri()),a)}}},splitButtonContainerCheckedHovered:{selectors:{".ms-Button--primary":{selectors:(s={},s[_e]=R({color:"Window",backgroundColor:"WindowText"},Ri()),s)}}},splitButtonContainerFocused:{outline:"none!important"},splitButtonMenuButton:(l={padding:6,height:"auto",boxSizing:"border-box",borderRadius:0,borderTopRightRadius:g.roundedCorner2,borderBottomRightRadius:g.roundedCorner2,border:"1px solid ".concat(C.neutralSecondaryAlt),borderLeft:"none",outline:"transparent",userSelect:"none",display:"inline-block",textDecoration:"none",textAlign:"center",cursor:"pointer",verticalAlign:"top",width:32,marginLeft:-1,marginTop:0,marginRight:0,marginBottom:0},l[_e]={".ms-Button-menuIcon":{color:"WindowText"}},l),splitButtonDivider:R(R({},T),{selectors:(u={},u[_e]={backgroundColor:"WindowText"},u)}),splitButtonDividerDisabled:R(R({},T),{selectors:(c={},c[_e]={backgroundColor:"GrayText"},c)}),splitButtonMenuButtonDisabled:{pointerEvents:"none",border:"none",selectors:(f={":hover":{cursor:"default"},".ms-Button--primary":{selectors:(d={},d[_e]={color:"GrayText",borderColor:"GrayText",backgroundColor:"Window"},d)},".ms-Button-menuIcon":{selectors:(h={},h[_e]={color:"GrayText"},h)}},f[_e]={color:"GrayText",border:"1px solid GrayText",backgroundColor:"Window"},f)},splitButtonFlexContainer:{display:"flex",height:"100%",flexWrap:"nowrap",justifyContent:"center",alignItems:"center"},splitButtonContainerDisabled:{outline:"none",border:"none",selectors:(v={},v[_e]=R({color:"GrayText",borderColor:"GrayText",backgroundColor:"Window"},Ri()),v)},splitButtonMenuFocused:R({},fs(e,{highContrastStyle:y,inset:2}))};return ba(S,t)}),vD=It(function(e,t){var n,r=mD(e),i=gD(e),o=e.palette,a=e.semanticColors,s={root:{padding:"0 4px",width:"32px",height:"32px",backgroundColor:"transparent",border:"none",color:a.link},rootHovered:{color:o.themeDarkAlt,backgroundColor:o.neutralLighter,selectors:(n={},n[_e]={borderColor:"Highlight",color:"Highlight"},n)},rootHasMenu:{width:"auto"},rootPressed:{color:o.themeDark,backgroundColor:o.neutralLight},rootExpanded:{color:o.themeDark,backgroundColor:o.neutralLight},rootChecked:{color:o.themeDark,backgroundColor:o.neutralLight},rootCheckedHovered:{color:o.themeDark,backgroundColor:o.neutralQuaternaryAlt},rootDisabled:{color:o.neutralTertiaryAlt}};return ba(r,s,i,t)}),Ay=function(e){yt(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.render=function(){var n=this.props,r=n.styles,i=n.theme;return E.createElement(pD,R({},this.props,{variantClassName:"ms-Button--icon",styles:vD(i,r),onRenderText:rg,onRenderDescription:rg}))},t=Yv([QI("IconButton",["theme","styles"],!0)],t),t}(E.Component),ED=Rn({cacheSize:100}),yD=function(e){yt(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.render=function(){var n=this.props,r=n.as,i=r===void 0?"label":r,o=n.children,a=n.className,s=n.disabled,l=n.styles,u=n.required,c=n.theme,f=ED(l,{className:a,disabled:s,required:u,theme:c});return E.createElement(i,R({},ht(this.props,Cs),{className:f.root}),o)},t}(E.Component),TD=function(e){var t,n=e.theme,r=e.className,i=e.disabled,o=e.required,a=n.semanticColors,s=et.semibold,l=a.bodyText,u=a.disabledBodyText,c=a.errorText;return{root:["ms-Label",n.fonts.medium,{fontWeight:s,color:l,boxSizing:"border-box",boxShadow:"none",margin:0,display:"block",padding:"5px 0",wordWrap:"break-word",overflowWrap:"break-word"},i&&{color:u,selectors:(t={},t[_e]=R({color:"GrayText"},Ri()),t)},o&&{selectors:{"::after":{content:"' *'",color:c,paddingRight:12}}},r]}},CD=Dn(yD,TD,void 0,{scope:"Label"}),SD=Rn(),_D="",Pa="TextField",bD="RedEye",AD="Hide",wD=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;r._textElement=E.createRef(),r._onFocus=function(a){r.props.onFocus&&r.props.onFocus(a),r.setState({isFocused:!0},function(){r.props.validateOnFocusIn&&r._validate(r.value)})},r._onBlur=function(a){r.props.onBlur&&r.props.onBlur(a),r.setState({isFocused:!1},function(){r.props.validateOnFocusOut&&r._validate(r.value)})},r._onRenderLabel=function(a){var s=a.label,l=a.required,u=r._classNames.subComponentStyles?r._classNames.subComponentStyles.label:void 0;return s?E.createElement(CD,{required:l,htmlFor:r._id,styles:u,disabled:a.disabled,id:r._labelId},a.label):null},r._onRenderDescription=function(a){return a.description?E.createElement("span",{className:r._classNames.description},a.description):null},r._onRevealButtonClick=function(a){r.setState(function(s){return{isRevealingPassword:!s.isRevealingPassword}})},r._onInputChange=function(a){var s,l,u=a.target,c=u.value,f=cm(r.props,r.state)||"";if(c===void 0||c===r._lastChangeValue||c===f){r._lastChangeValue=void 0;return}r._lastChangeValue=c,(l=(s=r.props).onChange)===null||l===void 0||l.call(s,a,c),r._isControlled||r.setState({uncontrolledValue:c})},Aa(r),r._async=new ac(r),r._fallbackId=bn(Pa),r._descriptionId=bn(Pa+"Description"),r._labelId=bn(Pa+"Label"),r._prefixId=bn(Pa+"Prefix"),r._suffixId=bn(Pa+"Suffix"),r._warnControlledUsage();var i=n.defaultValue,o=i===void 0?_D:i;return typeof o=="number"&&(o=String(o)),r.state={uncontrolledValue:r._isControlled?void 0:o,isFocused:!1,errorMessage:""},r._delayedValidate=r._async.debounce(r._validate,r.props.deferredValidationTime),r._lastValidation=0,r}return Object.defineProperty(t.prototype,"value",{get:function(){return cm(this.props,this.state)},enumerable:!1,configurable:!0}),t.prototype.componentDidMount=function(){this._adjustInputHeight(),this.props.validateOnLoad&&this._validate(this.value)},t.prototype.componentWillUnmount=function(){this._async.dispose()},t.prototype.getSnapshotBeforeUpdate=function(n,r){return{selection:[this.selectionStart,this.selectionEnd]}},t.prototype.componentDidUpdate=function(n,r,i){var o=this.props,a=(i||{}).selection,s=a===void 0?[null,null]:a,l=s[0],u=s[1];!!n.multiline!=!!o.multiline&&r.isFocused&&(this.focus(),l!==null&&u!==null&&l>=0&&u>=0&&this.setSelectionRange(l,u)),n.value!==o.value&&(this._lastChangeValue=void 0);var c=cm(n,r),f=this.value;c!==f&&(this._warnControlledUsage(n),this.state.errorMessage&&!o.errorMessage&&this.setState({errorMessage:""}),this._adjustInputHeight(),wy(o)&&this._delayedValidate(f))},t.prototype.render=function(){var n=this.props,r=n.borderless,i=n.className,o=n.disabled,a=n.invalid,s=n.iconProps,l=n.inputClassName,u=n.label,c=n.multiline,f=n.required,d=n.underlined,h=n.prefix,v=n.resizable,g=n.suffix,C=n.theme,m=n.styles,y=n.autoAdjustHeight,T=n.canRevealPassword,S=n.revealPasswordAriaLabel,w=n.type,b=n.onRenderPrefix,A=b===void 0?this._onRenderPrefix:b,F=n.onRenderSuffix,D=F===void 0?this._onRenderSuffix:F,I=n.onRenderLabel,B=I===void 0?this._onRenderLabel:I,M=n.onRenderDescription,oe=M===void 0?this._onRenderDescription:M,V=this.state,P=V.isFocused,z=V.isRevealingPassword,Z=this._errorMessage,H=typeof a=="boolean"?a:!!Z,U=!!T&&w==="password"&&kD(),_=this._classNames=SD(m,{theme:C,className:i,disabled:o,focused:P,required:f,multiline:c,hasLabel:!!u,hasErrorMessage:H,borderless:r,resizable:v,hasIcon:!!s,underlined:d,inputClassName:l,autoAdjustHeight:y,hasRevealButton:U});return E.createElement("div",{ref:this.props.elementRef,className:_.root},E.createElement("div",{className:_.wrapper},B(this.props,this._onRenderLabel),E.createElement("div",{className:_.fieldGroup},(h!==void 0||this.props.onRenderPrefix)&&E.createElement("div",{className:_.prefix,id:this._prefixId},A(this.props,this._onRenderPrefix)),c?this._renderTextArea():this._renderInput(),s&&E.createElement(ho,R({className:_.icon},s)),U&&E.createElement("button",{"aria-label":S,className:_.revealButton,onClick:this._onRevealButtonClick,"aria-pressed":!!z,type:"button"},E.createElement("span",{className:_.revealSpan},E.createElement(ho,{className:_.revealIcon,iconName:z?AD:bD}))),(g!==void 0||this.props.onRenderSuffix)&&E.createElement("div",{className:_.suffix,id:this._suffixId},D(this.props,this._onRenderSuffix)))),this._isDescriptionAvailable&&E.createElement("span",{id:this._descriptionId},oe(this.props,this._onRenderDescription),Z&&E.createElement("div",{role:"alert"},E.createElement(lI,null,this._renderErrorMessage()))))},t.prototype.focus=function(){this._textElement.current&&this._textElement.current.focus()},t.prototype.blur=function(){this._textElement.current&&this._textElement.current.blur()},t.prototype.select=function(){this._textElement.current&&this._textElement.current.select()},t.prototype.setSelectionStart=function(n){this._textElement.current&&(this._textElement.current.selectionStart=n)},t.prototype.setSelectionEnd=function(n){this._textElement.current&&(this._textElement.current.selectionEnd=n)},Object.defineProperty(t.prototype,"selectionStart",{get:function(){return this._textElement.current?this._textElement.current.selectionStart:-1},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"selectionEnd",{get:function(){return this._textElement.current?this._textElement.current.selectionEnd:-1},enumerable:!1,configurable:!0}),t.prototype.setSelectionRange=function(n,r){this._textElement.current&&this._textElement.current.setSelectionRange(n,r)},t.prototype._warnControlledUsage=function(n){this._id,this.props,this.props.value===null&&!this._hasWarnedNullValue&&(this._hasWarnedNullValue=!0,Jv("Warning: 'value' prop on '".concat(Pa,"' should not be null. Consider using an ")+"empty string to clear the component or undefined to indicate an uncontrolled component."))},Object.defineProperty(t.prototype,"_id",{get:function(){return this.props.id||this._fallbackId},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"_isControlled",{get:function(){return UI(this.props,"value")},enumerable:!1,configurable:!0}),t.prototype._onRenderPrefix=function(n){var r=n.prefix;return E.createElement("span",{style:{paddingBottom:"1px"}},r)},t.prototype._onRenderSuffix=function(n){var r=n.suffix;return E.createElement("span",{style:{paddingBottom:"1px"}},r)},Object.defineProperty(t.prototype,"_errorMessage",{get:function(){var n=this.props.errorMessage,r=n===void 0?this.state.errorMessage:n;return r||""},enumerable:!1,configurable:!0}),t.prototype._renderErrorMessage=function(){var n=this._errorMessage;return n?typeof n=="string"?E.createElement("p",{className:this._classNames.errorMessage},E.createElement("span",{"data-automation-id":"error-message"},n)):E.createElement("div",{className:this._classNames.errorMessage,"data-automation-id":"error-message"},n):null},Object.defineProperty(t.prototype,"_isDescriptionAvailable",{get:function(){var n=this.props;return!!(n.onRenderDescription||n.description||this._errorMessage)},enumerable:!1,configurable:!0}),t.prototype._renderTextArea=function(){var n=this.props.invalid,r=n===void 0?!!this._errorMessage:n,i=ht(this.props,ax,["defaultValue"]),o=this.props["aria-labelledby"]||(this.props.label?this._labelId:void 0);return E.createElement("textarea",R({id:this._id},i,{ref:this._textElement,value:this.value||"",onInput:this._onInputChange,onChange:this._onInputChange,className:this._classNames.field,"aria-labelledby":o,"aria-describedby":this._isDescriptionAvailable?this._descriptionId:this.props["aria-describedby"],"aria-invalid":r,"aria-label":this.props.ariaLabel,readOnly:this.props.readOnly,onFocus:this._onFocus,onBlur:this._onBlur}))},t.prototype._renderInput=function(){var n=this.props,r=n.ariaLabel,i=n.invalid,o=i===void 0?!!this._errorMessage:i,a=n.onRenderPrefix,s=n.onRenderSuffix,l=n.prefix,u=n.suffix,c=n.type,f=c===void 0?"text":c,d=n.label,h=[];d&&h.push(this._labelId),(l!==void 0||a)&&h.push(this._prefixId),(u!==void 0||s)&&h.push(this._suffixId);var v=R(R({type:this.state.isRevealingPassword?"text":f,id:this._id},ht(this.props,ox,["defaultValue","type"])),{"aria-labelledby":this.props["aria-labelledby"]||(h.length>0?h.join(" "):void 0),ref:this._textElement,value:this.value||"",onInput:this._onInputChange,onChange:this._onInputChange,className:this._classNames.field,"aria-label":r,"aria-describedby":this._isDescriptionAvailable?this._descriptionId:this.props["aria-describedby"],"aria-invalid":o,onFocus:this._onFocus,onBlur:this._onBlur}),g=function(m){return E.createElement("input",R({},m))},C=this.props.onRenderInput||g;return C(v,g)},t.prototype._validate=function(n){var r=this;if(!(this._latestValidateValue===n&&wy(this.props))){this._latestValidateValue=n;var i=this.props.onGetErrorMessage,o=i&&i(n||"");if(o!==void 0)if(typeof o=="string"||!("then"in o))this.setState({errorMessage:o}),this._notifyAfterValidate(n,o);else{var a=++this._lastValidation;o.then(function(s){a===r._lastValidation&&r.setState({errorMessage:s}),r._notifyAfterValidate(n,s)})}else this._notifyAfterValidate(n,"")}},t.prototype._notifyAfterValidate=function(n,r){n===this.value&&this.props.onNotifyValidationResult&&this.props.onNotifyValidationResult(r,n)},t.prototype._adjustInputHeight=function(){if(this._textElement.current&&this.props.autoAdjustHeight&&this.props.multiline){var n=this._textElement.current;n.style.height="",n.style.height=n.scrollHeight+"px"}},t.defaultProps={resizable:!0,deferredValidationTime:200,validateOnLoad:!0},t}(E.Component);function cm(e,t){var n=e.value,r=n===void 0?t.uncontrolledValue:n;return typeof r=="number"?String(r):r}function wy(e){return!(e.validateOnFocusIn||e.validateOnFocusOut)}var cf;function kD(){if(typeof cf!="boolean"){var e=st();if(e!=null&&e.navigator){var t=/Edg/.test(e.navigator.userAgent||"");cf=!(Ax()||t)}else cf=!0}return cf}var ID={root:"ms-TextField",description:"ms-TextField-description",errorMessage:"ms-TextField-errorMessage",field:"ms-TextField-field",fieldGroup:"ms-TextField-fieldGroup",prefix:"ms-TextField-prefix",suffix:"ms-TextField-suffix",wrapper:"ms-TextField-wrapper",revealButton:"ms-TextField-reveal",multiline:"ms-TextField--multiline",borderless:"ms-TextField--borderless",underlined:"ms-TextField--underlined",unresizable:"ms-TextField--unresizable",required:"is-required",disabled:"is-disabled",active:"is-active"};function xD(e){var t=e.underlined,n=e.disabled,r=e.focused,i=e.theme,o=i.palette,a=i.fonts;return function(){var s;return{root:[t&&n&&{color:o.neutralTertiary},t&&{fontSize:a.medium.fontSize,marginRight:8,paddingLeft:12,paddingRight:0,lineHeight:"22px",height:32},t&&r&&{selectors:(s={},s[_e]={height:31},s)}]}}}function ND(e){var t,n,r,i,o,a,s,l,u,c,f,d,h=e.theme,v=e.className,g=e.disabled,C=e.focused,m=e.required,y=e.multiline,T=e.hasLabel,S=e.borderless,w=e.underlined,b=e.hasIcon,A=e.resizable,F=e.hasErrorMessage,D=e.inputClassName,I=e.autoAdjustHeight,B=e.hasRevealButton,M=h.semanticColors,oe=h.effects,V=h.fonts,P=Mn(ID,h),z={background:M.disabledBackground,color:g?M.disabledText:M.inputPlaceholderText,display:"flex",alignItems:"center",padding:"0 10px",lineHeight:1,whiteSpace:"nowrap",flexShrink:0,selectors:(t={},t[_e]={background:"Window",color:g?"GrayText":"WindowText"},t)},Z=[{color:M.inputPlaceholderText,opacity:1,selectors:(n={},n[_e]={color:"GrayText"},n)}],H={color:M.disabledText,selectors:(r={},r[_e]={color:"GrayText"},r)};return{root:[P.root,V.medium,m&&P.required,g&&P.disabled,C&&P.active,y&&P.multiline,S&&P.borderless,w&&P.underlined,tm,{position:"relative"},v],wrapper:[P.wrapper,w&&[{display:"flex",borderBottom:"1px solid ".concat(F?M.errorText:M.inputBorder),width:"100%"},g&&{borderBottomColor:M.disabledBackground,selectors:(i={},i[_e]=R({borderColor:"GrayText"},Ri()),i)},!g&&{selectors:{":hover":{borderBottomColor:F?M.errorText:M.inputBorderHovered,selectors:(o={},o[_e]=R({borderBottomColor:"Highlight"},Ri()),o)}}},C&&[{position:"relative"},oy(F?M.errorText:M.inputFocusBorderAlt,0,"borderBottom")]]],fieldGroup:[P.fieldGroup,tm,{border:"1px solid ".concat(M.inputBorder),borderRadius:oe.roundedCorner2,background:M.inputBackground,cursor:"text",height:32,display:"flex",flexDirection:"row",alignItems:"stretch",position:"relative"},y&&{minHeight:"60px",height:"auto",display:"flex"},!C&&!g&&{selectors:{":hover":{borderColor:M.inputBorderHovered,selectors:(a={},a[_e]=R({borderColor:"Highlight"},Ri()),a)}}},C&&!w&&oy(F?M.errorText:M.inputFocusBorderAlt,oe.roundedCorner2),g&&{borderColor:M.disabledBackground,selectors:(s={},s[_e]=R({borderColor:"GrayText"},Ri()),s),cursor:"default"},S&&{border:"none"},S&&C&&{border:"none",selectors:{":after":{border:"none"}}},w&&{flex:"1 1 0px",border:"none",textAlign:"left"},w&&g&&{backgroundColor:"transparent"},F&&!w&&{borderColor:M.errorText,selectors:{"&:hover":{borderColor:M.errorText}}},!T&&m&&{selectors:(l={":before":{content:"'*'",color:M.errorText,position:"absolute",top:-5,right:-10}},l[_e]={selectors:{":before":{color:"WindowText",right:-14}}},l)}],field:[V.medium,P.field,tm,{borderRadius:0,border:"none",background:"none",backgroundColor:"transparent",color:M.inputText,padding:"0 8px",width:"100%",minWidth:0,textOverflow:"ellipsis",outline:0,selectors:(u={"&:active, &:focus, &:hover":{outline:0},"::-ms-clear":{display:"none"}},u[_e]={background:"Window",color:g?"GrayText":"WindowText"},u)},ly(Z),y&&!A&&[P.unresizable,{resize:"none"}],y&&{minHeight:"inherit",lineHeight:17,flexGrow:1,paddingTop:6,paddingBottom:6,overflow:"auto",width:"100%"},y&&I&&{overflow:"hidden"},b&&!B&&{paddingRight:24},y&&b&&{paddingRight:40},g&&[{backgroundColor:M.disabledBackground,color:M.disabledText,borderColor:M.disabledBackground},ly(H)],w&&{textAlign:"left"},C&&!S&&{selectors:(c={},c[_e]={paddingLeft:11,paddingRight:11},c)},C&&y&&!S&&{selectors:(f={},f[_e]={paddingTop:4},f)},D],icon:[y&&{paddingRight:24,alignItems:"flex-end"},{pointerEvents:"none",position:"absolute",bottom:6,right:8,top:"auto",fontSize:Zr.medium,lineHeight:18},g&&{color:M.disabledText}],description:[P.description,{color:M.bodySubtext,fontSize:V.xSmall.fontSize}],errorMessage:[P.errorMessage,ll.slideDownIn20,V.small,{color:M.errorText,margin:0,paddingTop:5,display:"flex",alignItems:"center"}],prefix:[P.prefix,z],suffix:[P.suffix,z],revealButton:[P.revealButton,"ms-Button","ms-Button--icon",fs(h,{inset:1}),{height:30,width:32,border:"none",padding:"0px 4px",backgroundColor:"transparent",color:M.link,selectors:{":hover":{outline:0,color:M.primaryButtonBackgroundHovered,backgroundColor:M.buttonBackgroundHovered,selectors:(d={},d[_e]={borderColor:"Highlight",color:"Highlight"},d)},":focus":{outline:0}}},b&&{marginRight:28}],revealSpan:{display:"flex",height:"100%",alignItems:"center"},revealIcon:{margin:"0px 4px",pointerEvents:"none",bottom:6,right:8,top:"auto",fontSize:Zr.medium,lineHeight:18},subComponentStyles:{label:xD(e)}}}var H_=Dn(wD,ND,void 0,{scope:"TextField"}),Xa;(function(e){e[e.normal=0]="normal",e[e.largeHeader=1]="largeHeader",e[e.close=2]="close"})(Xa||(Xa={}));var z_=cN.durationValue2,FD={root:"ms-Modal",main:"ms-Dialog-main",scrollableContent:"ms-Modal-scrollableContent",isOpen:"is-open",layer:"ms-Modal-Layer"},RD=function(e){var t,n=e.className,r=e.containerClassName,i=e.scrollableContentClassName,o=e.isOpen,a=e.isVisible,s=e.hasBeenOpened,l=e.modalRectangleTop,u=e.theme,c=e.topOffsetFixed,f=e.isModeless,d=e.layerClassName,h=e.isDefaultDragHandle,v=e.windowInnerHeight,g=u.palette,C=u.effects,m=u.fonts,y=Mn(FD,u);return{root:[y.root,m.medium,{backgroundColor:"transparent",position:"fixed",height:"100%",width:"100%",display:"flex",alignItems:"center",justifyContent:"center",opacity:0,pointerEvents:"none",transition:"opacity ".concat(z_)},c&&typeof l=="number"&&s&&{alignItems:"flex-start"},o&&y.isOpen,a&&{opacity:1},a&&!f&&{pointerEvents:"auto"},n],main:[y.main,{boxShadow:C.elevation64,borderRadius:C.roundedCorner2,backgroundColor:g.white,boxSizing:"border-box",position:"relative",textAlign:"left",outline:"3px solid transparent",maxHeight:"calc(100% - 32px)",maxWidth:"calc(100% - 32px)",minHeight:"176px",minWidth:"288px",overflowY:"auto",zIndex:f?Pl.Layer:void 0},f&&{pointerEvents:"auto"},c&&typeof l=="number"&&s&&{top:l},h&&{cursor:"move"},r],scrollableContent:[y.scrollableContent,{overflowY:"auto",flexGrow:1,maxHeight:"100vh",selectors:(t={},t["@supports (-webkit-overflow-scrolling: touch)"]={maxHeight:v},t)},i],layer:f&&[d,y.layer,{pointerEvents:"none"}],keyboardMoveIconContainer:{position:"absolute",display:"flex",justifyContent:"center",width:"100%",padding:"3px 0px"},keyboardMoveIcon:{fontSize:m.xLargePlus.fontSize,width:"24px"}}},DD=Rn(),MD=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;Aa(r);var i=r.props.allowTouchBodyScroll,o=i===void 0?!1:i;return r._allowTouchBodyScroll=o,r}return t.prototype.componentDidMount=function(){!this._allowTouchBodyScroll&&nI()},t.prototype.componentWillUnmount=function(){!this._allowTouchBodyScroll&&rI()},t.prototype.render=function(){var n=this.props,r=n.isDarkThemed,i=n.className,o=n.theme,a=n.styles,s=ht(this.props,Cs),l=DD(a,{theme:o,className:i,isDark:r});return E.createElement("div",R({},s,{className:l.root}))},t}(E.Component),PD={root:"ms-Overlay",rootDark:"ms-Overlay--dark"},OD=function(e){var t,n=e.className,r=e.theme,i=e.isNone,o=e.isDark,a=r.palette,s=Mn(PD,r);return{root:[s.root,r.fonts.medium,{backgroundColor:a.whiteTranslucent40,top:0,right:0,bottom:0,left:0,position:"absolute",selectors:(t={},t[_e]={border:"1px solid WindowText",opacity:0},t)},i&&{visibility:"hidden"},o&&[s.rootDark,{backgroundColor:a.blackTranslucent40}],n]}},LD=Dn(MD,OD,void 0,{scope:"Overlay"}),BD=It(function(e,t){return{root:Ii(e,t&&{touchAction:"none",selectors:{"& *":{userSelect:"none"}}})}}),Cu={touch:{start:"touchstart",move:"touchmove",stop:"touchend"},mouse:{start:"mousedown",move:"mousemove",stop:"mouseup"}},HD=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return r._currentEventType=Cu.mouse,r._events=[],r._onMouseDown=function(i){var o=E.Children.only(r.props.children).props.onMouseDown;return o&&o(i),r._currentEventType=Cu.mouse,r._onDragStart(i)},r._onMouseUp=function(i){var o=E.Children.only(r.props.children).props.onMouseUp;return o&&o(i),r._currentEventType=Cu.mouse,r._onDragStop(i)},r._onTouchStart=function(i){var o=E.Children.only(r.props.children).props.onTouchStart;return o&&o(i),r._currentEventType=Cu.touch,r._onDragStart(i)},r._onTouchEnd=function(i){var o=E.Children.only(r.props.children).props.onTouchEnd;o&&o(i),r._currentEventType=Cu.touch,r._onDragStop(i)},r._onDragStart=function(i){if(typeof i.button=="number"&&i.button!==0)return!1;if(!(r.props.handleSelector&&!r._matchesSelector(i.target,r.props.handleSelector)||r.props.preventDragSelector&&r._matchesSelector(i.target,r.props.preventDragSelector))){r._touchId=r._getTouchId(i);var o=r._getControlPosition(i);if(o!==void 0){var a=r._createDragDataFromPosition(o);r.props.onStart&&r.props.onStart(i,a),r.setState({isDragging:!0,lastPosition:o}),r._events=[xi(document.body,r._currentEventType.move,r._onDrag,!0),xi(document.body,r._currentEventType.stop,r._onDragStop,!0)]}}},r._onDrag=function(i){i.type==="touchmove"&&i.preventDefault();var o=r._getControlPosition(i);if(o){var a=r._createUpdatedDragData(r._createDragDataFromPosition(o)),s=a.position;r.props.onDragChange&&r.props.onDragChange(i,a),r.setState({position:s,lastPosition:o})}},r._onDragStop=function(i){if(r.state.isDragging){var o=r._getControlPosition(i);if(o){var a=r._createDragDataFromPosition(o);r.setState({isDragging:!1,lastPosition:void 0}),r.props.onStop&&r.props.onStop(i,a),r.props.position&&r.setState({position:r.props.position}),r._events.forEach(function(s){return s()})}}},r.state={isDragging:!1,position:r.props.position||{x:0,y:0},lastPosition:void 0},r}return t.prototype.componentDidUpdate=function(n){this.props.position&&(!n.position||this.props.position!==n.position)&&this.setState({position:this.props.position})},t.prototype.componentWillUnmount=function(){this._events.forEach(function(n){return n()})},t.prototype.render=function(){var n=E.Children.only(this.props.children),r=n.props,i=this.props.position,o=this.state,a=o.position,s=o.isDragging,l=a.x,u=a.y;return i&&!s&&(l=i.x,u=i.y),E.cloneElement(n,{style:R(R({},r.style),{transform:"translate(".concat(l,"px, ").concat(u,"px)")}),className:BD(r.className,this.state.isDragging).root,onMouseDown:this._onMouseDown,onMouseUp:this._onMouseUp,onTouchStart:this._onTouchStart,onTouchEnd:this._onTouchEnd})},t.prototype._getControlPosition=function(n){var r=this._getActiveTouch(n);if(!(this._touchId!==void 0&&!r)){var i=r||n;return{x:i.clientX,y:i.clientY}}},t.prototype._getActiveTouch=function(n){return n.targetTouches&&this._findTouchInTouchList(n.targetTouches)||n.changedTouches&&this._findTouchInTouchList(n.changedTouches)},t.prototype._getTouchId=function(n){var r=n.targetTouches&&n.targetTouches[0]||n.changedTouches&&n.changedTouches[0];if(r)return r.identifier},t.prototype._matchesSelector=function(n,r){if(!n||n===document.body)return!1;var i=n.matches||n.webkitMatchesSelector||n.msMatchesSelector;return i?i.call(n,r)||this._matchesSelector(n.parentElement,r):!1},t.prototype._findTouchInTouchList=function(n){if(this._touchId!==void 0){for(var r=0;r=(z||ds.small)&&E.createElement(E_,R({ref:it},pi),E.createElement(s9,R({role:Cr?"alertdialog":"dialog",ariaLabelledBy:B,ariaDescribedBy:oe,onDismiss:A,shouldRestoreFocus:!y,enableAriaHiddenSiblings:ee,"aria-modal":!H},X),E.createElement("div",{className:Ln.root,role:H?void 0:"document"},!H&&E.createElement(LD,R({"aria-hidden":!0,isDarkThemed:b,onClick:T?void 0:A,allowTouchBodyScroll:l},D)),U?E.createElement(HD,{handleSelector:U.dragHandleSelector||"#".concat(Bt),preventDragSelector:"button",onStart:dp,onDragChange:Pc,onStop:hp,position:Fa},Lc):Lc)))||null});U_.displayName="Modal";var K_=Dn(U_,RD,void 0,{scope:"Modal",fields:["theme","styles","enableAriaHiddenSiblings"]});K_.displayName="Modal";var jD=Rn(),GD=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return Aa(r),r}return t.prototype.render=function(){var n=this.props,r=n.className,i=n.styles,o=n.theme;return this._classNames=jD(i,{theme:o,className:r}),E.createElement("div",{className:this._classNames.actions},E.createElement("div",{className:this._classNames.actionsRight},this._renderChildrenAsActions()))},t.prototype._renderChildrenAsActions=function(){var n=this;return E.Children.map(this.props.children,function(r){return r?E.createElement("span",{className:n._classNames.action},r):null})},t}(E.Component),$D={actions:"ms-Dialog-actions",action:"ms-Dialog-action",actionsRight:"ms-Dialog-actionsRight"},VD=function(e){var t=e.className,n=e.theme,r=Mn($D,n);return{actions:[r.actions,{position:"relative",width:"100%",minHeight:"24px",lineHeight:"24px",margin:"16px 0 0",fontSize:"0",selectors:{".ms-Button":{lineHeight:"normal",verticalAlign:"middle"}}},t],action:[r.action,{margin:"0 4px"}],actionsRight:[r.actionsRight,{alignItems:"center",display:"flex",fontSize:"0",justifyContent:"flex-end",marginRight:"-4px"}]}},qD=Dn(GD,VD,void 0,{scope:"DialogFooter"}),YD=Rn(),QD=E.createElement(qD,null).type,XD=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return Aa(r),r}return t.prototype.render=function(){var n=this.props,r=n.showCloseButton,i=n.className,o=n.closeButtonAriaLabel,a=n.onDismiss,s=n.subTextId,l=n.subText,u=n.titleProps,c=u===void 0?{}:u,f=n.titleId,d=n.title,h=n.type,v=n.styles,g=n.theme,C=n.draggableHeaderClassName,m=YD(v,{theme:g,className:i,isLargeHeader:h===Xa.largeHeader,isClose:h===Xa.close,draggableHeaderClassName:C}),y=this._groupChildren(),T;return l&&(T=E.createElement("p",{className:m.subText,id:s},l)),E.createElement("div",{className:m.content},E.createElement("div",{className:m.header},E.createElement("div",R({id:f,role:"heading","aria-level":1},c,{className:qn(m.title,c.className)}),d),E.createElement("div",{className:m.topButton},this.props.topButtonsProps.map(function(S,w){return E.createElement(Ay,R({key:S.uniqueId||w},S))}),(h===Xa.close||r&&h!==Xa.largeHeader)&&E.createElement(Ay,{className:m.button,iconProps:{iconName:"Cancel"},ariaLabel:o,onClick:a}))),E.createElement("div",{className:m.inner},E.createElement("div",{className:m.innerContent},T,y.contents),y.footers))},t.prototype._groupChildren=function(){var n={footers:[],contents:[]};return E.Children.map(this.props.children,function(r){typeof r=="object"&&r!==null&&r.type===QD?n.footers.push(r):n.contents.push(r)}),n},t.defaultProps={showCloseButton:!1,className:"",topButtonsProps:[],closeButtonAriaLabel:"Close"},t=Yv([I_],t),t}(E.Component),ZD={contentLgHeader:"ms-Dialog-lgHeader",close:"ms-Dialog--close",subText:"ms-Dialog-subText",header:"ms-Dialog-header",headerLg:"ms-Dialog--lgHeader",button:"ms-Dialog-button ms-Dialog-button--close",inner:"ms-Dialog-inner",content:"ms-Dialog-content",title:"ms-Dialog-title"},JD=function(e){var t,n,r,i=e.className,o=e.theme,a=e.isLargeHeader,s=e.isClose,l=e.hidden,u=e.isMultiline,c=e.draggableHeaderClassName,f=o.palette,d=o.fonts,h=o.effects,v=o.semanticColors,g=Mn(ZD,o);return{content:[a&&[g.contentLgHeader,{borderTop:"4px solid ".concat(f.themePrimary)}],s&&g.close,{flexGrow:1,overflowY:"hidden"},i],subText:[g.subText,d.medium,{margin:"0 0 24px 0",color:v.bodySubtext,lineHeight:"1.5",wordWrap:"break-word",fontWeight:et.regular}],header:[g.header,{position:"relative",width:"100%",boxSizing:"border-box"},s&&g.close,c&&[c,{cursor:"move"}]],button:[g.button,l&&{selectors:{".ms-Icon.ms-Icon--Cancel":{color:v.buttonText,fontSize:Zr.medium}}}],inner:[g.inner,{padding:"0 24px 24px",selectors:(t={},t["@media (min-width: ".concat(Jp,"px) and (max-width: ").concat(em,"px)")]={padding:"0 16px 16px"},t)}],innerContent:[g.content,{position:"relative",width:"100%"}],title:[g.title,d.xLarge,{color:v.bodyText,margin:"0",minHeight:d.xLarge.fontSize,padding:"16px 46px 20px 24px",lineHeight:"normal",selectors:(n={},n["@media (min-width: ".concat(Jp,"px) and (max-width: ").concat(em,"px)")]={padding:"16px 46px 16px 16px"},n)},a&&{color:v.menuHeader},u&&{fontSize:d.xxLarge.fontSize}],topButton:[{display:"flex",flexDirection:"row",flexWrap:"nowrap",position:"absolute",top:"0",right:"0",padding:"15px 15px 0 0",selectors:(r={"> *":{flex:"0 0 auto"},".ms-Dialog-button":{color:v.buttonText},".ms-Dialog-button:hover":{color:v.buttonTextHovered,borderRadius:h.roundedCorner2}},r["@media (min-width: ".concat(Jp,"px) and (max-width: ").concat(em,"px)")]={padding:"15px 8px 0 0"},r)}]}},eM=Dn(XD,JD,void 0,{scope:"DialogContent"}),tM=Rn(),nM={isDarkOverlay:!1,isBlocking:!1,className:"",containerClassName:"",topOffsetFixed:!1,enableAriaHiddenSiblings:!0},rM={type:Xa.normal,className:"",topButtonsProps:[]},iM=function(e){yt(t,e);function t(n){var r=e.call(this,n)||this;return r._getSubTextId=function(){var i=r.props,o=i.ariaDescribedById,a=i.modalProps,s=i.dialogContentProps,l=i.subText,u=a&&a.subtitleAriaId||o;return u||(u=(s&&s.subText||l)&&r._defaultSubTextId),u},r._getTitleTextId=function(){var i=r.props,o=i.ariaLabelledById,a=i.modalProps,s=i.dialogContentProps,l=i.title,u=a&&a.titleAriaId||o;return u||(u=(s&&s.title||l)&&r._defaultTitleTextId),u},r._id=bn("Dialog"),r._defaultTitleTextId=r._id+"-title",r._defaultSubTextId=r._id+"-subText",r}return t.prototype.render=function(){var n,r,i,o=this.props,a=o.className,s=o.containerClassName,l=o.contentClassName,u=o.elementToFocusOnDismiss,c=o.firstFocusableSelector,f=o.forceFocusInsideTrap,d=o.styles,h=o.hidden,v=o.disableRestoreFocus,g=v===void 0?o.ignoreExternalFocusing:v,C=o.isBlocking,m=o.isClickableOutsideFocusTrap,y=o.isDarkOverlay,T=o.isOpen,S=T===void 0?!h:T,w=o.onDismiss,b=o.onDismissed,A=o.onLayerDidMount,F=o.responsiveMode,D=o.subText,I=o.theme,B=o.title,M=o.topButtonsProps,oe=o.type,V=o.minWidth,P=o.maxWidth,z=o.modalProps,Z=R({onLayerDidMount:A},z==null?void 0:z.layerProps),H,U;z!=null&&z.dragOptions&&!(!((n=z.dragOptions)===null||n===void 0)&&n.dragHandleSelector)&&(U=R({},z.dragOptions),H="ms-Dialog-draggable-header",U.dragHandleSelector=".".concat(H));var _=R(R(R(R({},nM),{elementToFocusOnDismiss:u,firstFocusableSelector:c,forceFocusInsideTrap:f,disableRestoreFocus:g,isClickableOutsideFocusTrap:m,responsiveMode:F,className:a,containerClassName:s,isBlocking:C,isDarkOverlay:y,onDismissed:b}),z),{dragOptions:U,layerProps:Z,isOpen:S}),ee=R(R(R({className:l,subText:D,title:B,topButtonsProps:M,type:oe},rM),o.dialogContentProps),{draggableHeaderClassName:H,titleProps:R({id:((r=o.dialogContentProps)===null||r===void 0?void 0:r.titleId)||this._defaultTitleTextId},(i=o.dialogContentProps)===null||i===void 0?void 0:i.titleProps)}),X=tM(d,{theme:I,className:_.className,containerClassName:_.containerClassName,hidden:h,dialogDefaultMinWidth:V,dialogDefaultMaxWidth:P});return E.createElement(K_,R({},_,{className:X.root,containerClassName:X.main,onDismiss:w||_.onDismiss,subtitleAriaId:this._getSubTextId(),titleAriaId:this._getTitleTextId()}),E.createElement(eM,R({subTextId:this._defaultSubTextId,showCloseButton:_.isBlocking,onDismiss:w},ee),o.children))},t.defaultProps={hidden:!0},t=Yv([I_],t),t}(E.Component),oM={root:"ms-Dialog"},aM=function(e){var t,n=e.className,r=e.containerClassName,i=e.dialogDefaultMinWidth,o=i===void 0?"288px":i,a=e.dialogDefaultMaxWidth,s=a===void 0?"340px":a,l=e.hidden,u=e.theme,c=Mn(oM,u);return{root:[c.root,u.fonts.medium,n],main:[{width:o,outline:"3px solid transparent",selectors:(t={},t["@media (min-width: ".concat(J8,"px)")]={width:"auto",maxWidth:s,minWidth:o},t)},!l&&{display:"flex"},r]}},W_=Dn(iM,aM,void 0,{scope:"Dialog"});W_.displayName="Dialog";function sM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons"',src:"url('".concat(e,"fabric-icons-a13498cf.woff') format('woff')")},icons:{GlobalNavButton:"",ChevronDown:"",ChevronUp:"",Edit:"",Add:"",Cancel:"",More:"",Settings:"",Mail:"",Filter:"",Search:"",Share:"",BlockedSite:"",FavoriteStar:"",FavoriteStarFill:"",CheckMark:"",Delete:"",ChevronLeft:"",ChevronRight:"",Calendar:"",Megaphone:"",Undo:"",Flag:"",Page:"",Pinned:"",View:"",Clear:"",Download:"",Upload:"",Folder:"",Sort:"",AlignRight:"",AlignLeft:"",Tag:"",AddFriend:"",Info:"",SortLines:"",List:"",CircleRing:"",Heart:"",HeartFill:"",Tiles:"",Embed:"",Glimmer:"",Ascending:"",Descending:"",SortUp:"",SortDown:"",SyncToPC:"",LargeGrid:"",SkypeCheck:"",SkypeClock:"",SkypeMinus:"",ClearFilter:"",Flow:"",StatusCircleCheckmark:"",MoreVertical:""}};Lt(n,t)}function lM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-0"',src:"url('".concat(e,"fabric-icons-0-467ee27f.woff') format('woff')")},icons:{PageLink:"",CommentSolid:"",ChangeEntitlements:"",Installation:"",WebAppBuilderModule:"",WebAppBuilderFragment:"",WebAppBuilderSlot:"",BullseyeTargetEdit:"",WebAppBuilderFragmentCreate:"",PageData:"",PageHeaderEdit:"",ProductList:"",UnpublishContent:"",DependencyAdd:"",DependencyRemove:"",EntitlementPolicy:"",EntitlementRedemption:"",SchoolDataSyncLogo:"",PinSolid12:"",PinSolidOff12:"",AddLink:"",SharepointAppIcon16:"",DataflowsLink:"",TimePicker:"",UserWarning:"",ComplianceAudit:"",InternetSharing:"",Brightness:"",MapPin:"",Airplane:"",Tablet:"",QuickNote:"",Video:"",People:"",Phone:"",Pin:"",Shop:"",Stop:"",Link:"",AllApps:"",Zoom:"",ZoomOut:"",Microphone:"",Camera:"",Attach:"",Send:"",FavoriteList:"",PageSolid:"",Forward:"",Back:"",Refresh:"",Lock:"",ReportHacked:"",EMI:"",MiniLink:"",Blocked:"",ReadingMode:"",Favicon:"",Remove:"",Checkbox:"",CheckboxComposite:"",CheckboxFill:"",CheckboxIndeterminate:"",CheckboxCompositeReversed:"",BackToWindow:"",FullScreen:"",Print:"",Up:"",Down:"",OEM:"",Save:"",ReturnKey:"",Cloud:"",Flashlight:"",CommandPrompt:"",Sad:"",RealEstate:"",SIPMove:"",EraseTool:"",GripperTool:"",Dialpad:"",PageLeft:"",PageRight:"",MultiSelect:"",KeyboardClassic:"",Play:"",Pause:"",InkingTool:"",Emoji2:"",GripperBarHorizontal:"",System:"",Personalize:"",SearchAndApps:"",Globe:"",EaseOfAccess:"",ContactInfo:"",Unpin:"",Contact:"",Memo:"",IncomingCall:""}};Lt(n,t)}function uM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-1"',src:"url('".concat(e,"fabric-icons-1-4d521695.woff') format('woff')")},icons:{Paste:"",WindowsLogo:"",Error:"",GripperBarVertical:"",Unlock:"",Slideshow:"",Trim:"",AutoEnhanceOn:"",AutoEnhanceOff:"",Color:"",SaveAs:"",Light:"",Filters:"",AspectRatio:"",Contrast:"",Redo:"",Crop:"",PhotoCollection:"",Album:"",Rotate:"",PanoIndicator:"",Translate:"",RedEye:"",ViewOriginal:"",ThumbnailView:"",Package:"",Telemarketer:"",Warning:"",Financial:"",Education:"",ShoppingCart:"",Train:"",Move:"",TouchPointer:"",Merge:"",TurnRight:"",Ferry:"",Highlight:"",PowerButton:"",Tab:"",Admin:"",TVMonitor:"",Speakers:"",Game:"",HorizontalTabKey:"",UnstackSelected:"",StackIndicator:"",Nav2DMapView:"",StreetsideSplitMinimize:"",Car:"",Bus:"",EatDrink:"",SeeDo:"",LocationCircle:"",Home:"",SwitcherStartEnd:"",ParkingLocation:"",IncidentTriangle:"",Touch:"",MapDirections:"",CaretHollow:"",CaretSolid:"",History:"",Location:"",MapLayers:"",SearchNearby:"",Work:"",Recent:"",Hotel:"",Bank:"",LocationDot:"",Dictionary:"",ChromeBack:"",FolderOpen:"",PinnedFill:"",RevToggleKey:"",USB:"",Previous:"",Next:"",Sync:"",Help:"",Emoji:"",MailForward:"",ClosePane:"",OpenPane:"",PreviewLink:"",ZoomIn:"",Bookmarks:"",Document:"",ProtectedDocument:"",OpenInNewWindow:"",MailFill:"",ViewAll:"",Switch:"",Rename:"",Go:"",Remote:"",SelectAll:"",Orientation:"",Import:""}};Lt(n,t)}function cM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-2"',src:"url('".concat(e,"fabric-icons-2-63c99abf.woff') format('woff')")},icons:{Picture:"",ChromeClose:"",ShowResults:"",Message:"",CalendarDay:"",CalendarWeek:"",MailReplyAll:"",Read:"",Cut:"",PaymentCard:"",Copy:"",Important:"",MailReply:"",GotoToday:"",Font:"",FontColor:"",FolderFill:"",Permissions:"",DisableUpdates:"",Unfavorite:"",Italic:"",Underline:"",Bold:"",MoveToFolder:"",Dislike:"",Like:"",AlignCenter:"",OpenFile:"",ClearSelection:"",FontDecrease:"",FontIncrease:"",FontSize:"",CellPhone:"",RepeatOne:"",RepeatAll:"",Calculator:"",Library:"",PostUpdate:"",NewFolder:"",CalendarReply:"",UnsyncFolder:"",SyncFolder:"",BlockContact:"",Accept:"",BulletedList:"",Preview:"",News:"",Chat:"",Group:"",World:"",Comment:"",DockLeft:"",DockRight:"",Repair:"",Accounts:"",Street:"",RadioBullet:"",Stopwatch:"",Clock:"",WorldClock:"",AlarmClock:"",Photo:"",ActionCenter:"",Hospital:"",Timer:"",FullCircleMask:"",LocationFill:"",ChromeMinimize:"",ChromeRestore:"",Annotation:"",Fingerprint:"",Handwriting:"",ChromeFullScreen:"",Completed:"",Label:"",FlickDown:"",FlickUp:"",FlickLeft:"",FlickRight:"",MiniExpand:"",MiniContract:"",Streaming:"",MusicInCollection:"",OneDriveLogo:"",CompassNW:"",Code:"",LightningBolt:"",CalculatorMultiply:"",CalculatorAddition:"",CalculatorSubtract:"",CalculatorPercentage:"",CalculatorEqualTo:"",PrintfaxPrinterFile:"",StorageOptical:"",Communications:"",Headset:"",Health:"",Webcam2:"",FrontCamera:"",ChevronUpSmall:""}};Lt(n,t)}function fM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-3"',src:"url('".concat(e,"fabric-icons-3-089e217a.woff') format('woff')")},icons:{ChevronDownSmall:"",ChevronLeftSmall:"",ChevronRightSmall:"",ChevronUpMed:"",ChevronDownMed:"",ChevronLeftMed:"",ChevronRightMed:"",Devices2:"",PC1:"",PresenceChickletVideo:"",Reply:"",HalfAlpha:"",ConstructionCone:"",DoubleChevronLeftMed:"",Volume0:"",Volume1:"",Volume2:"",Volume3:"",Chart:"",Robot:"",Manufacturing:"",LockSolid:"",FitPage:"",FitWidth:"",BidiLtr:"",BidiRtl:"",RightDoubleQuote:"",Sunny:"",CloudWeather:"",Cloudy:"",PartlyCloudyDay:"",PartlyCloudyNight:"",ClearNight:"",RainShowersDay:"",Rain:"",Thunderstorms:"",RainSnow:"",Snow:"",BlowingSnow:"",Frigid:"",Fog:"",Squalls:"",Duststorm:"",Unknown:"",Precipitation:"",Ribbon:"",AreaChart:"",Assign:"",FlowChart:"",CheckList:"",Diagnostic:"",Generate:"",LineChart:"",Equalizer:"",BarChartHorizontal:"",BarChartVertical:"",Freezing:"",FunnelChart:"",Processing:"",Quantity:"",ReportDocument:"",StackColumnChart:"",SnowShowerDay:"",HailDay:"",WorkFlow:"",HourGlass:"",StoreLogoMed20:"",TimeSheet:"",TriangleSolid:"",UpgradeAnalysis:"",VideoSolid:"",RainShowersNight:"",SnowShowerNight:"",Teamwork:"",HailNight:"",PeopleAdd:"",Glasses:"",DateTime2:"",Shield:"",Header1:"",PageAdd:"",NumberedList:"",PowerBILogo:"",Info2:"",MusicInCollectionFill:"",Asterisk:"",ErrorBadge:"",CircleFill:"",Record2:"",AllAppsMirrored:"",BookmarksMirrored:"",BulletedListMirrored:"",CaretHollowMirrored:"",CaretSolidMirrored:"",ChromeBackMirrored:"",ClearSelectionMirrored:"",ClosePaneMirrored:"",DockLeftMirrored:"",DoubleChevronLeftMedMirrored:"",GoMirrored:""}};Lt(n,t)}function dM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-4"',src:"url('".concat(e,"fabric-icons-4-a656cc0a.woff') format('woff')")},icons:{HelpMirrored:"",ImportMirrored:"",ImportAllMirrored:"",ListMirrored:"",MailForwardMirrored:"",MailReplyMirrored:"",MailReplyAllMirrored:"",MiniContractMirrored:"",MiniExpandMirrored:"",OpenPaneMirrored:"",ParkingLocationMirrored:"",SendMirrored:"",ShowResultsMirrored:"",ThumbnailViewMirrored:"",Media:"",Devices3:"",Focus:"",VideoLightOff:"",Lightbulb:"",StatusTriangle:"",VolumeDisabled:"",Puzzle:"",EmojiNeutral:"",EmojiDisappointed:"",HomeSolid:"",Ringer:"",PDF:"",HeartBroken:"",StoreLogo16:"",MultiSelectMirrored:"",Broom:"",AddToShoppingList:"",Cocktails:"",Wines:"",Articles:"",Cycling:"",DietPlanNotebook:"",Pill:"",ExerciseTracker:"",HandsFree:"",Medical:"",Running:"",Weights:"",Trackers:"",AddNotes:"",AllCurrency:"",BarChart4:"",CirclePlus:"",Coffee:"",Cotton:"",Market:"",Money:"",PieDouble:"",PieSingle:"",RemoveFilter:"",Savings:"",Sell:"",StockDown:"",StockUp:"",Lamp:"",Source:"",MSNVideos:"",Cricket:"",Golf:"",Baseball:"",Soccer:"",MoreSports:"",AutoRacing:"",CollegeHoops:"",CollegeFootball:"",ProFootball:"",ProHockey:"",Rugby:"",SubstitutionsIn:"",Tennis:"",Arrivals:"",Design:"",Website:"",Drop:"",HistoricalWeather:"",SkiResorts:"",Snowflake:"",BusSolid:"",FerrySolid:"",AirplaneSolid:"",TrainSolid:"",Ticket:"",WifiWarning4:"",Devices4:"",AzureLogo:"",BingLogo:"",MSNLogo:"",OutlookLogoInverse:"",OfficeLogo:"",SkypeLogo:"",Door:"",EditMirrored:"",GiftCard:"",DoubleBookmark:"",StatusErrorFull:""}};Lt(n,t)}function hM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-5"',src:"url('".concat(e,"fabric-icons-5-f95ba260.woff') format('woff')")},icons:{Certificate:"",FastForward:"",Rewind:"",Photo2:"",OpenSource:"",Movers:"",CloudDownload:"",Family:"",WindDirection:"",Bug:"",SiteScan:"",BrowserScreenShot:"",F12DevTools:"",CSS:"",JS:"",DeliveryTruck:"",ReminderPerson:"",ReminderGroup:"",ReminderTime:"",TabletMode:"",Umbrella:"",NetworkTower:"",CityNext:"",CityNext2:"",Section:"",OneNoteLogoInverse:"",ToggleFilled:"",ToggleBorder:"",SliderThumb:"",ToggleThumb:"",Documentation:"",Badge:"",Giftbox:"",VisualStudioLogo:"",HomeGroup:"",ExcelLogoInverse:"",WordLogoInverse:"",PowerPointLogoInverse:"",Cafe:"",SpeedHigh:"",Commitments:"",ThisPC:"",MusicNote:"",MicOff:"",PlaybackRate1x:"",EdgeLogo:"",CompletedSolid:"",AlbumRemove:"",MessageFill:"",TabletSelected:"",MobileSelected:"",LaptopSelected:"",TVMonitorSelected:"",DeveloperTools:"",Shapes:"",InsertTextBox:"",LowerBrightness:"",WebComponents:"",OfflineStorage:"",DOM:"",CloudUpload:"",ScrollUpDown:"",DateTime:"",Event:"",Cake:"",Org:"",PartyLeader:"",DRM:"",CloudAdd:"",AppIconDefault:"",Photo2Add:"",Photo2Remove:"",Calories:"",POI:"",AddTo:"",RadioBtnOff:"",RadioBtnOn:"",ExploreContent:"",Product:"",ProgressLoopInner:"",ProgressLoopOuter:"",Blocked2:"",FangBody:"",Toolbox:"",PageHeader:"",ChatInviteFriend:"",Brush:"",Shirt:"",Crown:"",Diamond:"",ScaleUp:"",QRCode:"",Feedback:"",SharepointLogoInverse:"",YammerLogo:"",Hide:"",Uneditable:"",ReturnToSession:"",OpenFolderHorizontal:"",CalendarMirrored:""}};Lt(n,t)}function pM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-6"',src:"url('".concat(e,"fabric-icons-6-ef6fd590.woff') format('woff')")},icons:{SwayLogoInverse:"",OutOfOffice:"",Trophy:"",ReopenPages:"",EmojiTabSymbols:"",AADLogo:"",AccessLogo:"",AdminALogoInverse32:"",AdminCLogoInverse32:"",AdminDLogoInverse32:"",AdminELogoInverse32:"",AdminLLogoInverse32:"",AdminMLogoInverse32:"",AdminOLogoInverse32:"",AdminPLogoInverse32:"",AdminSLogoInverse32:"",AdminYLogoInverse32:"",DelveLogoInverse:"",ExchangeLogoInverse:"",LyncLogo:"",OfficeVideoLogoInverse:"",SocialListeningLogo:"",VisioLogoInverse:"",Balloons:"",Cat:"",MailAlert:"",MailCheck:"",MailLowImportance:"",MailPause:"",MailRepeat:"",SecurityGroup:"",Table:"",VoicemailForward:"",VoicemailReply:"",Waffle:"",RemoveEvent:"",EventInfo:"",ForwardEvent:"",WipePhone:"",AddOnlineMeeting:"",JoinOnlineMeeting:"",RemoveLink:"",PeopleBlock:"",PeopleRepeat:"",PeopleAlert:"",PeoplePause:"",TransferCall:"",AddPhone:"",UnknownCall:"",NoteReply:"",NoteForward:"",NotePinned:"",RemoveOccurrence:"",Timeline:"",EditNote:"",CircleHalfFull:"",Room:"",Unsubscribe:"",Subscribe:"",HardDrive:"",RecurringTask:"",TaskManager:"",TaskManagerMirrored:"",Combine:"",Split:"",DoubleChevronUp:"",DoubleChevronLeft:"",DoubleChevronRight:"",TextBox:"",TextField:"",NumberField:"",Dropdown:"",PenWorkspace:"",BookingsLogo:"",ClassNotebookLogoInverse:"",DelveAnalyticsLogo:"",DocsLogoInverse:"",Dynamics365Logo:"",DynamicSMBLogo:"",OfficeAssistantLogo:"",OfficeStoreLogo:"",OneNoteEduLogoInverse:"",PlannerLogo:"",PowerApps:"",Suitcase:"",ProjectLogoInverse:"",CaretLeft8:"",CaretRight8:"",CaretUp8:"",CaretDown8:"",CaretLeftSolid8:"",CaretRightSolid8:"",CaretUpSolid8:"",CaretDownSolid8:"",ClearFormatting:"",Superscript:"",Subscript:"",Strikethrough:"",Export:"",ExportMirrored:""}};Lt(n,t)}function mM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-7"',src:"url('".concat(e,"fabric-icons-7-2b97bb99.woff') format('woff')")},icons:{SingleBookmark:"",SingleBookmarkSolid:"",DoubleChevronDown:"",FollowUser:"",ReplyAll:"",WorkforceManagement:"",RecruitmentManagement:"",Questionnaire:"",ManagerSelfService:"",ProductionFloorManagement:"",ProductRelease:"",ProductVariant:"",ReplyMirrored:"",ReplyAllMirrored:"",Medal:"",AddGroup:"",QuestionnaireMirrored:"",CloudImportExport:"",TemporaryUser:"",CaretSolid16:"",GroupedDescending:"",GroupedAscending:"",AwayStatus:"",MyMoviesTV:"",GenericScan:"",AustralianRules:"",WifiEthernet:"",TrackersMirrored:"",DateTimeMirrored:"",StopSolid:"",DoubleChevronUp12:"",DoubleChevronDown12:"",DoubleChevronLeft12:"",DoubleChevronRight12:"",CalendarAgenda:"",ConnectVirtualMachine:"",AddEvent:"",AssetLibrary:"",DataConnectionLibrary:"",DocLibrary:"",FormLibrary:"",FormLibraryMirrored:"",ReportLibrary:"",ReportLibraryMirrored:"",ContactCard:"",CustomList:"",CustomListMirrored:"",IssueTracking:"",IssueTrackingMirrored:"",PictureLibrary:"",OfficeAddinsLogo:"",OfflineOneDriveParachute:"",OfflineOneDriveParachuteDisabled:"",TriangleSolidUp12:"",TriangleSolidDown12:"",TriangleSolidLeft12:"",TriangleSolidRight12:"",TriangleUp12:"",TriangleDown12:"",TriangleLeft12:"",TriangleRight12:"",ArrowUpRight8:"",ArrowDownRight8:"",DocumentSet:"",GoToDashboard:"",DelveAnalytics:"",ArrowUpRightMirrored8:"",ArrowDownRightMirrored8:"",CompanyDirectory:"",OpenEnrollment:"",CompanyDirectoryMirrored:"",OneDriveAdd:"",ProfileSearch:"",Header2:"",Header3:"",Header4:"",RingerSolid:"",Eyedropper:"",MarketDown:"",CalendarWorkWeek:"",SidePanel:"",GlobeFavorite:"",CaretTopLeftSolid8:"",CaretTopRightSolid8:"",ViewAll2:"",DocumentReply:"",PlayerSettings:"",ReceiptForward:"",ReceiptReply:"",ReceiptCheck:"",Fax:"",RecurringEvent:"",ReplyAlt:"",ReplyAllAlt:"",EditStyle:"",EditMail:"",Lifesaver:"",LifesaverLock:"",InboxCheck:"",FolderSearch:""}};Lt(n,t)}function gM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-8"',src:"url('".concat(e,"fabric-icons-8-6fdf1528.woff') format('woff')")},icons:{CollapseMenu:"",ExpandMenu:"",Boards:"",SunAdd:"",SunQuestionMark:"",LandscapeOrientation:"",DocumentSearch:"",PublicCalendar:"",PublicContactCard:"",PublicEmail:"",PublicFolder:"",WordDocument:"",PowerPointDocument:"",ExcelDocument:"",GroupedList:"",ClassroomLogo:"",Sections:"",EditPhoto:"",Starburst:"",ShareiOS:"",AirTickets:"",PencilReply:"",Tiles2:"",SkypeCircleCheck:"",SkypeCircleClock:"",SkypeCircleMinus:"",SkypeMessage:"",ClosedCaption:"",ATPLogo:"",OfficeFormsLogoInverse:"",RecycleBin:"",EmptyRecycleBin:"",Hide2:"",Breadcrumb:"",BirthdayCake:"",TimeEntry:"",CRMProcesses:"",PageEdit:"",PageArrowRight:"",PageRemove:"",Database:"",DataManagementSettings:"",CRMServices:"",EditContact:"",ConnectContacts:"",AppIconDefaultAdd:"",AppIconDefaultList:"",ActivateOrders:"",DeactivateOrders:"",ProductCatalog:"",ScatterChart:"",AccountActivity:"",DocumentManagement:"",CRMReport:"",KnowledgeArticle:"",Relationship:"",HomeVerify:"",ZipFolder:"",SurveyQuestions:"",TextDocument:"",TextDocumentShared:"",PageCheckedOut:"",PageShared:"",SaveAndClose:"",Script:"",Archive:"",ActivityFeed:"",Compare:"",EventDate:"",ArrowUpRight:"",CaretRight:"",SetAction:"",ChatBot:"",CaretSolidLeft:"",CaretSolidDown:"",CaretSolidRight:"",CaretSolidUp:"",PowerAppsLogo:"",PowerApps2Logo:"",SearchIssue:"",SearchIssueMirrored:"",FabricAssetLibrary:"",FabricDataConnectionLibrary:"",FabricDocLibrary:"",FabricFormLibrary:"",FabricFormLibraryMirrored:"",FabricReportLibrary:"",FabricReportLibraryMirrored:"",FabricPublicFolder:"",FabricFolderSearch:"",FabricMovetoFolder:"",FabricUnsyncFolder:"",FabricSyncFolder:"",FabricOpenFolderHorizontal:"",FabricFolder:"",FabricFolderFill:"",FabricNewFolder:"",FabricPictureLibrary:"",PhotoVideoMedia:"",AddFavorite:""}};Lt(n,t)}function vM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-9"',src:"url('".concat(e,"fabric-icons-9-c6162b42.woff') format('woff')")},icons:{AddFavoriteFill:"",BufferTimeBefore:"",BufferTimeAfter:"",BufferTimeBoth:"",PublishContent:"",ClipboardList:"",ClipboardListMirrored:"",CannedChat:"",SkypeForBusinessLogo:"",TabCenter:"",PageCheckedin:"",PageList:"",ReadOutLoud:"",CaretBottomLeftSolid8:"",CaretBottomRightSolid8:"",FolderHorizontal:"",MicrosoftStaffhubLogo:"",GiftboxOpen:"",StatusCircleOuter:"",StatusCircleInner:"",StatusCircleRing:"",StatusTriangleOuter:"",StatusTriangleInner:"",StatusTriangleExclamation:"",StatusCircleExclamation:"",StatusCircleErrorX:"",StatusCircleInfo:"",StatusCircleBlock:"",StatusCircleBlock2:"",StatusCircleQuestionMark:"",StatusCircleSync:"",Toll:"",ExploreContentSingle:"",CollapseContent:"",CollapseContentSingle:"",InfoSolid:"",GroupList:"",ProgressRingDots:"",CaloriesAdd:"",BranchFork:"",MuteChat:"",AddHome:"",AddWork:"",MobileReport:"",ScaleVolume:"",HardDriveGroup:"",FastMode:"",ToggleLeft:"",ToggleRight:"",TriangleShape:"",RectangleShape:"",CubeShape:"",Trophy2:"",BucketColor:"",BucketColorFill:"",Taskboard:"",SingleColumn:"",DoubleColumn:"",TripleColumn:"",ColumnLeftTwoThirds:"",ColumnRightTwoThirds:"",AccessLogoFill:"",AnalyticsLogo:"",AnalyticsQuery:"",NewAnalyticsQuery:"",AnalyticsReport:"",WordLogo:"",WordLogoFill:"",ExcelLogo:"",ExcelLogoFill:"",OneNoteLogo:"",OneNoteLogoFill:"",OutlookLogo:"",OutlookLogoFill:"",PowerPointLogo:"",PowerPointLogoFill:"",PublisherLogo:"",PublisherLogoFill:"",ScheduleEventAction:"",FlameSolid:"",ServerProcesses:"",Server:"",SaveAll:"",LinkedInLogo:"",Decimals:"",SidePanelMirrored:"",ProtectRestrict:"",Blog:"",UnknownMirrored:"",PublicContactCardMirrored:"",GridViewSmall:"",GridViewMedium:"",GridViewLarge:"",Step:"",StepInsert:"",StepShared:"",StepSharedAdd:"",StepSharedInsert:"",ViewDashboard:"",ViewList:""}};Lt(n,t)}function EM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-10"',src:"url('".concat(e,"fabric-icons-10-c4ded8e4.woff') format('woff')")},icons:{ViewListGroup:"",ViewListTree:"",TriggerAuto:"",TriggerUser:"",PivotChart:"",StackedBarChart:"",StackedLineChart:"",BuildQueue:"",BuildQueueNew:"",UserFollowed:"",ContactLink:"",Stack:"",Bullseye:"",VennDiagram:"",FiveTileGrid:"",FocalPoint:"",Insert:"",RingerRemove:"",TeamsLogoInverse:"",TeamsLogo:"",TeamsLogoFill:"",SkypeForBusinessLogoFill:"",SharepointLogo:"",SharepointLogoFill:"",DelveLogo:"",DelveLogoFill:"",OfficeVideoLogo:"",OfficeVideoLogoFill:"",ExchangeLogo:"",ExchangeLogoFill:"",Signin:"",DocumentApproval:"",CloneToDesktop:"",InstallToDrive:"",Blur:"",Build:"",ProcessMetaTask:"",BranchFork2:"",BranchLocked:"",BranchCommit:"",BranchCompare:"",BranchMerge:"",BranchPullRequest:"",BranchSearch:"",BranchShelveset:"",RawSource:"",MergeDuplicate:"",RowsGroup:"",RowsChild:"",Deploy:"",Redeploy:"",ServerEnviroment:"",VisioDiagram:"",HighlightMappedShapes:"",TextCallout:"",IconSetsFlag:"",VisioLogo:"",VisioLogoFill:"",VisioDocument:"",TimelineProgress:"",TimelineDelivery:"",Backlog:"",TeamFavorite:"",TaskGroup:"",TaskGroupMirrored:"",ScopeTemplate:"",AssessmentGroupTemplate:"",NewTeamProject:"",CommentAdd:"",CommentNext:"",CommentPrevious:"",ShopServer:"",LocaleLanguage:"",QueryList:"",UserSync:"",UserPause:"",StreamingOff:"",ArrowTallUpLeft:"",ArrowTallUpRight:"",ArrowTallDownLeft:"",ArrowTallDownRight:"",FieldEmpty:"",FieldFilled:"",FieldChanged:"",FieldNotChanged:"",RingerOff:"",PlayResume:"",BulletedList2:"",BulletedList2Mirrored:"",ImageCrosshair:"",GitGraph:"",Repo:"",RepoSolid:"",FolderQuery:"",FolderList:"",FolderListMirrored:"",LocationOutline:"",POISolid:"",CalculatorNotEqualTo:"",BoxSubtractSolid:""}};Lt(n,t)}function yM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-11"',src:"url('".concat(e,"fabric-icons-11-2a8393d6.woff') format('woff')")},icons:{BoxAdditionSolid:"",BoxMultiplySolid:"",BoxPlaySolid:"",BoxCheckmarkSolid:"",CirclePauseSolid:"",CirclePause:"",MSNVideosSolid:"",CircleStopSolid:"",CircleStop:"",NavigateBack:"",NavigateBackMirrored:"",NavigateForward:"",NavigateForwardMirrored:"",UnknownSolid:"",UnknownMirroredSolid:"",CircleAddition:"",CircleAdditionSolid:"",FilePDB:"",FileTemplate:"",FileSQL:"",FileJAVA:"",FileASPX:"",FileCSS:"",FileSass:"",FileLess:"",FileHTML:"",JavaScriptLanguage:"",CSharpLanguage:"",CSharp:"",VisualBasicLanguage:"",VB:"",CPlusPlusLanguage:"",CPlusPlus:"",FSharpLanguage:"",FSharp:"",TypeScriptLanguage:"",PythonLanguage:"",PY:"",CoffeeScript:"",MarkDownLanguage:"",FullWidth:"",FullWidthEdit:"",Plug:"",PlugSolid:"",PlugConnected:"",PlugDisconnected:"",UnlockSolid:"",Variable:"",Parameter:"",CommentUrgent:"",Storyboard:"",DiffInline:"",DiffSideBySide:"",ImageDiff:"",ImagePixel:"",FileBug:"",FileCode:"",FileComment:"",BusinessHoursSign:"",FileImage:"",FileSymlink:"",AutoFillTemplate:"",WorkItem:"",WorkItemBug:"",LogRemove:"",ColumnOptions:"",Packages:"",BuildIssue:"",AssessmentGroup:"",VariableGroup:"",FullHistory:"",Wheelchair:"",SingleColumnEdit:"",DoubleColumnEdit:"",TripleColumnEdit:"",ColumnLeftTwoThirdsEdit:"",ColumnRightTwoThirdsEdit:"",StreamLogo:"",PassiveAuthentication:"",AlertSolid:"",MegaphoneSolid:"",TaskSolid:"",ConfigurationSolid:"",BugSolid:"",CrownSolid:"",Trophy2Solid:"",QuickNoteSolid:"",ConstructionConeSolid:"",PageListSolid:"",PageListMirroredSolid:"",StarburstSolid:"",ReadingModeSolid:"",SadSolid:"",HealthSolid:"",ShieldSolid:"",GiftBoxSolid:"",ShoppingCartSolid:"",MailSolid:"",ChatSolid:"",RibbonSolid:""}};Lt(n,t)}function TM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-12"',src:"url('".concat(e,"fabric-icons-12-7e945a1e.woff') format('woff')")},icons:{FinancialSolid:"",FinancialMirroredSolid:"",HeadsetSolid:"",PermissionsSolid:"",ParkingSolid:"",ParkingMirroredSolid:"",DiamondSolid:"",AsteriskSolid:"",OfflineStorageSolid:"",BankSolid:"",DecisionSolid:"",Parachute:"",ParachuteSolid:"",FiltersSolid:"",ColorSolid:"",ReviewSolid:"",ReviewRequestSolid:"",ReviewRequestMirroredSolid:"",ReviewResponseSolid:"",FeedbackRequestSolid:"",FeedbackRequestMirroredSolid:"",FeedbackResponseSolid:"",WorkItemBar:"",WorkItemBarSolid:"",Separator:"",NavigateExternalInline:"",PlanView:"",TimelineMatrixView:"",EngineeringGroup:"",ProjectCollection:"",CaretBottomRightCenter8:"",CaretBottomLeftCenter8:"",CaretTopRightCenter8:"",CaretTopLeftCenter8:"",DonutChart:"",ChevronUnfold10:"",ChevronFold10:"",DoubleChevronDown8:"",DoubleChevronUp8:"",DoubleChevronLeft8:"",DoubleChevronRight8:"",ChevronDownEnd6:"",ChevronUpEnd6:"",ChevronLeftEnd6:"",ChevronRightEnd6:"",ContextMenu:"",AzureAPIManagement:"",AzureServiceEndpoint:"",VSTSLogo:"",VSTSAltLogo1:"",VSTSAltLogo2:"",FileTypeSolution:"",WordLogoInverse16:"",WordLogo16:"",WordLogoFill16:"",PowerPointLogoInverse16:"",PowerPointLogo16:"",PowerPointLogoFill16:"",ExcelLogoInverse16:"",ExcelLogo16:"",ExcelLogoFill16:"",OneNoteLogoInverse16:"",OneNoteLogo16:"",OneNoteLogoFill16:"",OutlookLogoInverse16:"",OutlookLogo16:"",OutlookLogoFill16:"",PublisherLogoInverse16:"",PublisherLogo16:"",PublisherLogoFill16:"",VisioLogoInverse16:"",VisioLogo16:"",VisioLogoFill16:"",TestBeaker:"",TestBeakerSolid:"",TestExploreSolid:"",TestAutoSolid:"",TestUserSolid:"",TestImpactSolid:"",TestPlan:"",TestStep:"",TestParameter:"",TestSuite:"",TestCase:"",Sprint:"",SignOut:"",TriggerApproval:"",Rocket:"",AzureKeyVault:"",Onboarding:"",Transition:"",LikeSolid:"",DislikeSolid:"",CRMCustomerInsightsApp:"",EditCreate:"",PlayReverseResume:"",PlayReverse:"",SearchData:"",UnSetColor:"",DeclineCall:""}};Lt(n,t)}function CM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-13"',src:"url('".concat(e,"fabric-icons-13-c3989a02.woff') format('woff')")},icons:{RectangularClipping:"",TeamsLogo16:"",TeamsLogoFill16:"",Spacer:"",SkypeLogo16:"",SkypeForBusinessLogo16:"",SkypeForBusinessLogoFill16:"",FilterSolid:"",MailUndelivered:"",MailTentative:"",MailTentativeMirrored:"",MailReminder:"",ReceiptUndelivered:"",ReceiptTentative:"",ReceiptTentativeMirrored:"",Inbox:"",IRMReply:"",IRMReplyMirrored:"",IRMForward:"",IRMForwardMirrored:"",VoicemailIRM:"",EventAccepted:"",EventTentative:"",EventTentativeMirrored:"",EventDeclined:"",IDBadge:"",BackgroundColor:"",OfficeFormsLogoInverse16:"",OfficeFormsLogo:"",OfficeFormsLogoFill:"",OfficeFormsLogo16:"",OfficeFormsLogoFill16:"",OfficeFormsLogoInverse24:"",OfficeFormsLogo24:"",OfficeFormsLogoFill24:"",PageLock:"",NotExecuted:"",NotImpactedSolid:"",FieldReadOnly:"",FieldRequired:"",BacklogBoard:"",ExternalBuild:"",ExternalTFVC:"",ExternalXAML:"",IssueSolid:"",DefectSolid:"",LadybugSolid:"",NugetLogo:"",TFVCLogo:"",ProjectLogo32:"",ProjectLogoFill32:"",ProjectLogo16:"",ProjectLogoFill16:"",SwayLogo32:"",SwayLogoFill32:"",SwayLogo16:"",SwayLogoFill16:"",ClassNotebookLogo32:"",ClassNotebookLogoFill32:"",ClassNotebookLogo16:"",ClassNotebookLogoFill16:"",ClassNotebookLogoInverse32:"",ClassNotebookLogoInverse16:"",StaffNotebookLogo32:"",StaffNotebookLogoFill32:"",StaffNotebookLogo16:"",StaffNotebookLogoFill16:"",StaffNotebookLogoInverted32:"",StaffNotebookLogoInverted16:"",KaizalaLogo:"",TaskLogo:"",ProtectionCenterLogo32:"",GallatinLogo:"",Globe2:"",Guitar:"",Breakfast:"",Brunch:"",BeerMug:"",Vacation:"",Teeth:"",Taxi:"",Chopsticks:"",SyncOccurence:"",UnsyncOccurence:"",GIF:"",PrimaryCalendar:"",SearchCalendar:"",VideoOff:"",MicrosoftFlowLogo:"",BusinessCenterLogo:"",ToDoLogoBottom:"",ToDoLogoTop:"",EditSolid12:"",EditSolidMirrored12:"",UneditableSolid12:"",UneditableSolidMirrored12:"",UneditableMirrored:"",AdminALogo32:"",AdminALogoFill32:"",ToDoLogoInverse:""}};Lt(n,t)}function SM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-14"',src:"url('".concat(e,"fabric-icons-14-5cf58db8.woff') format('woff')")},icons:{Snooze:"",WaffleOffice365:"",ImageSearch:"",NewsSearch:"",VideoSearch:"",R:"",FontColorA:"",FontColorSwatch:"",LightWeight:"",NormalWeight:"",SemiboldWeight:"",GroupObject:"",UngroupObject:"",AlignHorizontalLeft:"",AlignHorizontalCenter:"",AlignHorizontalRight:"",AlignVerticalTop:"",AlignVerticalCenter:"",AlignVerticalBottom:"",HorizontalDistributeCenter:"",VerticalDistributeCenter:"",Ellipse:"",Line:"",Octagon:"",Hexagon:"",Pentagon:"",RightTriangle:"",HalfCircle:"",QuarterCircle:"",ThreeQuarterCircle:"","6PointStar":"","12PointStar":"",ArrangeBringToFront:"",ArrangeSendToBack:"",ArrangeSendBackward:"",ArrangeBringForward:"",BorderDash:"",BorderDot:"",LineStyle:"",LineThickness:"",WindowEdit:"",HintText:"",MediaAdd:"",AnchorLock:"",AutoHeight:"",ChartSeries:"",ChartXAngle:"",ChartYAngle:"",Combobox:"",LineSpacing:"",Padding:"",PaddingTop:"",PaddingBottom:"",PaddingLeft:"",PaddingRight:"",NavigationFlipper:"",AlignJustify:"",TextOverflow:"",VisualsFolder:"",VisualsStore:"",PictureCenter:"",PictureFill:"",PicturePosition:"",PictureStretch:"",PictureTile:"",Slider:"",SliderHandleSize:"",DefaultRatio:"",NumberSequence:"",GUID:"",ReportAdd:"",DashboardAdd:"",MapPinSolid:"",WebPublish:"",PieSingleSolid:"",BlockedSolid:"",DrillDown:"",DrillDownSolid:"",DrillExpand:"",DrillShow:"",SpecialEvent:"",OneDriveFolder16:"",FunctionalManagerDashboard:"",BIDashboard:"",CodeEdit:"",RenewalCurrent:"",RenewalFuture:"",SplitObject:"",BulkUpload:"",DownloadDocument:"",GreetingCard:"",Flower:"",WaitlistConfirm:"",WaitlistConfirmMirrored:"",LaptopSecure:"",DragObject:"",EntryView:"",EntryDecline:"",ContactCardSettings:"",ContactCardSettingsMirrored:""}};Lt(n,t)}function _M(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-15"',src:"url('".concat(e,"fabric-icons-15-3807251b.woff') format('woff')")},icons:{CalendarSettings:"",CalendarSettingsMirrored:"",HardDriveLock:"",HardDriveUnlock:"",AccountManagement:"",ReportWarning:"",TransitionPop:"",TransitionPush:"",TransitionEffect:"",LookupEntities:"",ExploreData:"",AddBookmark:"",SearchBookmark:"",DrillThrough:"",MasterDatabase:"",CertifiedDatabase:"",MaximumValue:"",MinimumValue:"",VisualStudioIDELogo32:"",PasteAsText:"",PasteAsCode:"",BrowserTab:"",BrowserTabScreenshot:"",DesktopScreenshot:"",FileYML:"",ClipboardSolid:"",FabricUserFolder:"",FabricNetworkFolder:"",BullseyeTarget:"",AnalyticsView:"",Video360Generic:"",Untag:"",Leave:"",Trending12:"",Blocked12:"",Warning12:"",CheckedOutByOther12:"",CheckedOutByYou12:"",CircleShapeSolid:"",SquareShapeSolid:"",TriangleShapeSolid:"",DropShapeSolid:"",RectangleShapeSolid:"",ZoomToFit:"",InsertColumnsLeft:"",InsertColumnsRight:"",InsertRowsAbove:"",InsertRowsBelow:"",DeleteColumns:"",DeleteRows:"",DeleteRowsMirrored:"",DeleteTable:"",AccountBrowser:"",VersionControlPush:"",StackedColumnChart2:"",TripleColumnWide:"",QuadColumn:"",WhiteBoardApp16:"",WhiteBoardApp32:"",PinnedSolid:"",InsertSignatureLine:"",ArrangeByFrom:"",Phishing:"",CreateMailRule:"",PublishCourse:"",DictionaryRemove:"",UserRemove:"",UserEvent:"",Encryption:"",PasswordField:"",OpenInNewTab:"",Hide3:"",VerifiedBrandSolid:"",MarkAsProtected:"",AuthenticatorApp:"",WebTemplate:"",DefenderTVM:"",MedalSolid:"",D365TalentLearn:"",D365TalentInsight:"",D365TalentHRCore:"",BacklogList:"",ButtonControl:"",TableGroup:"",MountainClimbing:"",TagUnknown:"",TagUnknownMirror:"",TagUnknown12:"",TagUnknown12Mirror:"",Link12:"",Presentation:"",Presentation12:"",Lock12:"",BuildDefinition:"",ReleaseDefinition:"",SaveTemplate:"",UserGauge:"",BlockedSiteSolid12:"",TagSolid:"",OfficeChat:""}};Lt(n,t)}function bM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-16"',src:"url('".concat(e,"fabric-icons-16-9cf93f3b.woff') format('woff')")},icons:{OfficeChatSolid:"",MailSchedule:"",WarningSolid:"",Blocked2Solid:"",SkypeCircleArrow:"",SkypeArrow:"",SyncStatus:"",SyncStatusSolid:"",ProjectDocument:"",ToDoLogoOutline:"",VisioOnlineLogoFill32:"",VisioOnlineLogo32:"",VisioOnlineLogoCloud32:"",VisioDiagramSync:"",Event12:"",EventDateMissed12:"",UserOptional:"",ResponsesMenu:"",DoubleDownArrow:"",DistributeDown:"",BookmarkReport:"",FilterSettings:"",GripperDotsVertical:"",MailAttached:"",AddIn:"",LinkedDatabase:"",TableLink:"",PromotedDatabase:"",BarChartVerticalFilter:"",BarChartVerticalFilterSolid:"",MicOff2:"",MicrosoftTranslatorLogo:"",ShowTimeAs:"",FileRequest:"",WorkItemAlert:"",PowerBILogo16:"",PowerBILogoBackplate16:"",BulletedListText:"",BulletedListBullet:"",BulletedListTextMirrored:"",BulletedListBulletMirrored:"",NumberedListText:"",NumberedListNumber:"",NumberedListTextMirrored:"",NumberedListNumberMirrored:"",RemoveLinkChain:"",RemoveLinkX:"",FabricTextHighlight:"",ClearFormattingA:"",ClearFormattingEraser:"",Photo2Fill:"",IncreaseIndentText:"",IncreaseIndentArrow:"",DecreaseIndentText:"",DecreaseIndentArrow:"",IncreaseIndentTextMirrored:"",IncreaseIndentArrowMirrored:"",DecreaseIndentTextMirrored:"",DecreaseIndentArrowMirrored:"",CheckListText:"",CheckListCheck:"",CheckListTextMirrored:"",CheckListCheckMirrored:"",NumberSymbol:"",Coupon:"",VerifiedBrand:"",ReleaseGate:"",ReleaseGateCheck:"",ReleaseGateError:"",M365InvoicingLogo:"",RemoveFromShoppingList:"",ShieldAlert:"",FabricTextHighlightComposite:"",Dataflows:"",GenericScanFilled:"",DiagnosticDataBarTooltip:"",SaveToMobile:"",Orientation2:"",ScreenCast:"",ShowGrid:"",SnapToGrid:"",ContactList:"",NewMail:"",EyeShadow:"",FabricFolderConfirm:"",InformationBarriers:"",CommentActive:"",ColumnVerticalSectionEdit:"",WavingHand:"",ShakeDevice:"",SmartGlassRemote:"",Rotate90Clockwise:"",Rotate90CounterClockwise:"",CampaignTemplate:"",ChartTemplate:"",PageListFilter:"",SecondaryNav:"",ColumnVerticalSection:"",SkypeCircleSlash:"",SkypeSlash:""}};Lt(n,t)}function AM(e,t){e===void 0&&(e="");var n={style:{MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontStyle:"normal",fontWeight:"normal",speak:"none"},fontFace:{fontFamily:'"FabricMDL2Icons-17"',src:"url('".concat(e,"fabric-icons-17-0c4ed701.woff') format('woff')")},icons:{CustomizeToolbar:"",DuplicateRow:"",RemoveFromTrash:"",MailOptions:"",Childof:"",Footer:"",Header:"",BarChartVerticalFill:"",StackedColumnChart2Fill:"",PlainText:"",AccessibiltyChecker:"",DatabaseSync:"",ReservationOrders:"",TabOneColumn:"",TabTwoColumn:"",TabThreeColumn:"",BulletedTreeList:"",MicrosoftTranslatorLogoGreen:"",MicrosoftTranslatorLogoBlue:"",InternalInvestigation:"",AddReaction:"",ContactHeart:"",VisuallyImpaired:"",EventToDoLogo:"",Variable2:"",ModelingView:"",DisconnectVirtualMachine:"",ReportLock:"",Uneditable2:"",Uneditable2Mirrored:"",BarChartVerticalEdit:"",GlobalNavButtonActive:"",PollResults:"",Rerun:"",QandA:"",QandAMirror:"",BookAnswers:"",AlertSettings:"",TrimStart:"",TrimEnd:"",TableComputed:"",DecreaseIndentLegacy:"",IncreaseIndentLegacy:"",SizeLegacy:""}};Lt(n,t)}var wM=function(){Ma("trash","delete"),Ma("onedrive","onedrivelogo"),Ma("alertsolid12","eventdatemissed12"),Ma("sixpointstar","6pointstar"),Ma("twelvepointstar","12pointstar"),Ma("toggleon","toggleleft"),Ma("toggleoff","toggleright")};qv("@fluentui/font-icons-mdl2","8.5.21");var kM="".concat(MN,"/assets/icons/"),Os=st();function IM(e,t){var n,r;e===void 0&&(e=((n=Os==null?void 0:Os.FabricConfig)===null||n===void 0?void 0:n.iconBaseUrl)||((r=Os==null?void 0:Os.FabricConfig)===null||r===void 0?void 0:r.fontBaseUrl)||kM),[sM,lM,uM,cM,fM,dM,hM,pM,mM,gM,vM,EM,yM,TM,CM,SM,_M,bM,AM].forEach(function(i){return i(e,t)}),wM()}var d9=R;function Zu(e,t){for(var n=[],r=2;r0)throw new Error("Any module using getSlots must use withSlots. Please see withSlots javadoc for more info.");return RM(t[a],l,r[a],r.slots&&r.slots[a],r._defaultStyles&&r._defaultStyles[a],r.theme)};s.isSlot=!0,n[a]=s}};for(var o in t)i(o);return n}function NM(e,t){var n,r;return typeof t=="string"||typeof t=="number"||typeof t=="boolean"?r=(n={},n[e]=t,n):r=t,r}function FM(e,t){for(var n=[],r=2;r2)return{rowGap:{value:0,unit:"px"},columnGap:{value:0,unit:"px"}};if(n.length===2)return{rowGap:fm(_l(n[0],t)),columnGap:fm(_l(n[1],t))};var r=fm(_l(e,t));return{rowGap:r,columnGap:r}},ky=function(e,t){if(e===void 0||typeof e=="number"||e==="")return e;var n=e.split(" ");return n.length<2?_l(e,t):n.reduce(function(r,i){return _l(r,t)+" "+_l(i,t)})},Ls={start:"flex-start",end:"flex-end"},dg={root:"ms-Stack",inner:"ms-Stack-inner",child:"ms-Stack-child"},HM=function(e,t,n){var r,i,o,a,s,l,u,c,f,d,h,v,g,C=e.className,m=e.disableShrink,y=e.enableScopedSelectors,T=e.grow,S=e.horizontal,w=e.horizontalAlign,b=e.reversed,A=e.verticalAlign,F=e.verticalFill,D=e.wrap,I=Mn(dg,t),B=n&&n.childrenGap?n.childrenGap:e.gap,M=n&&n.maxHeight?n.maxHeight:e.maxHeight,oe=n&&n.maxWidth?n.maxWidth:e.maxWidth,V=n&&n.padding?n.padding:e.padding,P=BM(B,t),z=P.rowGap,Z=P.columnGap,H="".concat(-.5*Z.value).concat(Z.unit),U="".concat(-.5*z.value).concat(z.unit),_={textOverflow:"ellipsis"},ee="> "+(y?"."+dg.child:"*"),X=(r={},r["".concat(ee,":not(.").concat($_.root,")")]={flexShrink:0},r);return D?{root:[I.root,{flexWrap:"wrap",maxWidth:oe,maxHeight:M,width:"auto",overflow:"visible",height:"100%"},w&&(i={},i[S?"justifyContent":"alignItems"]=Ls[w]||w,i),A&&(o={},o[S?"alignItems":"justifyContent"]=Ls[A]||A,o),C,{display:"flex"},S&&{height:F?"100%":"auto"}],inner:[I.inner,(a={display:"flex",flexWrap:"wrap",marginLeft:H,marginRight:H,marginTop:U,marginBottom:U,overflow:"visible",boxSizing:"border-box",padding:ky(V,t),width:Z.value===0?"100%":"calc(100% + ".concat(Z.value).concat(Z.unit,")"),maxWidth:"100vw"},a[ee]=R({margin:"".concat(.5*z.value).concat(z.unit," ").concat(.5*Z.value).concat(Z.unit)},_),a),m&&X,w&&(s={},s[S?"justifyContent":"alignItems"]=Ls[w]||w,s),A&&(l={},l[S?"alignItems":"justifyContent"]=Ls[A]||A,l),S&&(u={flexDirection:b?"row-reverse":"row",height:z.value===0?"100%":"calc(100% + ".concat(z.value).concat(z.unit,")")},u[ee]={maxWidth:Z.value===0?"100%":"calc(100% - ".concat(Z.value).concat(Z.unit,")")},u),!S&&(c={flexDirection:b?"column-reverse":"column",height:"calc(100% + ".concat(z.value).concat(z.unit,")")},c[ee]={maxHeight:z.value===0?"100%":"calc(100% - ".concat(z.value).concat(z.unit,")")},c)]}:{root:[I.root,(f={display:"flex",flexDirection:S?b?"row-reverse":"row":b?"column-reverse":"column",flexWrap:"nowrap",width:"auto",height:F?"100%":"auto",maxWidth:oe,maxHeight:M,padding:ky(V,t),boxSizing:"border-box"},f[ee]=_,f),m&&X,T&&{flexGrow:T===!0?1:T},w&&(d={},d[S?"justifyContent":"alignItems"]=Ls[w]||w,d),A&&(h={},h[S?"alignItems":"justifyContent"]=Ls[A]||A,h),S&&Z.value>0&&(v={},v[b?"".concat(ee,":not(:last-child)"):"".concat(ee,":not(:first-child)")]={marginLeft:"".concat(Z.value).concat(Z.unit)},v),!S&&z.value>0&&(g={},g[b?"".concat(ee,":not(:last-child)"):"".concat(ee,":not(:first-child)")]={marginTop:"".concat(z.value).concat(z.unit)},g),C]}},zM=function(e){var t=e.as,n=t===void 0?"div":t,r=e.disableShrink,i=r===void 0?!1:r,o=e.enableScopedSelectors,a=o===void 0?!1:o,s=e.wrap,l=_a(e,["as","disableShrink","enableScopedSelectors","wrap"]),u=q_(e.children,{disableShrink:i,enableScopedSelectors:a}),c=ht(l,pt),f=h9(e,{root:n,inner:"div"});return s?Zu(f.root,R({},c),Zu(f.inner,null,u)):Zu(f.root,R({},c),u)};function q_(e,t){var n=t.disableShrink,r=t.enableScopedSelectors,i=E.Children.toArray(e);return i=E.Children.map(i,function(o){if(!o||!E.isValidElement(o))return o;if(o.type===E.Fragment)return o.props.children?q_(o.props.children,{disableShrink:n,enableScopedSelectors:r}):null;var a=o,s={};UM(o)&&(s={shrink:!n});var l=a.props.className;return E.cloneElement(a,R(R(R(R({},s),a.props),l&&{className:l}),r&&{className:qn(dg.child,l)}))}),i}function UM(e){return!!e&&typeof e=="object"&&!!e.type&&e.type.displayName===V_.displayName}var KM={Item:V_},ln=p9(zM,{displayName:"Stack",styles:HM,statics:KM}),WM=function(e){if(e.children==null)return null;e.block,e.className;var t=e.as,n=t===void 0?"span":t;e.variant,e.nowrap;var r=_a(e,["block","className","as","variant","nowrap"]),i=h9(e,{root:n});return Zu(i.root,R({},ht(r,pt)))},jM=function(e,t){var n=e.as,r=e.className,i=e.block,o=e.nowrap,a=e.variant,s=t.fonts,l=t.semanticColors,u=s[a||"medium"];return{root:[u,{color:u.color||l.bodyText,display:i?n==="td"?"table-cell":"block":"inline",mozOsxFontSmoothing:u.MozOsxFontSmoothing,webkitFontSmoothing:u.WebkitFontSmoothing},o&&{whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},r]}},GM=p9(WM,{displayName:"Text",styles:jM});const $M="_layout_1tkk8_1",VM="_header_1tkk8_13",qM="_headerContainer_1tkk8_21",YM="_headerTitleContainer_1tkk8_33",QM="_headerTitle_1tkk8_33",XM="_headerIcon_1tkk8_69",ZM="_shareButtonContainer_1tkk8_81",JM="_shareButton_1tkk8_81",eP="_shareButtonText_1tkk8_127",tP="_urlTextBox_1tkk8_149",nP="_copyButtonContainer_1tkk8_171",rP="_copyButton_1tkk8_171",iP="_copyButtonText_1tkk8_215",ar={layout:$M,header:VM,headerContainer:qM,headerTitleContainer:YM,headerTitle:QM,headerIcon:XM,shareButtonContainer:ZM,shareButton:JM,shareButtonText:eP,urlTextBox:tP,copyButtonContainer:nP,copyButton:rP,copyButtonText:iP},Y_="/assets/Azure-d0a62656.svg",dm=typeof window>"u"?global:window,hm="@griffel/";function oP(e,t){return dm[Symbol.for(hm+e)]||(dm[Symbol.for(hm+e)]=t),dm[Symbol.for(hm+e)]}const hg=oP("DEFINITION_LOOKUP_TABLE",{}),id="data-make-styles-bucket",pg=7,m9="___",aP=m9.length+pg,sP=0,lP=1;function uP(e){for(var t=0,n,r=0,i=e.length;i>=4;++r,i-=4)n=e.charCodeAt(r)&255|(e.charCodeAt(++r)&255)<<8|(e.charCodeAt(++r)&255)<<16|(e.charCodeAt(++r)&255)<<24,n=(n&65535)*1540483477+((n>>>16)*59797<<16),n^=n>>>24,t=(n&65535)*1540483477+((n>>>16)*59797<<16)^(t&65535)*1540483477+((t>>>16)*59797<<16);switch(i){case 3:t^=(e.charCodeAt(r+2)&255)<<16;case 2:t^=(e.charCodeAt(r+1)&255)<<8;case 1:t^=e.charCodeAt(r)&255,t=(t&65535)*1540483477+((t>>>16)*59797<<16)}return t^=t>>>13,t=(t&65535)*1540483477+((t>>>16)*59797<<16),((t^t>>>15)>>>0).toString(36)}function cP(e){const t=e.length;if(t===pg)return e;for(let n=t;n0&&(t+=c.slice(0,f)),n+=d,r[u]=d}}}if(n==="")return t.slice(0,-1);const i=xy[n];if(i!==void 0)return t+i;const o=[];for(let u=0;uo.cssText):r}}}const pP=["r","d","l","v","w","f","i","h","a","k","t","m","c"],Ny=pP.reduce((e,t,n)=>(e[t]=n,e),{});function mP(e,t,n,r,i={}){const o=e==="m",a=o?e+i.m:e;if(!r.stylesheets[a]){const s=t&&t.createElement("style"),l=hP(s,e,{...r.styleElementAttributes,...o&&{media:i.m}});r.stylesheets[a]=l,t&&s&&t.head.insertBefore(s,gP(t,n,e,r,i))}return r.stylesheets[a]}function gP(e,t,n,r,i){const o=Ny[n];let a=c=>o-Ny[c.getAttribute(id)],s=e.head.querySelectorAll(`[${id}]`);if(n==="m"&&i){const c=e.head.querySelectorAll(`[${id}="${n}"]`);c.length&&(s=c,a=f=>r.compareMediaQueries(i.m,f.media))}const l=s.length;let u=l-1;for(;u>=0;){const c=s.item(u);if(a(c)>0)return c.nextSibling;u--}return l>0?s.item(0):t?t.nextSibling:null}let vP=0;const EP=(e,t)=>et?1:0;function yP(e=typeof document>"u"?void 0:document,t={}){const{unstable_filterCSSRule:n,insertionPoint:r,styleElementAttributes:i,compareMediaQueries:o=EP}=t,a={insertionCache:{},stylesheets:{},styleElementAttributes:Object.freeze(i),compareMediaQueries:o,id:`d${vP++}`,insertCSSRules(s){for(const l in s){const u=s[l];for(let c=0,f=u.length;c{const{title:t,primaryFill:n="currentColor",...r}=e,i={...r,title:void 0,fill:n},o=wP();return i.className=fP(o.root,i.className),t&&(i["aria-label"]=t),!i["aria-label"]&&!i["aria-labelledby"]?i["aria-hidden"]=!0:i.role="img",i},Ql=(e,t,n)=>{const r=t==="1em"?"20":t,i=o=>{const a={...kP(o),width:t,height:t,viewBox:`0 0 ${r} ${r}`,xmlns:"http://www.w3.org/2000/svg"};return E.createElement("svg",a,...n.map(s=>E.createElement("path",{d:s,fill:a.fill})))};return i.displayName=e,i},IP=Ql("BroomRegular","1em",["M17.91 2.18c.2.2.2.51 0 .7l-5.32 5.33a4.5 4.5 0 0 1-.34 6l-.66.66-2.09 3.48a.5.5 0 0 1-.78.1l-7.07-7.08a.5.5 0 0 1 .1-.78L5.22 8.5l.66-.66a4.5 4.5 0 0 1 5.99-.34l5.32-5.32c.2-.2.51-.2.71 0ZM6.24 8.9l4.95 4.95.36-.35A3.5 3.5 0 1 0 6.6 8.55l-.36.35Zm-.78.63L2.8 11.12l6.16 6.17 1.6-2.66-5.11-5.1Z"]),xP=Ql("CopyRegular","1em",["M8 2a2 2 0 0 0-2 2v10c0 1.1.9 2 2 2h6a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8ZM7 4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V4ZM4 6a2 2 0 0 1 1-1.73V14.5A2.5 2.5 0 0 0 7.5 17h6.23A2 2 0 0 1 12 18H7.5A3.5 3.5 0 0 1 4 14.5V6Z"]),NP=Ql("DismissRegular","1em",["m4.09 4.22.06-.07a.5.5 0 0 1 .63-.06l.07.06L10 9.29l5.15-5.14a.5.5 0 0 1 .63-.06l.07.06c.18.17.2.44.06.63l-.06.07L10.71 10l5.14 5.15c.18.17.2.44.06.63l-.06.07a.5.5 0 0 1-.63.06l-.07-.06L10 10.71l-5.15 5.14a.5.5 0 0 1-.63.06l-.07-.06a.5.5 0 0 1-.06-.63l.06-.07L9.29 10 4.15 4.85a.5.5 0 0 1-.06-.63l.06-.07-.06.07Z"]),FP=Ql("SendRegular","1em",["M2.18 2.11a.5.5 0 0 1 .54-.06l15 7.5a.5.5 0 0 1 0 .9l-15 7.5a.5.5 0 0 1-.7-.58L3.98 10 2.02 2.63a.5.5 0 0 1 .16-.52Zm2.7 8.39-1.61 6.06L16.38 10 3.27 3.44 4.88 9.5h6.62a.5.5 0 1 1 0 1H4.88Z"]),RP=Ql("ShareRegular","1em",["m13.33 12.84 4.5-4.42.05-.07a.59.59 0 0 0-.05-.77l-4.5-4.42-.06-.05c-.36-.27-.9-.01-.9.47V5.7l-.22.01C8.6 6.01 6.5 8.26 6 12.35c-.06.53.54.85.93.5a9.64 9.64 0 0 1 4.45-2.38c.24-.06.5-.1.74-.12l.26-.02v2.17c.06.46.61.67.95.34Zm-1.1-6.12 1.15-.08V4.61L16.82 8l-3.44 3.39V9.23l-1.36.12c-1.7.19-3.32.87-4.83 2 .3-1.33.8-2.34 1.47-3.06a5.2 5.2 0 0 1 3.57-1.57ZM5.5 4A2.5 2.5 0 0 0 3 6.5v8A2.5 2.5 0 0 0 5.5 17h8a2.5 2.5 0 0 0 2.5-2.5v-1a.5.5 0 0 0-1 0v1c0 .83-.67 1.5-1.5 1.5h-8A1.5 1.5 0 0 1 4 14.5v-8C4 5.67 4.67 5 5.5 5h3a.5.5 0 0 0 0-1h-3Z"]),DP=Ql("SquareRegular","1em",["M3 6a3 3 0 0 1 3-3h8a3 3 0 0 1 3 3v8a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6Zm3-2a2 2 0 0 0-2 2v8c0 1.1.9 2 2 2h8a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2H6Z"]),MP=()=>{const[e,t]=E.useState(!1),[n,r]=E.useState(!1),[i,o]=E.useState("Copy URL"),a=()=>{t(!0)},s=()=>{t(!1),r(!1),o("Copy URL")},l=()=>{navigator.clipboard.writeText(window.location.href),r(!0)};return E.useEffect(()=>{n&&o("Copied URL")},[n]),gt("div",{className:ar.layout,children:[re("header",{className:ar.header,role:"banner",children:re("div",{className:ar.headerContainer,children:gt(ln,{horizontal:!0,verticalAlign:"center",children:[re("img",{src:Y_,className:ar.headerIcon,"aria-hidden":"true"}),re(Dk,{to:"/",className:ar.headerTitleContainer,children:re("h3",{className:ar.headerTitle,children:"Azure AI"})}),gt("div",{className:ar.shareButtonContainer,role:"button",tabIndex:0,"aria-label":"Share",onClick:a,onKeyDown:u=>u.key==="Enter"||u.key===" "?a():null,children:[re(RP,{className:ar.shareButton}),re("span",{className:ar.shareButtonText,children:"Share"})]})]})})}),re(_k,{}),re(W_,{onDismiss:s,hidden:!e,styles:{main:[{selectors:{["@media (min-width: 480px)"]:{maxWidth:"600px",background:"#FFFFFF",boxShadow:"0px 14px 28.8px rgba(0, 0, 0, 0.24), 0px 0px 8px rgba(0, 0, 0, 0.2)",borderRadius:"8px",maxHeight:"200px",minHeight:"100px"}}}]},dialogContentProps:{title:"Share the web app",showCloseButton:!0},children:gt(ln,{horizontal:!0,verticalAlign:"center",style:{gap:"8px"},children:[re(H_,{className:ar.urlTextBox,defaultValue:window.location.href,readOnly:!0}),gt("div",{className:ar.copyButtonContainer,role:"button",tabIndex:0,"aria-label":"Copy",onClick:l,onKeyDown:u=>u.key==="Enter"||u.key===" "?l():null,children:[re(xP,{className:ar.copyButton}),re("span",{className:ar.copyButtonText,children:i})]})]})})]})},PP=()=>re("h1",{children:"404"});let ff;const OP=new Uint8Array(16);function LP(){if(!ff&&(ff=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!ff))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return ff(OP)}const Yt=[];for(let e=0;e<256;++e)Yt.push((e+256).toString(16).slice(1));function BP(e,t=0){return(Yt[e[t+0]]+Yt[e[t+1]]+Yt[e[t+2]]+Yt[e[t+3]]+"-"+Yt[e[t+4]]+Yt[e[t+5]]+"-"+Yt[e[t+6]]+Yt[e[t+7]]+"-"+Yt[e[t+8]]+Yt[e[t+9]]+"-"+Yt[e[t+10]]+Yt[e[t+11]]+Yt[e[t+12]]+Yt[e[t+13]]+Yt[e[t+14]]+Yt[e[t+15]]).toLowerCase()}const HP=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),Fy={randomUUID:HP};function mg(e,t,n){if(Fy.randomUUID&&!t&&!e)return Fy.randomUUID();e=e||{};const r=e.random||(e.rng||LP)();if(r[6]=r[6]&15|64,r[8]=r[8]&63|128,t){n=n||0;for(let i=0;i<16;++i)t[n+i]=r[i];return t}return BP(r)}const zP=()=>mg(),Pt=()=>zP().replace(new RegExp("-","g"),"").toUpperCase();var li;(function(e){e[e.Debug=0]="Debug",e[e.Info=1]="Info",e[e.Warning=2]="Warning",e[e.Error=3]="Error",e[e.None=4]="None"})(li||(li={}));class cc{constructor(t,n){this.privName=t,this.privEventId=Pt(),this.privEventTime=new Date().toISOString(),this.privEventType=n,this.privMetadata={}}get name(){return this.privName}get eventId(){return this.privEventId}get eventTime(){return this.privEventTime}get eventType(){return this.privEventType}get metadata(){return this.privMetadata}}class fc extends cc{constructor(t,n,r=li.Info){super(t,r),this.privAudioSourceId=n}get audioSourceId(){return this.privAudioSourceId}}class Lh extends fc{constructor(t){super("AudioSourceInitializingEvent",t)}}class z1 extends fc{constructor(t){super("AudioSourceReadyEvent",t)}}class Z_ extends fc{constructor(t){super("AudioSourceOffEvent",t)}}class Qd extends fc{constructor(t,n){super("AudioSourceErrorEvent",t,li.Error),this.privError=n}get error(){return this.privError}}class Bh extends fc{constructor(t,n,r){super(t,n),this.privAudioNodeId=r}get audioNodeId(){return this.privAudioNodeId}}class dc extends Bh{constructor(t,n){super("AudioStreamNodeAttachingEvent",t,n)}}class hc extends Bh{constructor(t,n){super("AudioStreamNodeAttachedEvent",t,n)}}class mo extends Bh{constructor(t,n){super("AudioStreamNodeDetachedEvent",t,n)}}class g9 extends Bh{constructor(t,n,r){super("AudioStreamNodeErrorEvent",t,n),this.privError=r}get error(){return this.privError}}class UP extends cc{constructor(t,n,r=li.Info){super(t,r),this.privJsonResult=n}get jsonString(){return this.privJsonResult}}class Ss extends cc{constructor(t,n,r=li.Info){super(t,r),this.privConnectionId=n}get connectionId(){return this.privConnectionId}}class J_ extends Ss{constructor(t,n,r){super("ConnectionStartEvent",t),this.privUri=n,this.privHeaders=r}get uri(){return this.privUri}get headers(){return this.privHeaders}}class e6 extends Ss{constructor(t){super("ConnectionEstablishedEvent",t)}}class KP extends Ss{constructor(t,n,r){super("ConnectionClosedEvent",t,li.Debug),this.privReason=r,this.privStatusCode=n}get reason(){return this.privReason}get statusCode(){return this.privStatusCode}}class WP extends Ss{constructor(t,n,r){super("ConnectionErrorEvent",t,li.Debug),this.privMessage=n,this.privType=r}get message(){return this.privMessage}get type(){return this.privType}}class jP extends Ss{constructor(t,n,r){super("ConnectionEstablishErrorEvent",t,li.Error),this.privStatusCode=n,this.privReason=r}get reason(){return this.privReason}get statusCode(){return this.privStatusCode}}class gg extends Ss{constructor(t,n,r){super("ConnectionMessageReceivedEvent",t),this.privNetworkReceivedTime=n,this.privMessage=r}get networkReceivedTime(){return this.privNetworkReceivedTime}get message(){return this.privMessage}}class GP extends Ss{constructor(t,n,r){super("ConnectionMessageSentEvent",t),this.privNetworkSentTime=n,this.privMessage=r}get networkSentTime(){return this.privNetworkSentTime}get message(){return this.privMessage}}class wn extends Error{constructor(t){super(t),this.name="ArgumentNull",this.message=t}}class xr extends Error{constructor(t){super(t),this.name="InvalidOperation",this.message=t}}class v9 extends Error{constructor(t,n){super(n),this.name=t+"ObjectDisposed",this.message=n}}var ze;(function(e){e[e.Text=0]="Text",e[e.Binary=1]="Binary"})(ze||(ze={}));class vg{constructor(t,n,r,i){if(this.privBody=null,t===ze.Text&&n&&typeof n!="string")throw new xr("Payload must be a string");if(t===ze.Binary&&n&&!(n instanceof ArrayBuffer))throw new xr("Payload must be ArrayBuffer");switch(this.privMessageType=t,this.privBody=n,this.privHeaders=r||{},this.privId=i||Pt(),this.messageType){case ze.Binary:this.privSize=this.binaryBody!==null?this.binaryBody.byteLength:0;break;case ze.Text:this.privSize=this.textBody.length}}get messageType(){return this.privMessageType}get headers(){return this.privHeaders}get body(){return this.privBody}get textBody(){if(this.privMessageType===ze.Binary)throw new xr("Not supported for binary message");return this.privBody}get binaryBody(){if(this.privMessageType===ze.Text)throw new xr("Not supported for text message");return this.privBody}get id(){return this.privId}}class pm{constructor(t,n){this.privStatusCode=t,this.privReason=n}get statusCode(){return this.privStatusCode}get reason(){return this.privReason}}class pa{constructor(t){this.privEventListeners={},this.privIsDisposed=!1,this.privConsoleListener=void 0,this.privMetadata=t}onEvent(t){if(this.isDisposed())throw new v9("EventSource");if(this.metadata)for(const n in this.metadata)n&&t.metadata&&(t.metadata[n]||(t.metadata[n]=this.metadata[n]));for(const n in this.privEventListeners)n&&this.privEventListeners[n]&&this.privEventListeners[n](t)}attach(t){const n=Pt();return this.privEventListeners[n]=t,{detach:()=>(delete this.privEventListeners[n],Promise.resolve())}}attachListener(t){return this.attach(n=>t.onEvent(n))}attachConsoleListener(t){return this.privConsoleListener&&this.privConsoleListener.detach(),this.privConsoleListener=this.attach(n=>t.onEvent(n)),this.privConsoleListener}isDisposed(){return this.privIsDisposed}dispose(){this.privEventListeners=null,this.privIsDisposed=!0}get metadata(){return this.privMetadata}}class In{static setEventSource(t){if(!t)throw new wn("eventSource");In.privInstance=t}static get instance(){return In.privInstance}}In.privInstance=new pa;var Qt;(function(e){e[e.None=0]="None",e[e.Connected=1]="Connected",e[e.Connecting=2]="Connecting",e[e.Disconnected=3]="Disconnected"})(Qt||(Qt={}));class wi{constructor(t){if(this.privSubscriptionIdCounter=0,this.privAddSubscriptions={},this.privRemoveSubscriptions={},this.privDisposedSubscriptions={},this.privDisposeReason=null,this.privList=[],t)for(const n of t)this.privList.push(n)}get(t){return this.throwIfDisposed(),this.privList[t]}first(){return this.get(0)}last(){return this.get(this.length()-1)}add(t){this.throwIfDisposed(),this.insertAt(this.privList.length,t)}insertAt(t,n){this.throwIfDisposed(),t===0?this.privList.unshift(n):t===this.privList.length?this.privList.push(n):this.privList.splice(t,0,n),this.triggerSubscriptions(this.privAddSubscriptions)}removeFirst(){return this.throwIfDisposed(),this.removeAt(0)}removeLast(){return this.throwIfDisposed(),this.removeAt(this.length()-1)}removeAt(t){return this.throwIfDisposed(),this.remove(t,1)[0]}remove(t,n){this.throwIfDisposed();const r=this.privList.splice(t,n);return this.triggerSubscriptions(this.privRemoveSubscriptions),r}clear(){this.throwIfDisposed(),this.remove(0,this.length())}length(){return this.throwIfDisposed(),this.privList.length}onAdded(t){this.throwIfDisposed();const n=this.privSubscriptionIdCounter++;return this.privAddSubscriptions[n]=t,{detach:()=>(delete this.privAddSubscriptions[n],Promise.resolve())}}onRemoved(t){this.throwIfDisposed();const n=this.privSubscriptionIdCounter++;return this.privRemoveSubscriptions[n]=t,{detach:()=>(delete this.privRemoveSubscriptions[n],Promise.resolve())}}onDisposed(t){this.throwIfDisposed();const n=this.privSubscriptionIdCounter++;return this.privDisposedSubscriptions[n]=t,{detach:()=>(delete this.privDisposedSubscriptions[n],Promise.resolve())}}join(t){return this.throwIfDisposed(),this.privList.join(t)}toArray(){const t=Array();return this.privList.forEach(n=>{t.push(n)}),t}any(t){return this.throwIfDisposed(),t?this.where(t).length()>0:this.length()>0}all(t){return this.throwIfDisposed(),this.where(t).length()===this.length()}forEach(t){this.throwIfDisposed();for(let n=0;nt(r,n))}clone(){return this.throwIfDisposed(),new wi(this.toArray())}concat(t){return this.throwIfDisposed(),new wi(this.privList.concat(t.toArray()))}concatArray(t){return this.throwIfDisposed(),new wi(this.privList.concat(t))}isDisposed(){return this.privList==null}dispose(t){this.isDisposed()||(this.privDisposeReason=t,this.privList=null,this.privAddSubscriptions=null,this.privRemoveSubscriptions=null,this.triggerSubscriptions(this.privDisposedSubscriptions))}throwIfDisposed(){if(this.isDisposed())throw new v9("List",this.privDisposeReason)}triggerSubscriptions(t){if(t)for(const n in t)n&&t[n]()}}var Ry;(function(e){e[e.None=0]="None",e[e.Resolved=1]="Resolved",e[e.Rejected=2]="Rejected"})(Ry||(Ry={}));class fn{constructor(){this.resolve=t=>(this.privResolve(t),this),this.reject=t=>(this.privReject(t),this),this.privPromise=new Promise((t,n)=>{this.privResolve=t,this.privReject=n})}get promise(){return this.privPromise}}function Lu(e,t,n){e.then(r=>{try{t&&t(r)}catch(i){if(n)try{if(i instanceof Error){const o=i;n(o.name+": "+o.message)}else n(i)}catch{}}},r=>{if(n)try{if(r instanceof Error){const i=r;n(i.name+": "+i.message)}else n(r)}catch{}})}var Dy=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})},Ju;(function(e){e[e.Dequeue=0]="Dequeue",e[e.Peek=1]="Peek"})(Ju||(Ju={}));class Xd{constructor(t){this.privPromiseStore=new wi,this.privIsDrainInProgress=!1,this.privIsDisposing=!1,this.privDisposeReason=null,this.privList=t||new wi,this.privDetachables=[],this.privSubscribers=new wi,this.privDetachables.push(this.privList.onAdded(()=>this.drain()))}enqueue(t){this.throwIfDispose(),this.enqueueFromPromise(new Promise(n=>n(t)))}enqueueFromPromise(t){this.throwIfDispose(),t.then(n=>{this.privList.add(n)},()=>{})}dequeue(){this.throwIfDispose();const t=new fn;return this.privSubscribers&&(this.privSubscribers.add({deferral:t,type:Ju.Dequeue}),this.drain()),t.promise}peek(){this.throwIfDispose();const t=new fn;return this.privSubscribers&&(this.privSubscribers.add({deferral:t,type:Ju.Peek}),this.drain()),t.promise}length(){return this.throwIfDispose(),this.privList.length()}isDisposed(){return this.privSubscribers==null}drainAndDispose(t,n){return Dy(this,void 0,void 0,function*(){if(!this.isDisposed()&&!this.privIsDisposing){this.privDisposeReason=n,this.privIsDisposing=!0;const r=this.privSubscribers;if(r){for(;r.length()>0;)r.removeFirst().deferral.resolve(void 0);this.privSubscribers===r&&(this.privSubscribers=r)}for(const i of this.privDetachables)yield i.detach();if(this.privPromiseStore.length()>0&&t){const i=[];return this.privPromiseStore.toArray().forEach(o=>{i.push(o)}),Promise.all(i).finally(()=>{this.privSubscribers=null,this.privList.forEach(o=>{t(o)}),this.privList=null}).then()}else this.privSubscribers=null,this.privList=null}})}dispose(t){return Dy(this,void 0,void 0,function*(){yield this.drainAndDispose(null,t)})}drain(){if(!this.privIsDrainInProgress&&!this.privIsDisposing){this.privIsDrainInProgress=!0;const t=this.privSubscribers,n=this.privList;if(t&&n){for(;n.length()>0&&t.length()>0&&!this.privIsDisposing;){const r=t.removeFirst();if(r.type===Ju.Peek)r.deferral.resolve(n.first());else{const i=n.removeFirst();r.deferral.resolve(i)}}this.privSubscribers===t&&(this.privSubscribers=t),this.privList===n&&(this.privList=n)}this.privIsDrainInProgress=!1}}throwIfDispose(){if(this.isDisposed())throw this.privDisposeReason?new xr(this.privDisposeReason):new v9("Queue");if(this.privIsDisposing)throw new xr("Queue disposing")}}class Zd{constructor(t,n,r){if(this.privPayload=null,!n)throw new wn("payload");if(t===ze.Binary&&n.__proto__.constructor.name!=="ArrayBuffer")throw new xr("Payload must be ArrayBuffer");if(t===ze.Text&&typeof n!="string")throw new xr("Payload must be a string");this.privMessageType=t,this.privPayload=n,this.privId=r||Pt()}get messageType(){return this.privMessageType}get payload(){return this.privPayload}get textContent(){if(this.privMessageType===ze.Binary)throw new xr("Not supported for binary message");return this.privPayload}get binaryContent(){if(this.privMessageType===ze.Text)throw new xr("Not supported for text message");return this.privPayload}get id(){return this.privId}}class $P{constructor(t,n){this.privActualSampleRate=t,this.privDesiredSampleRate=n}encode(t){const n=this.downSampleAudioFrame(t,this.privActualSampleRate,this.privDesiredSampleRate);if(!n)return null;const r=n.length*2,i=new ArrayBuffer(r),o=new DataView(i);return this.floatTo16BitPCM(o,0,n),i}setString(t,n,r){for(let i=0;in)return t;const i=n/r,o=Math.round(t.length/i),a=new Float32Array(o);let s=0,l=0;for(;lVP(this,void 0,void 0,function*(){return(t===void 0||t.isEnd)&&(yield this.privReaderQueue.dispose("End of stream reached")),t}))}readEnded(){this.privIsReadEnded||(this.privIsReadEnded=!0,this.privReaderQueue=new Xd)}throwIfClosed(){if(this.privIsWriteEnded)throw new xr("Stream closed")}}class E9 extends t6{constructor(t,n){super(n),this.privTargetChunkSize=t,this.privNextBufferReadyBytes=0}writeStreamChunk(t){if(t.isEnd||this.privNextBufferReadyBytes===0&&t.buffer.byteLength===this.privTargetChunkSize){super.writeStreamChunk(t);return}let n=0;for(;n{}]]),n=new Map,i="data:text/javascript;base64,"+btoa(`!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=14)}([function(e,t,n){"use strict";n.d(t,"a",(function(){return i})),n.d(t,"b",(function(){return u})),n.d(t,"c",(function(){return a})),n.d(t,"d",(function(){return d}));const r=new Map,o=new Map,i=e=>{const t=r.get(e);if(void 0===t)throw new Error('There is no interval scheduled with the given id "'.concat(e,'".'));clearTimeout(t),r.delete(e)},u=e=>{const t=o.get(e);if(void 0===t)throw new Error('There is no timeout scheduled with the given id "'.concat(e,'".'));clearTimeout(t),o.delete(e)},f=(e,t)=>{let n,r;if("performance"in self){const o=performance.now();n=o,r=e-Math.max(0,o-t)}else n=Date.now(),r=e;return{expected:n+r,remainingDelay:r}},c=(e,t,n,r)=>{const o="performance"in self?performance.now():Date.now();o>n?postMessage({id:null,method:"call",params:{timerId:t}}):e.set(t,setTimeout(c,n-o,e,t,n))},a=(e,t,n)=>{const{expected:o,remainingDelay:i}=f(e,n);r.set(t,setTimeout(c,i,r,t,o))},d=(e,t,n)=>{const{expected:r,remainingDelay:i}=f(e,n);o.set(t,setTimeout(c,i,o,t,r))}},function(e,t,n){"use strict";n.r(t);var r=n(2);for(var o in r)"default"!==o&&function(e){n.d(t,e,(function(){return r[e]}))}(o);var i=n(3);for(var o in i)"default"!==o&&function(e){n.d(t,e,(function(){return i[e]}))}(o);var u=n(4);for(var o in u)"default"!==o&&function(e){n.d(t,e,(function(){return u[e]}))}(o);var f=n(5);for(var o in f)"default"!==o&&function(e){n.d(t,e,(function(){return f[e]}))}(o);var c=n(6);for(var o in c)"default"!==o&&function(e){n.d(t,e,(function(){return c[e]}))}(o);var a=n(7);for(var o in a)"default"!==o&&function(e){n.d(t,e,(function(){return a[e]}))}(o);var d=n(8);for(var o in d)"default"!==o&&function(e){n.d(t,e,(function(){return d[e]}))}(o);var s=n(9);for(var o in s)"default"!==o&&function(e){n.d(t,e,(function(){return s[e]}))}(o)},function(e,t){},function(e,t){},function(e,t){},function(e,t){},function(e,t){},function(e,t){},function(e,t){},function(e,t){},function(e,t,n){"use strict";n.r(t);var r=n(11);for(var o in r)"default"!==o&&function(e){n.d(t,e,(function(){return r[e]}))}(o);var i=n(12);for(var o in i)"default"!==o&&function(e){n.d(t,e,(function(){return i[e]}))}(o);var u=n(13);for(var o in u)"default"!==o&&function(e){n.d(t,e,(function(){return u[e]}))}(o)},function(e,t){},function(e,t){},function(e,t){},function(e,t,n){"use strict";n.r(t);var r=n(0),o=n(1);for(var i in o)"default"!==i&&function(e){n.d(t,e,(function(){return o[e]}))}(i);var u=n(10);for(var i in u)"default"!==i&&function(e){n.d(t,e,(function(){return u[e]}))}(i);addEventListener("message",({data:e})=>{try{if("clear"===e.method){const{id:t,params:{timerId:n}}=e;Object(r.b)(n),postMessage({error:null,id:t})}else{if("set"!==e.method)throw new Error('The given method "'.concat(e.method,'" is not supported'));{const{params:{delay:t,now:n,timerId:o}}=e;Object(r.d)(t,o,n)}}}catch(t){postMessage({error:{message:t.message},id:e.id,result:null})}})}]);`),o=new Worker(i);return o.addEventListener("message",({data:l})=>{if(un.isCallNotification(l)){const{params:{timerId:u}}=l,c=t.get(u);if(typeof c=="number"){const f=n.get(c);if(f===void 0||f!==u)throw new Error("The timer is in an undefined state.")}else if(typeof c<"u")c(),t.delete(u);else throw new Error("The timer is in an undefined state.")}else if(un.isClearResponse(l)){const{id:u}=l,c=n.get(u);if(c===void 0)throw new Error("The timer is in an undefined state.");n.delete(u),t.delete(c)}else{const{error:{message:u}}=l;throw new Error(u)}}),{clearTimeout:l=>{const u=Math.random();n.set(u,l),t.set(l,u),o.postMessage({id:u,method:"clear",params:{timerId:l}})},setTimeout:(l,u)=>{const c=Math.random();return t.set(c,l),o.postMessage({id:null,method:"set",params:{delay:u,now:performance.now(),timerId:c}}),c}}}static loadWorkerTimers(){return()=>(un.workerTimers!==null||(un.workerTimers=un.load()),un.workerTimers)}static isCallNotification(t){return t.method!==void 0&&t.method==="call"}static isClearResponse(t){return t.error===null&&typeof t.id=="number"}}un.workerTimers=null;un.clearTimeout=e=>un.timers().clearTimeout(e);un.setTimeout=(e,t)=>un.timers().setTimeout(e,t);un.timers=un.loadWorkerTimers();class e1 extends cc{constructor(t){super("BackgroundEvent",li.Error),this.privError=t}get error(){return this.privError}}class ot{}ot.AuthKey="Ocp-Apim-Subscription-Key";ot.Authorization="Authorization";ot.SpIDAuthKey="Apim-Subscription-Id";ot.ConnectionId="X-ConnectionId";ot.ContentType="Content-Type";ot.CustomCommandsAppId="X-CommandsAppId";ot.Path="Path";ot.RequestId="X-RequestId";ot.RequestStreamId="X-StreamId";ot.RequestTimestamp="X-Timestamp";class Eg{constructor(t,n){this.privHeaderName=t,this.privToken=n}get headerName(){return this.privHeaderName}get token(){return this.privToken}}class qP{constructor(t){if(!t)throw new wn("subscriptionKey");this.privAuthInfo=new Eg(ot.AuthKey,t)}fetch(t){return Promise.resolve(this.privAuthInfo)}fetchOnExpiry(t){return Promise.resolve(this.privAuthInfo)}}class U1{constructor(t,n){if(!t)throw new wn("fetchCallback");if(!n)throw new wn("fetchOnExpiryCallback");this.privFetchCallback=t,this.privFetchOnExpiryCallback=n}fetch(t){return this.privFetchCallback(t).then(n=>new Eg(ot.Authorization,n===void 0?void 0:U1.privTokenPrefix+n))}fetchOnExpiry(t){return this.privFetchOnExpiryCallback(t).then(n=>new Eg(ot.Authorization,n===void 0?void 0:U1.privTokenPrefix+n))}}U1.privTokenPrefix="Bearer ";const mm={};class Fe{static throwIfNullOrUndefined(t,n){if(t==null)throw new Error("throwIfNullOrUndefined:"+n)}static throwIfNull(t,n){if(t===null)throw new Error("throwIfNull:"+n)}static throwIfNullOrWhitespace(t,n){if(Fe.throwIfNullOrUndefined(t,n),(""+t).trim().length<1)throw new Error("throwIfNullOrWhitespace:"+n)}static throwIfNullOrTooLong(t,n,r){if(Fe.throwIfNullOrUndefined(t,n),(""+t).length>r)throw new Error("throwIfNullOrTooLong:"+n+" (more than "+r.toString()+" characters)")}static throwIfNullOrTooShort(t,n,r){if(Fe.throwIfNullOrUndefined(t,n),(""+t).length{t.mediaDevices.getUserMedia(r).then(i).catch(o)}),n){const r=()=>{this.onEvent(new Lh(this.privId)),this.privMediaStream&&this.privMediaStream.active?(this.onEvent(new z1(this.privId)),this.privInitializeDeferral.resolve()):n({audio:this.deviceId?{deviceId:this.deviceId}:!0,video:!1},i=>{this.privMediaStream=i,this.onEvent(new z1(this.privId)),this.privInitializeDeferral.resolve()},i=>{const o=`Error occurred during microphone initialization: ${i}`;this.privInitializeDeferral.reject(o),this.onEvent(new Qd(this.privId,o))})};this.privContext.state==="suspended"?this.privContext.resume().then(r).catch(i=>{this.privInitializeDeferral.reject(`Failed to initialize audio context: ${i}`)}):r()}else{const r="Browser does not support getUserMedia.";this.privInitializeDeferral.reject(r),this.onEvent(new Qd(r,""))}return this.privInitializeDeferral.promise}id(){return this.privId}attach(t){return this.onEvent(new dc(this.privId,t)),this.listen(t).then(n=>(this.onEvent(new hc(this.privId,t)),{detach:()=>df(this,void 0,void 0,function*(){return n.readEnded(),delete this.privStreams[t],this.onEvent(new mo(this.privId,t)),this.turnOff()}),id:()=>t,read:()=>n.read()}))}detach(t){t&&this.privStreams[t]&&(this.privStreams[t].close(),delete this.privStreams[t],this.onEvent(new mo(this.privId,t)))}turnOff(){return df(this,void 0,void 0,function*(){for(const t in this.privStreams)if(t){const n=this.privStreams[t];n&&n.close()}this.onEvent(new Z_(this.privId)),this.privInitializeDeferral&&(yield this.privInitializeDeferral,this.privInitializeDeferral=null),yield this.destroyAudioContext()})}get events(){return this.privEvents}get deviceInfo(){return this.getMicrophoneLabel().then(t=>({bitspersample:Jr.AUDIOFORMAT.bitsPerSample,channelcount:Jr.AUDIOFORMAT.channels,connectivity:Bl.Unknown,manufacturer:"Speech SDK",model:t,samplerate:Jr.AUDIOFORMAT.samplesPerSec,type:hs.Microphones}))}setProperty(t,n){if(t===YP)this.privRecorder.setWorkletUrl(n);else throw new Error("Property '"+t+"' is not supported on Microphone.")}getMicrophoneLabel(){const t="microphone";if(this.privMicrophoneLabel!==void 0)return Promise.resolve(this.privMicrophoneLabel);if(this.privMediaStream===void 0||!this.privMediaStream.active)return Promise.resolve(t);this.privMicrophoneLabel=t;const n=this.privMediaStream.getTracks()[0].getSettings().deviceId;if(n===void 0)return Promise.resolve(this.privMicrophoneLabel);const r=new fn;return navigator.mediaDevices.enumerateDevices().then(i=>{for(const o of i)if(o.deviceId===n){this.privMicrophoneLabel=o.label;break}r.resolve(this.privMicrophoneLabel)},()=>r.resolve(this.privMicrophoneLabel)),r.promise}listen(t){return df(this,void 0,void 0,function*(){yield this.turnOn();const n=new E9(this.privOutputChunkSize,t);this.privStreams[t]=n;try{this.privRecorder.record(this.privContext,this.privMediaStream,n)}catch(i){throw this.onEvent(new g9(this.privId,t,i)),i}return n})}onEvent(t){this.privEvents.onEvent(t),In.instance.onEvent(t)}createAudioContext(){this.privContext||(this.privContext=fa.getAudioContext(Jr.AUDIOFORMAT.samplesPerSec))}destroyAudioContext(){return df(this,void 0,void 0,function*(){if(!this.privContext)return;this.privRecorder.releaseMediaResources(this.privContext);let t=!1;"close"in this.privContext&&(t=!0),t?this.privIsClosing||(this.privIsClosing=!0,yield this.privContext.close(),this.privContext=null,this.privIsClosing=!1):this.privContext!==null&&this.privContext.state==="running"&&(yield this.privContext.suspend())})}}Jr.AUDIOFORMAT=Hh.getDefaultInputFormat();var gm=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class QP{constructor(t,n,r){this.privStreams={},this.privHeaderEnd=44,this.privId=r||Pt(),this.privEvents=new pa,this.privSource=t,typeof window<"u"&&typeof Blob<"u"&&this.privSource instanceof Blob?this.privFilename=t.name:this.privFilename=n||"unknown.wav",this.privAudioFormatPromise=this.readHeader()}get format(){return this.privAudioFormatPromise}turnOn(){if(this.privFilename.lastIndexOf(".wav")!==this.privFilename.length-4){const t=this.privFilename+" is not supported. Only WAVE files are allowed at the moment.";return this.onEvent(new Qd(t,"")),Promise.reject(t)}this.onEvent(new Lh(this.privId)),this.onEvent(new z1(this.privId))}id(){return this.privId}attach(t){return gm(this,void 0,void 0,function*(){this.onEvent(new dc(this.privId,t));const n=yield this.upload(t);return this.onEvent(new hc(this.privId,t)),Promise.resolve({detach:()=>gm(this,void 0,void 0,function*(){n.readEnded(),delete this.privStreams[t],this.onEvent(new mo(this.privId,t)),yield this.turnOff()}),id:()=>t,read:()=>n.read()})})}detach(t){t&&this.privStreams[t]&&(this.privStreams[t].close(),delete this.privStreams[t],this.onEvent(new mo(this.privId,t)))}turnOff(){for(const t in this.privStreams)if(t){const n=this.privStreams[t];n&&!n.isClosed&&n.close()}return this.onEvent(new Z_(this.privId)),Promise.resolve()}get events(){return this.privEvents}get deviceInfo(){return this.privAudioFormatPromise.then(t=>Promise.resolve({bitspersample:t.bitsPerSample,channelcount:t.channels,connectivity:Bl.Unknown,manufacturer:"Speech SDK",model:"File",samplerate:t.samplesPerSec,type:hs.File}))}readHeader(){const n=this.privSource.slice(0,4296),r=new fn,i=o=>{const a=new DataView(o),s=h=>String.fromCharCode(a.getUint8(h),a.getUint8(h+1),a.getUint8(h+2),a.getUint8(h+3));if(s(0)!=="RIFF"){r.reject("Invalid WAV header in file, RIFF was not found");return}if(s(8)!=="WAVE"||s(12)!=="fmt "){r.reject("Invalid WAV header in file, WAVEfmt was not found");return}const l=a.getInt32(16,!0),u=a.getUint16(22,!0),c=a.getUint32(24,!0),f=a.getUint16(34,!0);let d=36+Math.max(l-16,0);for(;s(d)!=="data";d+=2)if(d>4296-8){r.reject("Invalid WAV header in file, data block was not found");return}this.privHeaderEnd=d+8,r.resolve(Hh.getWaveFormatPCM(c,f,u))};if(typeof window<"u"&&typeof Blob<"u"&&n instanceof Blob){const o=new FileReader;o.onload=a=>{const s=a.target.result;i(s)},o.readAsArrayBuffer(n)}else{const o=n;i(o.buffer.slice(o.byteOffset,o.byteOffset+o.byteLength))}return r.promise}upload(t){return gm(this,void 0,void 0,function*(){const n=r=>{const i=`Error occurred while processing '${this.privFilename}'. ${r}`;throw this.onEvent(new g9(this.privId,t,i)),new Error(i)};try{yield this.turnOn();const r=yield this.privAudioFormatPromise,i=new E9(r.avgBytesPerSec/10,t);this.privStreams[t]=i;const o=this.privSource.slice(this.privHeaderEnd),a=s=>{i.isClosed||(i.writeStreamChunk({buffer:s,isEnd:!1,timeReceived:Date.now()}),i.close())};if(typeof window<"u"&&typeof Blob<"u"&&o instanceof Blob){const s=new FileReader;s.onerror=l=>n(l.toString()),s.onload=l=>{const u=l.target.result;a(u)},s.readAsArrayBuffer(o)}else{const s=o;a(s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength))}return i}catch(r){n(r)}})}onEvent(t){this.privEvents.onEvent(t),In.instance.onEvent(t)}}class vm{constructor(t){this.privStopInputOnRelease=t}record(t,n,r){const o=new $P(t.sampleRate,16e3),a=t.createMediaStreamSource(n),s=()=>{const c=(()=>{let f=0;try{return t.createScriptProcessor(f,1,1)}catch{f=2048;let h=t.sampleRate;for(;f<16384&&h>=32e3;)f<<=1,h>>=1;return t.createScriptProcessor(f,1,1)}})();c.onaudioprocess=f=>{const d=f.inputBuffer.getChannelData(0);if(r&&!r.isClosed){const h=o.encode(d);h&&r.writeStreamChunk({buffer:h,isEnd:!1,timeReceived:Date.now()})}},a.connect(c),c.connect(t.destination),this.privMediaResources={scriptProcessorNode:c,source:a,stream:n}},l=c=>{const f=new AudioWorkletNode(c,"speech-processor");f.port.onmessage=d=>{const h=d.data;if(r&&!r.isClosed){const v=o.encode(h);v&&r.writeStreamChunk({buffer:v,isEnd:!1,timeReceived:Date.now()})}},a.connect(f),f.connect(c.destination),this.privMediaResources={scriptProcessorNode:f,source:a,stream:n}},u=!!this.privSpeechProcessorScript&&this.privSpeechProcessorScript.toLowerCase()==="ignore";if(t.audioWorklet&&!u)this.privSpeechProcessorScript=new URL("data:application/javascript;base64,LyogSW1wbGVtZW50YXRpb24gb2YgdGhlIEF1ZGlvV29ya2xldFByb2Nlc3Nvcg0KIGh0dHBzOi8vd2ViYXVkaW8uZ2l0aHViLmlvL3dlYi1hdWRpby1hcGkvI2F1ZGlvd29ya2xldA0KIFRoaXMgZmlsZSB3aWxsIGJlIGxvYWRlZCBvbmx5IGluIHJlY2VudCBicm93c2VycyB0aGF0IHN1cHBvcnRzIEF1ZGlvIHdvcmtsZXQgaXQgaXMNCiBjdXJyZW50bHkgaW4ganMgYmVjYXVzZSBpdCBuZWVkcyB0byBiZSBpbiBlczYgKi8NCmNsYXNzIFNwZWVjaFByb2Nlc3NvciBleHRlbmRzIEF1ZGlvV29ya2xldFByb2Nlc3NvciB7DQogIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHsNCiAgICAvLyBUaGUgc3VwZXIgY29uc3RydWN0b3IgY2FsbCBpcyByZXF1aXJlZC4NCiAgICBzdXBlcihvcHRpb25zKTsNCiAgfQ0KDQogIHByb2Nlc3MoaW5wdXRzLCBvdXRwdXRzKSB7DQogICAgY29uc3QgaW5wdXQgPSBpbnB1dHNbMF07DQogICAgY29uc3Qgb3V0cHV0ID0gW107IC8vIE1ha2Ugc3VyZSBvdXRwdXQgYXJyYXkgY2FuIGJlIGFzc2lnbmVkIHRvDQogICAgZm9yIChsZXQgY2hhbm5lbCA9IDA7IGNoYW5uZWwgPCBpbnB1dC5sZW5ndGg7IGNoYW5uZWwgKz0gMSkgew0KICAgICAgb3V0cHV0W2NoYW5uZWxdID0gaW5wdXRbY2hhbm5lbF07DQogICAgfQ0KICAgIHRoaXMucG9ydC5wb3N0TWVzc2FnZShvdXRwdXRbMF0pOw0KICAgIHJldHVybiB0cnVlOw0KICB9DQp9DQoNCnJlZ2lzdGVyUHJvY2Vzc29yKCdzcGVlY2gtcHJvY2Vzc29yJywgU3BlZWNoUHJvY2Vzc29yKTsNCg==",self.location).toString(),t.audioWorklet.addModule(this.privSpeechProcessorScript).then(()=>{l(t)}).catch(()=>{const c=`class SP extends AudioWorkletProcessor { + constructor(options) { + super(options); + } + process(inputs, outputs) { + const input = inputs[0]; + const output = []; + for (let channel = 0; channel < input.length; channel += 1) { + output[channel] = input[channel]; + } + this.port.postMessage(output[0]); + return true; + } + } + registerProcessor('speech-processor', SP);`,f=new Blob([c],{type:"application/javascript; charset=utf-8"});this.privSpeechProcessorScript=URL.createObjectURL(f),t.audioWorklet.addModule(this.privSpeechProcessorScript).then(()=>{l(t)}).catch(()=>{s()})});else try{s()}catch(c){throw new Error(`Unable to start audio worklet node for PCMRecorder: ${c}`)}}releaseMediaResources(t){this.privMediaResources&&(this.privMediaResources.scriptProcessorNode&&(this.privMediaResources.scriptProcessorNode.disconnect(t.destination),this.privMediaResources.scriptProcessorNode=null),this.privMediaResources.source&&(this.privMediaResources.source.disconnect(),this.privStopInputOnRelease&&this.privMediaResources.stream.getTracks().forEach(n=>n.stop()),this.privMediaResources.source=null))}setWorkletUrl(t){this.privSpeechProcessorScript=t}}var My=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class K1{constructor(t,n,r,i,o,a){if(!t)throw new wn("uri");if(!r)throw new wn("messageFormatter");this.proxyInfo=i,this.privConnectionEvents=new pa,this.privConnectionId=n,this.privMessageFormatter=r,this.privConnectionState=Qt.None,this.privUri=t,this.privHeaders=o,this.privEnableCompression=a,this.privHeaders[ot.ConnectionId]=this.privConnectionId,this.privLastErrorReceived=""}get state(){return this.privConnectionState}open(){if(this.privConnectionState===Qt.Disconnected)return Promise.reject(`Cannot open a connection that is in ${this.privConnectionState} state`);if(this.privConnectionEstablishDeferral)return this.privConnectionEstablishDeferral.promise;this.privConnectionEstablishDeferral=new fn,this.privCertificateValidatedDeferral=new fn,this.privConnectionState=Qt.Connecting;try{if(typeof WebSocket<"u"&&!K1.forceNpmWebSocket)this.privCertificateValidatedDeferral.resolve(),this.privWebsocketClient=new WebSocket(this.privUri);else{const t={headers:this.privHeaders,perMessageDeflate:this.privEnableCompression};this.privCertificateValidatedDeferral.resolve(),t.agent=this.getAgent();let r=new URL(this.privUri).protocol;(r==null?void 0:r.toLocaleLowerCase())==="wss:"?r="https:":(r==null?void 0:r.toLocaleLowerCase())==="ws:"&&(r="http:"),t.agent.protocol=r,this.privWebsocketClient=new mm(this.privUri,t)}this.privWebsocketClient.binaryType="arraybuffer",this.privReceivingMessageQueue=new Xd,this.privDisconnectDeferral=new fn,this.privSendMessageQueue=new Xd,this.processSendQueue().catch(t=>{In.instance.onEvent(new e1(t))})}catch(t){return this.privConnectionEstablishDeferral.resolve(new pm(500,t)),this.privConnectionEstablishDeferral.promise}return this.onEvent(new J_(this.privConnectionId,this.privUri)),this.privWebsocketClient.onopen=()=>{this.privCertificateValidatedDeferral.promise.then(()=>{this.privConnectionState=Qt.Connected,this.onEvent(new e6(this.privConnectionId)),this.privConnectionEstablishDeferral.resolve(new pm(200,""))},t=>{this.privConnectionEstablishDeferral.reject(t)})},this.privWebsocketClient.onerror=t=>{this.onEvent(new WP(this.privConnectionId,t.message,t.type)),this.privLastErrorReceived=t.message},this.privWebsocketClient.onclose=t=>{this.privConnectionState===Qt.Connecting?(this.privConnectionState=Qt.Disconnected,this.privConnectionEstablishDeferral.resolve(new pm(t.code,t.reason+" "+this.privLastErrorReceived))):(this.privConnectionState=Qt.Disconnected,this.privWebsocketClient=null,this.onEvent(new KP(this.privConnectionId,t.code,t.reason))),this.onClose(t.code,t.reason).catch(n=>{In.instance.onEvent(new e1(n))})},this.privWebsocketClient.onmessage=t=>{const n=new Date().toISOString();if(this.privConnectionState===Qt.Connected){const r=new fn;if(this.privReceivingMessageQueue.enqueueFromPromise(r.promise),t.data instanceof ArrayBuffer){const i=new Zd(ze.Binary,t.data);this.privMessageFormatter.toConnectionMessage(i).then(o=>{this.onEvent(new gg(this.privConnectionId,n,o)),r.resolve(o)},o=>{r.reject(`Invalid binary message format. Error: ${o}`)})}else{const i=new Zd(ze.Text,t.data);this.privMessageFormatter.toConnectionMessage(i).then(o=>{this.onEvent(new gg(this.privConnectionId,n,o)),r.resolve(o)},o=>{r.reject(`Invalid text message format. Error: ${o}`)})}}},this.privConnectionEstablishDeferral.promise}send(t){if(this.privConnectionState!==Qt.Connected)return Promise.reject(`Cannot send on connection that is in ${Qt[this.privConnectionState]} state`);const n=new fn,r=new fn;return this.privSendMessageQueue.enqueueFromPromise(r.promise),this.privMessageFormatter.fromConnectionMessage(t).then(i=>{r.resolve({Message:t,RawWebsocketMessage:i,sendStatusDeferral:n})},i=>{r.reject(`Error formatting the message. ${i}`)}),n.promise}read(){return this.privConnectionState!==Qt.Connected?Promise.reject(`Cannot read on connection that is in ${this.privConnectionState} state`):this.privReceivingMessageQueue.dequeue()}close(t){if(this.privWebsocketClient)this.privConnectionState!==Qt.Disconnected&&this.privWebsocketClient.close(1e3,t||"Normal closure by client");else return Promise.resolve();return this.privDisconnectDeferral.promise}get events(){return this.privConnectionEvents}sendRawMessage(t){try{if(!t)return Promise.resolve();if(this.onEvent(new GP(this.privConnectionId,new Date().toISOString(),t.Message)),this.isWebsocketOpen)this.privWebsocketClient.send(t.RawWebsocketMessage.payload);else return Promise.reject("websocket send error: Websocket not ready "+this.privConnectionId+" "+t.Message.id+" "+new Error().stack);return Promise.resolve()}catch(n){return Promise.reject(`websocket send error: ${n}`)}}onClose(t,n){return My(this,void 0,void 0,function*(){const r=`Connection closed. ${t}: ${n}`;this.privConnectionState=Qt.Disconnected,this.privDisconnectDeferral.resolve(),yield this.privReceivingMessageQueue.drainAndDispose(()=>{},r),yield this.privSendMessageQueue.drainAndDispose(i=>{i.sendStatusDeferral.reject(r)},r)})}processSendQueue(){return My(this,void 0,void 0,function*(){for(;;){const n=yield this.privSendMessageQueue.dequeue();if(!n)return;try{yield this.sendRawMessage(n),n.sendStatusDeferral.resolve()}catch(r){n.sendStatusDeferral.reject(r)}}})}onEvent(t){this.privConnectionEvents.onEvent(t),In.instance.onEvent(t)}getAgent(){const t=new mm.Agent(this.createConnection);return this.proxyInfo!==void 0&&this.proxyInfo.HostName!==void 0&&this.proxyInfo.Port>0&&(t.proxyInfo=this.proxyInfo),t}static GetProxyAgent(t){const n={host:t.HostName,port:t.Port};return t.UserName?n.headers={"Proxy-Authentication":"Basic "+new Buffer(`${t.UserName}:${t.Password===void 0?"":t.Password}`).toString("base64")}:n.headers={},n.headers.requestOCSP="true",new mm(n)}createConnection(t,n){let r;if(n=Object.assign(Object.assign({},n),{requestOCSP:!0,servername:n.host}),this.proxyInfo){const o=K1.GetProxyAgent(this.proxyInfo);r=new Promise((a,s)=>{o.callback(t,n,(l,u)=>{l?s(l):a(u)})})}else n.secureEndpoint,r=Promise.resolve((void 0)(n));return r}get isWebsocketOpen(){return this.privWebsocketClient&&this.privWebsocketClient.readyState===this.privWebsocketClient.OPEN}}K1.forceNpmWebSocket=!1;var XP=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class ZP{constructor(t,n,r,i,o,a=!1,s){if(this.privIsDisposed=!1,!t)throw new wn("uri");if(!i)throw new wn("messageFormatter");this.privMessageFormatter=i;let l="",u=0;if(n){for(const c in n)if(c){l+=u===0&&t.indexOf("?")===-1?"?":"&";const f=encodeURIComponent(c);l+=f;let d=n[c];d&&(d=encodeURIComponent(d),l+=`=${d}`),u++}}if(r){for(const c in r)if(c){l+=u===0&&t.indexOf("?")===-1?"?":"&";const f=encodeURIComponent(r[c]);l+=`${c}=${f}`,u++}}this.privUri=t+l,this.privId=s||Pt(),this.privConnectionMessageAdapter=new K1(this.privUri,this.id,this.privMessageFormatter,o,r,a)}dispose(){return XP(this,void 0,void 0,function*(){this.privIsDisposed=!0,this.privConnectionMessageAdapter&&(yield this.privConnectionMessageAdapter.close())})}isDisposed(){return this.privIsDisposed}get id(){return this.privId}get uri(){return this.privUri}state(){return this.privConnectionMessageAdapter.state}open(){return this.privConnectionMessageAdapter.open()}send(t){return this.privConnectionMessageAdapter.send(t)}read(){return this.privConnectionMessageAdapter.read()}get events(){return this.privConnectionMessageAdapter.events}}class JP{constructor(t,n){this.privBuffers=[],this.privReplayOffset=0,this.privLastShrinkOffset=0,this.privBufferStartOffset=0,this.privBufferSerial=0,this.privBufferedBytes=0,this.privReplay=!1,this.privLastChunkAcquiredTime=0,this.privAudioNode=t,this.privBytesPerSecond=n}id(){return this.privAudioNode.id()}read(){if(this.privReplay&&this.privBuffers.length!==0){const t=this.privReplayOffset-this.privBufferStartOffset;let n=Math.round(t*this.privBytesPerSecond*1e-7);n%2!==0&&n++;let r=0;for(;r=this.privBuffers[r].chunk.buffer.byteLength;)n-=this.privBuffers[r++].chunk.buffer.byteLength;if(r(t&&t.buffer&&(this.privBuffers.push(new eO(t,this.privBufferSerial++,this.privBufferedBytes)),this.privBufferedBytes+=t.buffer.byteLength),t))}detach(){return this.privBuffers=void 0,this.privAudioNode.detach()}replay(){this.privBuffers&&this.privBuffers.length!==0&&(this.privReplay=!0,this.privReplayOffset=this.privLastShrinkOffset)}shrinkBuffers(t){if(this.privBuffers===void 0||this.privBuffers.length===0)return;this.privLastShrinkOffset=t;const n=t-this.privBufferStartOffset;let r=Math.round(n*this.privBytesPerSecond*1e-7),i=0;for(;i=this.privBuffers[i].chunk.buffer.byteLength;)r-=this.privBuffers[i++].chunk.buffer.byteLength;this.privBufferStartOffset=Math.round(t-r/this.privBytesPerSecond*1e7),this.privBuffers=this.privBuffers.slice(i)}findTimeAtOffset(t){if(t=r&&t<=i)return n.chunk.timeReceived}return 0}}class eO{constructor(t,n,r){this.chunk=t,this.serial=n,this.byteOffset=r}}class tO{constructor(t){Fe.throwIfNullOrUndefined(void 0,` +File System access not available, please use Push or PullAudioOutputStream`),this.privFd=(void 0)(t,"w")}set format(t){Fe.throwIfNotUndefined(this.privAudioFormat,"format is already set"),this.privAudioFormat=t;let n=0;this.privAudioFormat.hasHeader&&(n=this.privAudioFormat.header.byteLength),this.privFd!==void 0&&(this.privWriteStream=(void 0)("",{fd:this.privFd,start:n,autoClose:!1}))}write(t){Fe.throwIfNullOrUndefined(this.privAudioFormat,"must set format before writing."),this.privWriteStream!==void 0&&this.privWriteStream.write(new Uint8Array(t.slice(0)))}close(){this.privFd!==void 0&&(this.privWriteStream.on("finish",()=>{this.privAudioFormat.hasHeader&&(this.privAudioFormat.updateHeader(this.privWriteStream.bytesWritten),(void 0)(this.privFd,new Int8Array(this.privAudioFormat.header),0,this.privAudioFormat.header.byteLength,0)),(void 0)(this.privFd),this.privFd=void 0}),this.privWriteStream.end())}id(){return this.privId}}var yg=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class y9{constructor(){}static createPushStream(t){return n6.create(t)}static createPullStream(t,n){return r6.create(t,n)}}class n6 extends y9{static create(t){return new nO(t)}}class nO extends n6{constructor(t){super(),t===void 0?this.privFormat=fa.getDefaultInputFormat():this.privFormat=t,this.privEvents=new pa,this.privId=Pt(),this.privStream=new E9(this.privFormat.avgBytesPerSec/10)}get format(){return Promise.resolve(this.privFormat)}write(t){this.privStream.writeStreamChunk({buffer:t,isEnd:!1,timeReceived:Date.now()})}close(){this.privStream.close()}id(){return this.privId}turnOn(){this.onEvent(new Lh(this.privId)),this.onEvent(new z1(this.privId))}attach(t){return yg(this,void 0,void 0,function*(){this.onEvent(new dc(this.privId,t)),yield this.turnOn();const n=this.privStream;return this.onEvent(new hc(this.privId,t)),{detach:()=>yg(this,void 0,void 0,function*(){return this.onEvent(new mo(this.privId,t)),this.turnOff()}),id:()=>t,read:()=>n.read()}})}detach(t){this.onEvent(new mo(this.privId,t))}turnOff(){}get events(){return this.privEvents}get deviceInfo(){return Promise.resolve({bitspersample:this.privFormat.bitsPerSample,channelcount:this.privFormat.channels,connectivity:Bl.Unknown,manufacturer:"Speech SDK",model:"PushStream",samplerate:this.privFormat.samplesPerSec,type:hs.Stream})}onEvent(t){this.privEvents.onEvent(t),In.instance.onEvent(t)}toBuffer(t){const n=Buffer.alloc(t.byteLength),r=new Uint8Array(t);for(let i=0;i(this.privCallback.close(),this.onEvent(new mo(this.privId,t)),this.turnOff()),id:()=>t,read:()=>{let n=0,r;for(;nt.byteLength)return n.set(this.privLastChunkView.slice(0,t.byteLength)),this.privLastChunkView=this.privLastChunkView.slice(t.byteLength),Promise.resolve(t.byteLength);n.set(this.privLastChunkView),r=this.privLastChunkView.length,this.privLastChunkView=void 0}for(;rt.byteLength-r?(o=i.buffer.slice(0,t.byteLength-r),this.privLastChunkView=new Int8Array(i.buffer.slice(t.byteLength-r))):o=i.buffer,n.set(new Int8Array(o),r),r+=o.byteLength}else this.privStream.readEnded()}return r})}write(t){Fe.throwIfNullOrUndefined(this.privStream,"must set format before writing"),this.privStream.writeStreamChunk({buffer:t,isEnd:!1,timeReceived:Date.now()})}close(){this.privStream.close()}}class s6 extends o6{constructor(){super()}static create(t){return new l6(t)}}class l6 extends s6{constructor(t){super(),this.privId=Pt(),this.privCallback=t}set format(t){}write(t){this.privCallback.write&&this.privCallback.write(t)}close(){this.privCallback.close&&this.privCallback.close()}id(){return this.privId}}class Xl{static fromDefaultMicrophoneInput(){const t=new vm(!0);return new Bs(new Jr(t))}static fromMicrophoneInput(t){const n=new vm(!0);return new Bs(new Jr(n,t))}static fromWavFileInput(t,n="unnamedBuffer.wav"){return new Bs(new QP(t,n))}static fromStreamInput(t){if(t instanceof iO)return new Bs(new i6(t));if(t instanceof y9)return new Bs(t);if(typeof MediaStream<"u"&&t instanceof MediaStream){const n=new vm(!1);return new Bs(new Jr(n,null,null,t))}throw new Error("Not Supported Type")}static fromDefaultSpeakerOutput(){return new Hs(new By)}static fromSpeakerOutput(t){if(t===void 0)return Xl.fromDefaultSpeakerOutput();if(t instanceof By)return new Hs(t);throw new Error("Not Supported Type")}static fromAudioFileOutput(t){return new Hs(new tO(t))}static fromStreamOutput(t){if(t instanceof oO)return new Hs(new l6(t));if(t instanceof s6)return new Hs(t);if(t instanceof T9)return new Hs(t);throw new Error("Not Supported Type")}}class Bs extends Xl{constructor(t){super(),this.privSource=t}get format(){return this.privSource.format}close(t,n){this.privSource.turnOff().then(()=>{t&&t()},r=>{n&&n(r)})}id(){return this.privSource.id()}turnOn(){return this.privSource.turnOn()}attach(t){return this.privSource.attach(t)}detach(t){return this.privSource.detach(t)}turnOff(){return this.privSource.turnOff()}get events(){return this.privSource.events}setProperty(t,n){if(Fe.throwIfNull(n,"value"),this.privSource.setProperty!==void 0)this.privSource.setProperty(t,n);else throw new Error("This AudioConfig instance does not support setting properties.")}getProperty(t,n){if(this.privSource.getProperty!==void 0)return this.privSource.getProperty(t,n);throw new Error("This AudioConfig instance does not support getting properties.")}get deviceInfo(){return this.privSource.deviceInfo}}class Hs extends Xl{constructor(t){super(),this.privDestination=t}set format(t){this.privDestination.format=t}write(t){this.privDestination.write(t)}close(){this.privDestination.close()}id(){return this.privDestination.id()}setProperty(){throw new Error("This AudioConfig instance does not support setting properties.")}getProperty(){throw new Error("This AudioConfig instance does not support getting properties.")}}var Ni;(function(e){e[e.Error=0]="Error",e[e.EndOfStream=1]="EndOfStream"})(Ni||(Ni={}));class iO{}class oO{}class Tg{constructor(t){this.privSessionId=t}get sessionId(){return this.privSessionId}}class Jd extends Tg{constructor(t,n){super(n),this.privOffset=t}get offset(){return this.privOffset}}var Dt;(function(e){e[e.Simple=0]="Simple",e[e.Detailed=1]="Detailed"})(Dt||(Dt={}));class aO{constructor(t,n,r,i,o,a,s,l,u,c){this.privResultId=t,this.privReason=n,this.privText=r,this.privDuration=i,this.privOffset=o,this.privLanguage=a,this.privLanguageDetectionConfidence=s,this.privErrorDetails=l,this.privJson=u,this.privProperties=c}get resultId(){return this.privResultId}get reason(){return this.privReason}get text(){return this.privText}get duration(){return this.privDuration}get offset(){return this.privOffset}get language(){return this.privLanguage}get languageDetectionConfidence(){return this.privLanguageDetectionConfidence}get errorDetails(){return this.privErrorDetails}get json(){return this.privJson}get properties(){return this.privProperties}}class hf extends aO{constructor(t,n,r,i,o,a,s,l,u,c,f){super(t,n,r,i,o,a,s,u,c,f),this.privSpeakerId=l}get speakerId(){return this.privSpeakerId}}class Py extends Jd{constructor(t,n,r){super(n,r),this.privResult=t}get result(){return this.privResult}}class sO extends Jd{constructor(t,n,r,i,o){super(i,o),this.privReason=t,this.privErrorDetails=n,this.privErrorCode=r}get reason(){return this.privReason}get errorCode(){return this.privErrorCode}get errorDetails(){return this.privErrorDetails}}class lO extends sO{}var ei;(function(e){e[e.NoMatch=0]="NoMatch",e[e.Canceled=1]="Canceled",e[e.RecognizingSpeech=2]="RecognizingSpeech",e[e.RecognizedSpeech=3]="RecognizedSpeech",e[e.RecognizedKeyword=4]="RecognizedKeyword",e[e.RecognizingIntent=5]="RecognizingIntent",e[e.RecognizedIntent=6]="RecognizedIntent",e[e.TranslatingSpeech=7]="TranslatingSpeech",e[e.TranslatedSpeech=8]="TranslatedSpeech",e[e.SynthesizingAudio=9]="SynthesizingAudio",e[e.SynthesizingAudioCompleted=10]="SynthesizingAudioCompleted",e[e.SynthesizingAudioStarted=11]="SynthesizingAudioStarted",e[e.EnrollingVoiceProfile=12]="EnrollingVoiceProfile",e[e.EnrolledVoiceProfile=13]="EnrolledVoiceProfile",e[e.RecognizedSpeakers=14]="RecognizedSpeakers",e[e.RecognizedSpeaker=15]="RecognizedSpeaker",e[e.ResetVoiceProfile=16]="ResetVoiceProfile",e[e.DeletedVoiceProfile=17]="DeletedVoiceProfile",e[e.VoicesListRetrieved=18]="VoicesListRetrieved",e[e.TranslatingParticipantSpeech=19]="TranslatingParticipantSpeech",e[e.TranslatedParticipantSpeech=20]="TranslatedParticipantSpeech",e[e.TranslatedInstantMessage=21]="TranslatedInstantMessage",e[e.TranslatedParticipantInstantMessage=22]="TranslatedParticipantInstantMessage"})(ei||(ei={}));class u6{constructor(){}static fromSubscription(t,n){Fe.throwIfNullOrWhitespace(t,"subscriptionKey"),Fe.throwIfNullOrWhitespace(n,"region");const r=new ul;return r.setProperty(q.SpeechServiceConnection_Region,n),r.setProperty(q.SpeechServiceConnection_IntentRegion,n),r.setProperty(q.SpeechServiceConnection_Key,t),r}static fromEndpoint(t,n){Fe.throwIfNull(t,"endpoint");const r=new ul;return r.setProperty(q.SpeechServiceConnection_Endpoint,t.href),n!==void 0&&r.setProperty(q.SpeechServiceConnection_Key,n),r}static fromHost(t,n){Fe.throwIfNull(t,"hostName");const r=new ul;return r.setProperty(q.SpeechServiceConnection_Host,t.protocol+"//"+t.hostname+(t.port===""?"":":"+t.port)),n!==void 0&&r.setProperty(q.SpeechServiceConnection_Key,n),r}static fromAuthorizationToken(t,n){Fe.throwIfNull(t,"authorizationToken"),Fe.throwIfNullOrWhitespace(n,"region");const r=new ul;return r.setProperty(q.SpeechServiceConnection_Region,n),r.setProperty(q.SpeechServiceConnection_IntentRegion,n),r.authorizationToken=t,r}close(){}}class ul extends u6{constructor(){super(),this.privProperties=new W1,this.speechRecognitionLanguage="en-US",this.outputFormat=Dt.Simple}get properties(){return this.privProperties}get endPoint(){return new URL(this.privProperties.getProperty(q.SpeechServiceConnection_Endpoint))}get subscriptionKey(){return this.privProperties.getProperty(q.SpeechServiceConnection_Key)}get region(){return this.privProperties.getProperty(q.SpeechServiceConnection_Region)}get authorizationToken(){return this.privProperties.getProperty(q.SpeechServiceAuthorization_Token)}set authorizationToken(t){this.privProperties.setProperty(q.SpeechServiceAuthorization_Token,t)}get speechRecognitionLanguage(){return this.privProperties.getProperty(q.SpeechServiceConnection_RecoLanguage)}set speechRecognitionLanguage(t){this.privProperties.setProperty(q.SpeechServiceConnection_RecoLanguage,t)}get autoDetectSourceLanguages(){return this.privProperties.getProperty(q.SpeechServiceConnection_AutoDetectSourceLanguages)}set autoDetectSourceLanguages(t){this.privProperties.setProperty(q.SpeechServiceConnection_AutoDetectSourceLanguages,t)}get outputFormat(){return Dt[this.privProperties.getProperty(Hl,void 0)]}set outputFormat(t){this.privProperties.setProperty(Hl,Dt[t])}get endpointId(){return this.privProperties.getProperty(q.SpeechServiceConnection_EndpointId)}set endpointId(t){this.privProperties.setProperty(q.SpeechServiceConnection_EndpointId,t)}setProperty(t,n){Fe.throwIfNull(n,"value"),this.privProperties.setProperty(t,n)}getProperty(t,n){return this.privProperties.getProperty(t,n)}setProxy(t,n,r,i){this.setProperty(q[q.SpeechServiceConnection_ProxyHostName],t),this.setProperty(q[q.SpeechServiceConnection_ProxyPort],n),this.setProperty(q[q.SpeechServiceConnection_ProxyUserName],r),this.setProperty(q[q.SpeechServiceConnection_ProxyPassword],i)}setServiceProperty(t,n){const r=JSON.parse(this.privProperties.getProperty(Sg,"{}"));r[t]=n,this.privProperties.setProperty(Sg,JSON.stringify(r))}setProfanity(t){this.privProperties.setProperty(q.SpeechServiceResponse_ProfanityOption,Cg[t])}enableAudioLogging(){this.privProperties.setProperty(q.SpeechServiceConnection_EnableAudioLogging,"true")}requestWordLevelTimestamps(){this.privProperties.setProperty(q.SpeechServiceResponse_RequestWordLevelTimestamps,"true")}enableDictation(){this.privProperties.setProperty(p6,"true")}clone(){const t=new ul;return t.privProperties=this.privProperties.clone(),t}get speechSynthesisLanguage(){return this.privProperties.getProperty(q.SpeechServiceConnection_SynthLanguage)}set speechSynthesisLanguage(t){this.privProperties.setProperty(q.SpeechServiceConnection_SynthLanguage,t)}get speechSynthesisVoiceName(){return this.privProperties.getProperty(q.SpeechServiceConnection_SynthVoice)}set speechSynthesisVoiceName(t){this.privProperties.setProperty(q.SpeechServiceConnection_SynthVoice,t)}get speechSynthesisOutputFormat(){return ve[this.privProperties.getProperty(q.SpeechServiceConnection_SynthOutputFormat,void 0)]}set speechSynthesisOutputFormat(t){this.privProperties.setProperty(q.SpeechServiceConnection_SynthOutputFormat,ve[t])}}class W1{constructor(){this.privKeys=[],this.privValues=[]}getProperty(t,n){let r;typeof t=="string"?r=t:r=q[t];for(let i=0;i{if(t.getProperty(n,void 0)===void 0){const r=this.getProperty(n);t.setProperty(n,r)}})}get keys(){return this.privKeys}}var q;(function(e){e[e.SpeechServiceConnection_Key=0]="SpeechServiceConnection_Key",e[e.SpeechServiceConnection_Endpoint=1]="SpeechServiceConnection_Endpoint",e[e.SpeechServiceConnection_Region=2]="SpeechServiceConnection_Region",e[e.SpeechServiceAuthorization_Token=3]="SpeechServiceAuthorization_Token",e[e.SpeechServiceAuthorization_Type=4]="SpeechServiceAuthorization_Type",e[e.SpeechServiceConnection_EndpointId=5]="SpeechServiceConnection_EndpointId",e[e.SpeechServiceConnection_TranslationToLanguages=6]="SpeechServiceConnection_TranslationToLanguages",e[e.SpeechServiceConnection_TranslationVoice=7]="SpeechServiceConnection_TranslationVoice",e[e.SpeechServiceConnection_TranslationFeatures=8]="SpeechServiceConnection_TranslationFeatures",e[e.SpeechServiceConnection_IntentRegion=9]="SpeechServiceConnection_IntentRegion",e[e.SpeechServiceConnection_ProxyHostName=10]="SpeechServiceConnection_ProxyHostName",e[e.SpeechServiceConnection_ProxyPort=11]="SpeechServiceConnection_ProxyPort",e[e.SpeechServiceConnection_ProxyUserName=12]="SpeechServiceConnection_ProxyUserName",e[e.SpeechServiceConnection_ProxyPassword=13]="SpeechServiceConnection_ProxyPassword",e[e.SpeechServiceConnection_RecoMode=14]="SpeechServiceConnection_RecoMode",e[e.SpeechServiceConnection_RecoLanguage=15]="SpeechServiceConnection_RecoLanguage",e[e.Speech_SessionId=16]="Speech_SessionId",e[e.SpeechServiceConnection_SynthLanguage=17]="SpeechServiceConnection_SynthLanguage",e[e.SpeechServiceConnection_SynthVoice=18]="SpeechServiceConnection_SynthVoice",e[e.SpeechServiceConnection_SynthOutputFormat=19]="SpeechServiceConnection_SynthOutputFormat",e[e.SpeechServiceConnection_AutoDetectSourceLanguages=20]="SpeechServiceConnection_AutoDetectSourceLanguages",e[e.SpeechServiceResponse_RequestDetailedResultTrueFalse=21]="SpeechServiceResponse_RequestDetailedResultTrueFalse",e[e.SpeechServiceResponse_RequestProfanityFilterTrueFalse=22]="SpeechServiceResponse_RequestProfanityFilterTrueFalse",e[e.SpeechServiceResponse_JsonResult=23]="SpeechServiceResponse_JsonResult",e[e.SpeechServiceResponse_JsonErrorDetails=24]="SpeechServiceResponse_JsonErrorDetails",e[e.CancellationDetails_Reason=25]="CancellationDetails_Reason",e[e.CancellationDetails_ReasonText=26]="CancellationDetails_ReasonText",e[e.CancellationDetails_ReasonDetailedText=27]="CancellationDetails_ReasonDetailedText",e[e.LanguageUnderstandingServiceResponse_JsonResult=28]="LanguageUnderstandingServiceResponse_JsonResult",e[e.SpeechServiceConnection_Url=29]="SpeechServiceConnection_Url",e[e.SpeechServiceConnection_InitialSilenceTimeoutMs=30]="SpeechServiceConnection_InitialSilenceTimeoutMs",e[e.SpeechServiceConnection_EndSilenceTimeoutMs=31]="SpeechServiceConnection_EndSilenceTimeoutMs",e[e.Speech_SegmentationSilenceTimeoutMs=32]="Speech_SegmentationSilenceTimeoutMs",e[e.SpeechServiceConnection_EnableAudioLogging=33]="SpeechServiceConnection_EnableAudioLogging",e[e.SpeechServiceConnection_LanguageIdMode=34]="SpeechServiceConnection_LanguageIdMode",e[e.SpeechServiceConnection_RecognitionEndpointVersion=35]="SpeechServiceConnection_RecognitionEndpointVersion",e[e.SpeechServiceConnection_SpeakerIdMode=36]="SpeechServiceConnection_SpeakerIdMode",e[e.SpeechServiceResponse_ProfanityOption=37]="SpeechServiceResponse_ProfanityOption",e[e.SpeechServiceResponse_PostProcessingOption=38]="SpeechServiceResponse_PostProcessingOption",e[e.SpeechServiceResponse_RequestWordLevelTimestamps=39]="SpeechServiceResponse_RequestWordLevelTimestamps",e[e.SpeechServiceResponse_StablePartialResultThreshold=40]="SpeechServiceResponse_StablePartialResultThreshold",e[e.SpeechServiceResponse_OutputFormatOption=41]="SpeechServiceResponse_OutputFormatOption",e[e.SpeechServiceResponse_TranslationRequestStablePartialResult=42]="SpeechServiceResponse_TranslationRequestStablePartialResult",e[e.SpeechServiceResponse_RequestWordBoundary=43]="SpeechServiceResponse_RequestWordBoundary",e[e.SpeechServiceResponse_RequestPunctuationBoundary=44]="SpeechServiceResponse_RequestPunctuationBoundary",e[e.SpeechServiceResponse_RequestSentenceBoundary=45]="SpeechServiceResponse_RequestSentenceBoundary",e[e.Conversation_ApplicationId=46]="Conversation_ApplicationId",e[e.Conversation_DialogType=47]="Conversation_DialogType",e[e.Conversation_Initial_Silence_Timeout=48]="Conversation_Initial_Silence_Timeout",e[e.Conversation_From_Id=49]="Conversation_From_Id",e[e.Conversation_Conversation_Id=50]="Conversation_Conversation_Id",e[e.Conversation_Custom_Voice_Deployment_Ids=51]="Conversation_Custom_Voice_Deployment_Ids",e[e.Conversation_Speech_Activity_Template=52]="Conversation_Speech_Activity_Template",e[e.Conversation_Request_Bot_Status_Messages=53]="Conversation_Request_Bot_Status_Messages",e[e.Conversation_Agent_Connection_Id=54]="Conversation_Agent_Connection_Id",e[e.SpeechServiceConnection_Host=55]="SpeechServiceConnection_Host",e[e.ConversationTranslator_Host=56]="ConversationTranslator_Host",e[e.ConversationTranslator_Name=57]="ConversationTranslator_Name",e[e.ConversationTranslator_CorrelationId=58]="ConversationTranslator_CorrelationId",e[e.ConversationTranslator_Token=59]="ConversationTranslator_Token",e[e.PronunciationAssessment_ReferenceText=60]="PronunciationAssessment_ReferenceText",e[e.PronunciationAssessment_GradingSystem=61]="PronunciationAssessment_GradingSystem",e[e.PronunciationAssessment_Granularity=62]="PronunciationAssessment_Granularity",e[e.PronunciationAssessment_EnableMiscue=63]="PronunciationAssessment_EnableMiscue",e[e.PronunciationAssessment_Json=64]="PronunciationAssessment_Json",e[e.PronunciationAssessment_Params=65]="PronunciationAssessment_Params",e[e.SpeakerRecognition_Api_Version=66]="SpeakerRecognition_Api_Version",e[e.WebWorkerLoadType=67]="WebWorkerLoadType",e[e.TalkingAvatarService_WebRTC_SDP=68]="TalkingAvatarService_WebRTC_SDP"})(q||(q={}));var Su=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class C9{constructor(t,n,r){this.audioConfig=t!==void 0?t:Xl.fromDefaultMicrophoneInput(),this.privDisposed=!1,this.privProperties=n.clone(),this.privConnectionFactory=r,this.implCommonRecognizerSetup()}close(t,n){Fe.throwIfDisposed(this.privDisposed),Lu(this.dispose(!0),t,n)}get internalData(){return this.privReco}dispose(t){return Su(this,void 0,void 0,function*(){this.privDisposed||(this.privDisposed=!0,t&&this.privReco&&(yield this.privReco.audioSource.turnOff(),yield this.privReco.dispose()))})}static get telemetryEnabled(){return oo.telemetryDataEnabled}static enableTelemetry(t){oo.telemetryDataEnabled=t}implCommonRecognizerSetup(){let t=typeof window<"u"?"Browser":"Node",n="unknown",r="unknown";typeof navigator<"u"&&(t=t+"/"+navigator.platform,n=navigator.userAgent,r=navigator.appVersion);const i=this.createRecognizerConfig(new d6(new h6(new bO(t,n,r))));this.privReco=this.createServiceRecognizer(C9.getAuthFromProperties(this.privProperties),this.privConnectionFactory,this.audioConfig,i)}recognizeOnceAsyncImpl(t){return Su(this,void 0,void 0,function*(){Fe.throwIfDisposed(this.privDisposed);const n=new fn;yield this.implRecognizerStop(),yield this.privReco.recognize(t,n.resolve,n.reject);const r=yield n.promise;return yield this.implRecognizerStop(),r})}startContinuousRecognitionAsyncImpl(t){return Su(this,void 0,void 0,function*(){Fe.throwIfDisposed(this.privDisposed),yield this.implRecognizerStop(),yield this.privReco.recognize(t,void 0,void 0)})}stopContinuousRecognitionAsyncImpl(){return Su(this,void 0,void 0,function*(){Fe.throwIfDisposed(this.privDisposed),yield this.implRecognizerStop()})}implRecognizerStop(){return Su(this,void 0,void 0,function*(){this.privReco&&(yield this.privReco.stopRecognizing())})}static getAuthFromProperties(t){const n=t.getProperty(q.SpeechServiceConnection_Key,void 0);return n&&n!==""?new qP(n):new U1(()=>{const i=t.getProperty(q.SpeechServiceAuthorization_Token,void 0);return Promise.resolve(i)},()=>{const i=t.getProperty(q.SpeechServiceAuthorization_Token,void 0);return Promise.resolve(i)})}}var uO=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class S9 extends C9{constructor(t,n){const r=t;Fe.throwIfNull(r,"speechConfig"),Fe.throwIfNullOrWhitespace(r.properties.getProperty(q.SpeechServiceConnection_RecoLanguage),q[q.SpeechServiceConnection_RecoLanguage]),super(n,r.properties,new mO),this.privDisposedRecognizer=!1}static FromConfig(t,n,r){const i=t;return n.properties.mergeTo(i.properties),new S9(t,r)}get endpointId(){return Fe.throwIfDisposed(this.privDisposedRecognizer),this.properties.getProperty(q.SpeechServiceConnection_EndpointId,"00000000-0000-0000-0000-000000000000")}get authorizationToken(){return this.properties.getProperty(q.SpeechServiceAuthorization_Token)}set authorizationToken(t){Fe.throwIfNullOrWhitespace(t,"token"),this.properties.setProperty(q.SpeechServiceAuthorization_Token,t)}get speechRecognitionLanguage(){return Fe.throwIfDisposed(this.privDisposedRecognizer),this.properties.getProperty(q.SpeechServiceConnection_RecoLanguage)}get outputFormat(){return Fe.throwIfDisposed(this.privDisposedRecognizer),this.properties.getProperty(Hl,Dt[Dt.Simple])===Dt[Dt.Simple]?Dt.Simple:Dt.Detailed}get properties(){return this.privProperties}recognizeOnceAsync(t,n){Lu(this.recognizeOnceAsyncImpl(ii.Interactive),t,n)}startContinuousRecognitionAsync(t,n){Lu(this.startContinuousRecognitionAsyncImpl(ii.Conversation),t,n)}stopContinuousRecognitionAsync(t,n){Lu(this.stopContinuousRecognitionAsyncImpl(),t,n)}startKeywordRecognitionAsync(t,n,r){Fe.throwIfNull(t,"model"),r&&r("Not yet implemented.")}stopKeywordRecognitionAsync(t){t&&t()}close(t,n){Fe.throwIfDisposed(this.privDisposedRecognizer),Lu(this.dispose(!0),t,n)}dispose(t){const n=Object.create(null,{dispose:{get:()=>super.dispose}});return uO(this,void 0,void 0,function*(){this.privDisposedRecognizer||(t&&(this.privDisposedRecognizer=!0,yield this.implRecognizerStop()),yield n.dispose.call(this,t))})}createRecognizerConfig(t){return new hO(t,this.privProperties)}createServiceRecognizer(t,n,r,i){const o=r;return new vO(t,n,o,i,this)}}var Wt;(function(e){e[e.NoError=0]="NoError",e[e.AuthenticationFailure=1]="AuthenticationFailure",e[e.BadRequestParameters=2]="BadRequestParameters",e[e.TooManyRequests=3]="TooManyRequests",e[e.ConnectionFailure=4]="ConnectionFailure",e[e.ServiceTimeout=5]="ServiceTimeout",e[e.ServiceError=6]="ServiceError",e[e.RuntimeError=7]="RuntimeError",e[e.Forbidden=8]="Forbidden"})(Wt||(Wt={}));class we{}we.BotId="botid";we.CustomSpeechDeploymentId="cid";we.CustomVoiceDeploymentId="deploymentId";we.EnableAudioLogging="storeAudio";we.EnableLanguageId="lidEnabled";we.EnableWordLevelTimestamps="wordLevelTimestamps";we.EndSilenceTimeoutMs="endSilenceTimeoutMs";we.SegmentationSilenceTimeoutMs="segmentationSilenceTimeoutMs";we.Format="format";we.InitialSilenceTimeoutMs="initialSilenceTimeoutMs";we.Language="language";we.Profanity="profanity";we.RequestBotStatusMessages="enableBotMessageStatus";we.StableIntermediateThreshold="stableIntermediateThreshold";we.StableTranslation="stableTranslation";we.TestHooks="testhooks";we.Postprocessing="postprocessing";we.CtsMeetingId="meetingId";we.CtsDeviceId="deviceId";we.CtsIsParticipant="isParticipant";we.EnableAvatar="enableTalkingAvatar";class Oy{static getHostSuffix(t){if(t){if(t.toLowerCase().startsWith("china"))return".azure.cn";if(t.toLowerCase().startsWith("usgov"))return".azure.us"}return".microsoft.com"}setCommonUrlParams(t,n,r){new Map([[q.Speech_SegmentationSilenceTimeoutMs,we.SegmentationSilenceTimeoutMs],[q.SpeechServiceConnection_EnableAudioLogging,we.EnableAudioLogging],[q.SpeechServiceConnection_EndSilenceTimeoutMs,we.EndSilenceTimeoutMs],[q.SpeechServiceConnection_InitialSilenceTimeoutMs,we.InitialSilenceTimeoutMs],[q.SpeechServiceResponse_PostProcessingOption,we.Postprocessing],[q.SpeechServiceResponse_ProfanityOption,we.Profanity],[q.SpeechServiceResponse_RequestWordLevelTimestamps,we.EnableWordLevelTimestamps],[q.SpeechServiceResponse_StablePartialResultThreshold,we.StableIntermediateThreshold]]).forEach((a,s)=>{this.setUrlParameter(s,a,t,n,r)});const o=JSON.parse(t.parameters.getProperty(Sg,"{}"));Object.keys(o).forEach(a=>{n[a]=o[a]})}setUrlParameter(t,n,r,i,o){const a=r.parameters.getProperty(t,void 0);a&&(!o||o.search(n)===-1)&&(i[n]=a.toLocaleLowerCase())}}var Cg;(function(e){e[e.Masked=0]="Masked",e[e.Removed=1]="Removed",e[e.Raw=2]="Raw"})(Cg||(Cg={}));var Em=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};const cO=60*30,Ly={[se.PCM]:"audio/wav",[se.MuLaw]:"audio/x-wav",[se.MP3]:"audio/mpeg",[se.OGG_OPUS]:"audio/ogg",[se.WEBM_OPUS]:"audio/webm; codecs=opus",[se.ALaw]:"audio/x-wav",[se.FLAC]:"audio/flac"};class By{constructor(t){this.privPlaybackStarted=!1,this.privAppendingToBuffer=!1,this.privMediaSourceOpened=!1,this.privBytesReceived=0,this.privId=t||Pt(),this.privIsPaused=!1,this.privIsClosed=!1}id(){return this.privId}write(t,n,r){this.privAudioBuffer!==void 0?(this.privAudioBuffer.push(t),this.updateSourceBuffer().then(()=>{n&&n()},i=>{r&&r(i)})):this.privAudioOutputStream!==void 0&&(this.privAudioOutputStream.write(t),this.privBytesReceived+=t.byteLength)}close(t,n){if(this.privIsClosed=!0,this.privSourceBuffer!==void 0)this.handleSourceBufferUpdateEnd().then(()=>{t&&t()},r=>{n&&n(r)});else if(this.privAudioOutputStream!==void 0&&typeof window<"u")if((this.privFormat.formatTag===se.PCM||this.privFormat.formatTag===se.MuLaw||this.privFormat.formatTag===se.ALaw)&&this.privFormat.hasHeader===!1)console.warn("Play back is not supported for raw PCM, mulaw or alaw format without header."),this.onAudioEnd&&this.onAudioEnd(this);else{let r=new ArrayBuffer(this.privBytesReceived);this.privAudioOutputStream.read(r).then(()=>{r=this.privFormat.addHeader(r);const i=new Blob([r],{type:Ly[this.privFormat.formatTag]});this.privAudio.src=window.URL.createObjectURL(i),this.notifyPlayback().then(()=>{t&&t()},o=>{n&&n(o)})},i=>{n&&n(i)})}else this.onAudioEnd&&this.onAudioEnd(this)}set format(t){if(typeof AudioContext<"u"||typeof window<"u"&&typeof window.webkitAudioContext<"u"){this.privFormat=t;const n=Ly[this.privFormat.formatTag];n===void 0?console.warn(`Unknown mimeType for format ${se[this.privFormat.formatTag]}; playback is not supported.`):typeof MediaSource<"u"&&MediaSource.isTypeSupported(n)?(this.privAudio=new Audio,this.privAudioBuffer=[],this.privMediaSource=new MediaSource,this.privAudio.src=URL.createObjectURL(this.privMediaSource),this.privAudio.load(),this.privMediaSource.onsourceopen=()=>{this.privMediaSourceOpened=!0,this.privMediaSource.duration=cO,this.privSourceBuffer=this.privMediaSource.addSourceBuffer(n),this.privSourceBuffer.onupdate=()=>{this.updateSourceBuffer().catch(r=>{In.instance.onEvent(new e1(r))})},this.privSourceBuffer.onupdateend=()=>{this.handleSourceBufferUpdateEnd().catch(r=>{In.instance.onEvent(new e1(r))})},this.privSourceBuffer.onupdatestart=()=>{this.privAppendingToBuffer=!1}},this.updateSourceBuffer().catch(r=>{In.instance.onEvent(new e1(r))})):(console.warn(`Format ${se[this.privFormat.formatTag]} could not be played by MSE, streaming playback is not enabled.`),this.privAudioOutputStream=new a6,this.privAudioOutputStream.format=this.privFormat,this.privAudio=new Audio)}}get volume(){var t,n;return(n=(t=this.privAudio)===null||t===void 0?void 0:t.volume)!==null&&n!==void 0?n:-1}set volume(t){this.privAudio&&(this.privAudio.volume=t)}mute(){this.privAudio&&(this.privAudio.muted=!0)}unmute(){this.privAudio&&(this.privAudio.muted=!1)}get isClosed(){return this.privIsClosed}get currentTime(){return this.privAudio!==void 0?this.privAudio.currentTime:-1}pause(){!this.privIsPaused&&this.privAudio!==void 0&&(this.privAudio.pause(),this.privIsPaused=!0)}resume(t,n){this.privIsPaused&&this.privAudio!==void 0&&(this.privAudio.play().then(()=>{t&&t()},r=>{n&&n(r)}),this.privIsPaused=!1)}get internalAudio(){return this.privAudio}updateSourceBuffer(){return Em(this,void 0,void 0,function*(){if(this.privAudioBuffer!==void 0&&this.privAudioBuffer.length>0&&this.sourceBufferAvailable()){this.privAppendingToBuffer=!0;const t=this.privAudioBuffer.shift();try{this.privSourceBuffer.appendBuffer(t)}catch{this.privAudioBuffer.unshift(t),console.log("buffer filled, pausing addition of binaries until space is made");return}yield this.notifyPlayback()}else this.canEndStream()&&(yield this.handleSourceBufferUpdateEnd())})}handleSourceBufferUpdateEnd(){return Em(this,void 0,void 0,function*(){this.canEndStream()&&this.sourceBufferAvailable()&&(this.privMediaSource.endOfStream(),yield this.notifyPlayback())})}notifyPlayback(){return Em(this,void 0,void 0,function*(){!this.privPlaybackStarted&&this.privAudio!==void 0&&(this.privPlaybackStarted=!0,this.onAudioStart&&this.onAudioStart(this),this.privAudio.onended=()=>{this.onAudioEnd&&this.onAudioEnd(this)},this.privIsPaused||(yield this.privAudio.play()))})}canEndStream(){return this.isClosed&&this.privSourceBuffer!==void 0&&this.privAudioBuffer.length===0&&this.privMediaSourceOpened&&!this.privAppendingToBuffer&&this.privMediaSource.readyState==="open"}sourceBufferAvailable(){return this.privSourceBuffer!==void 0&&!this.privSourceBuffer.updating}}class _9{constructor(t,n,r,i){this.privProxyHostName=t,this.privProxyPort=n,this.privProxyUserName=r,this.privProxyPassword=i}static fromParameters(t){return new _9(t.getProperty(q.SpeechServiceConnection_ProxyHostName),parseInt(t.getProperty(q.SpeechServiceConnection_ProxyPort),10),t.getProperty(q.SpeechServiceConnection_ProxyUserName),t.getProperty(q.SpeechServiceConnection_ProxyPassword))}static fromRecognizerConfig(t){return this.fromParameters(t.parameters)}get HostName(){return this.privProxyHostName}get Port(){return this.privProxyPort}get UserName(){return this.privProxyUserName}get Password(){return this.privProxyPassword}}class zh extends cc{constructor(t,n,r,i=li.Info){super(t,i),this.privRequestId=n,this.privSessionId=r}get requestId(){return this.privRequestId}get sessionId(){return this.privSessionId}}class c6 extends zh{constructor(t,n,r,i){super("RecognitionTriggeredEvent",t,n),this.privAudioSourceId=r,this.privAudioNodeId=i}get audioSourceId(){return this.privAudioSourceId}get audioNodeId(){return this.privAudioNodeId}}class fO extends zh{constructor(t,n,r,i){super("ListeningStartedEvent",t,n),this.privAudioSourceId=r,this.privAudioNodeId=i}get audioSourceId(){return this.privAudioSourceId}get audioNodeId(){return this.privAudioNodeId}}class f6 extends zh{constructor(t,n,r){super("ConnectingToServiceEvent",t,r),this.privAuthFetchEventid=n}get authFetchEventid(){return this.privAuthFetchEventid}}class dO extends zh{constructor(t,n,r,i,o){super("RecognitionStartedEvent",t,o),this.privAudioSourceId=n,this.privAudioNodeId=r,this.privAuthFetchEventId=i}get audioSourceId(){return this.privAudioSourceId}get audioNodeId(){return this.privAudioNodeId}get authFetchEventId(){return this.privAuthFetchEventId}}var Hy;(function(e){e[e.Success=0]="Success",e[e.AudioSourceError=1]="AudioSourceError",e[e.AudioSourceTimeout=2]="AudioSourceTimeout",e[e.AuthTokenFetchError=3]="AuthTokenFetchError",e[e.AuthTokenFetchTimeout=4]="AuthTokenFetchTimeout",e[e.UnAuthorized=5]="UnAuthorized",e[e.ConnectTimeout=6]="ConnectTimeout",e[e.ConnectError=7]="ConnectError",e[e.ClientRecognitionActivityTimeout=8]="ClientRecognitionActivityTimeout",e[e.UnknownError=9]="UnknownError"})(Hy||(Hy={}));class Si extends vg{constructor(t,n,r,i,o,a,s,l){if(!n)throw new wn("path");if(!r)throw new wn("requestId");const u={};if(u[ot.Path]=n,u[ot.RequestId]=r,u[ot.RequestTimestamp]=new Date().toISOString(),i&&(u[ot.ContentType]=i),a&&(u[ot.RequestStreamId]=a),s)for(const c in s)c&&(u[c]=s[c]);l?super(t,o,u,l):super(t,o,u),this.privPath=n,this.privRequestId=r,this.privContentType=i,this.privStreamId=a,this.privAdditionalHeaders=s}get path(){return this.privPath}get requestId(){return this.privRequestId}get contentType(){return this.privContentType}get streamId(){return this.privStreamId}get additionalHeaders(){return this.privAdditionalHeaders}static fromConnectionMessage(t){let n=null,r=null,i=null,o=null;const a={};if(t.headers)for(const s in t.headers)s&&(s.toLowerCase()===ot.Path.toLowerCase()?n=t.headers[s]:s.toLowerCase()===ot.RequestId.toLowerCase()?r=t.headers[s]:s.toLowerCase()===ot.ContentType.toLowerCase()?i=t.headers[s]:s.toLowerCase()===ot.RequestStreamId.toLowerCase()?o=t.headers[s]:a[s]=t.headers[s]);return new Si(t.messageType,n,r,i,t.body,o,a,t.id)}}var Kt=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class oo{constructor(t,n,r,i,o){if(this.privConnectionConfigurationPromise=void 0,this.privConnectionPromise=void 0,this.privSetTimeout=setTimeout,this.privIsLiveAudio=!1,this.privAverageBytesPerMs=0,this.privEnableSpeakerId=!1,this.recognizeOverride=void 0,this.recognizeSpeaker=void 0,this.disconnectOverride=void 0,this.receiveMessageOverride=void 0,this.sendPrePayloadJSONOverride=void 0,this.postConnectImplOverride=void 0,this.configConnectionOverride=void 0,this.handleSpeechPhraseMessage=void 0,this.handleSpeechHypothesisMessage=void 0,!t)throw new wn("authentication");if(!n)throw new wn("connectionFactory");if(!r)throw new wn("audioSource");if(!i)throw new wn("recognizerConfig");this.privEnableSpeakerId=i.isSpeakerDiarizationEnabled,this.privMustReportEndOfStream=!1,this.privAuthentication=t,this.privConnectionFactory=n,this.privAudioSource=r,this.privRecognizerConfig=i,this.privIsDisposed=!1,this.privRecognizer=o,this.privRequestSession=new yO(this.privAudioSource.id()),this.privConnectionEvents=new pa,this.privServiceEvents=new pa,this.privDynamicGrammar=new CO,this.privSpeechContext=new TO(this.privDynamicGrammar),this.privAgentConfig=new SO,this.privRecognizerConfig.parameters.getProperty(q.WebWorkerLoadType,"on").toLowerCase()==="on"&&typeof Blob<"u"&&typeof Worker<"u"?this.privSetTimeout=un.setTimeout:typeof window<"u"&&(this.privSetTimeout=window.setTimeout.bind(window)),this.connectionEvents.attach(s=>{if(s.name==="ConnectionClosedEvent"){const l=s;(l.statusCode===1003||l.statusCode===1007||l.statusCode===1002||l.statusCode===4e3||this.privRequestSession.numConnectionAttempts>this.privRecognizerConfig.maxRetryCount)&&this.cancelRecognitionLocal(Ni.Error,l.statusCode===1007?Wt.BadRequestParameters:Wt.ConnectionFailure,`${l.reason} websocket error code: ${l.statusCode}`)}}),this.privEnableSpeakerId&&(this.privDiarizationSessionId=Pt()),this.setLanguageIdJson(),this.setOutputDetailLevelJson()}setTranslationJson(){const t=this.privRecognizerConfig.parameters.getProperty(q.SpeechServiceConnection_TranslationToLanguages,void 0);if(t!==void 0){const n=t.split(","),r=this.privRecognizerConfig.parameters.getProperty(q.SpeechServiceConnection_TranslationVoice,void 0),i=r!==void 0?"Synthesize":"None";if(this.privSpeechContext.setSection("translation",{onSuccess:{action:i},output:{interimResults:{mode:"Always"}},targetLanguages:n}),r!==void 0){const o={};for(const a of n)o[a]=r;this.privSpeechContext.setSection("synthesis",{defaultVoices:o})}}}setSpeechSegmentationTimeoutJson(){const t=this.privRecognizerConfig.parameters.getProperty(q.Speech_SegmentationSilenceTimeoutMs,void 0);if(t!==void 0){const n=this.recognitionMode===ii.Conversation?"CONVERSATION":this.recognitionMode===ii.Dictation?"DICTATION":"INTERACTIVE",r=parseInt(t,10),i=this.privSpeechContext.getSection("phraseDetection");i.mode=n,i[n]={segmentation:{mode:"Custom",segmentationSilenceTimeoutMs:r}},this.privSpeechContext.setSection("phraseDetection",i)}}setLanguageIdJson(){const t=this.privSpeechContext.getSection("phraseDetection");if(this.privRecognizerConfig.autoDetectSourceLanguages!==void 0){const r=this.privRecognizerConfig.autoDetectSourceLanguages.split(",");let i;this.privRecognizerConfig.languageIdMode==="Continuous"?i="DetectContinuous":i="DetectAtAudioStart",this.privSpeechContext.setSection("languageId",{Priority:"PrioritizeLatency",languages:r,mode:i,onSuccess:{action:"Recognize"},onUnknown:{action:"None"}}),this.privSpeechContext.setSection("phraseOutput",{interimResults:{resultType:"Auto"},phraseResults:{resultType:"Always"}});const o=this.privRecognizerConfig.sourceLanguageModels;o!==void 0&&(t.customModels=o,t.onInterim={action:"None"},t.onSuccess={action:"None"})}this.privRecognizerConfig.parameters.getProperty(q.SpeechServiceConnection_TranslationToLanguages,void 0)!==void 0&&(t.onInterim={action:"Translate"},t.onSuccess={action:"Translate"},this.privSpeechContext.setSection("phraseOutput",{interimResults:{resultType:"None"},phraseResults:{resultType:"None"}})),this.privSpeechContext.setSection("phraseDetection",t)}setOutputDetailLevelJson(){this.privEnableSpeakerId&&(this.privRecognizerConfig.parameters.getProperty(q.SpeechServiceResponse_RequestWordLevelTimestamps,"false").toLowerCase()==="true"?this.privSpeechContext.setWordLevelTimings():this.privRecognizerConfig.parameters.getProperty(Hl,Dt[Dt.Simple]).toLowerCase()===Dt[Dt.Detailed].toLocaleLowerCase()&&this.privSpeechContext.setDetailedOutputFormat())}get isSpeakerDiarizationEnabled(){return this.privEnableSpeakerId}get audioSource(){return this.privAudioSource}get speechContext(){return this.privSpeechContext}get dynamicGrammar(){return this.privDynamicGrammar}get agentConfig(){return this.privAgentConfig}set conversationTranslatorToken(t){this.privRecognizerConfig.parameters.setProperty(q.ConversationTranslator_Token,t)}set voiceProfileType(t){this.privRecognizerConfig.parameters.setProperty(q.SpeechServiceConnection_SpeakerIdMode,t)}set authentication(t){this.privAuthentication=t}isDisposed(){return this.privIsDisposed}dispose(t){return Kt(this,void 0,void 0,function*(){if(this.privIsDisposed=!0,this.privConnectionConfigurationPromise!==void 0)try{yield(yield this.privConnectionConfigurationPromise).dispose(t)}catch{return}})}get connectionEvents(){return this.privConnectionEvents}get serviceEvents(){return this.privServiceEvents}get recognitionMode(){return this.privRecognizerConfig.recognitionMode}recognize(t,n,r){return Kt(this,void 0,void 0,function*(){if(this.recognizeOverride!==void 0){yield this.recognizeOverride(t,n,r);return}this.privConnectionConfigurationPromise=void 0,this.privRecognizerConfig.recognitionMode=t,this.setSpeechSegmentationTimeoutJson(),this.setTranslationJson(),this.privSuccessCallback=n,this.privErrorCallback=r,this.privRequestSession.startNewRecognition(),this.privRequestSession.listenForServiceTelemetry(this.privAudioSource.events);const i=this.connectImpl();let o;try{const l=yield this.audioSource.attach(this.privRequestSession.audioNodeId),u=yield this.audioSource.format,c=yield this.audioSource.deviceInfo;this.privIsLiveAudio=c.type&&c.type===hs.Microphones,o=new JP(l,u.avgBytesPerSec),yield this.privRequestSession.onAudioSourceAttachCompleted(o,!1),this.privRecognizerConfig.SpeechServiceConfig.Context.audio={source:c}}catch(l){throw yield this.privRequestSession.onStopRecognizing(),l}try{yield i}catch(l){yield this.cancelRecognitionLocal(Ni.Error,Wt.ConnectionFailure,l);return}const a=new Tg(this.privRequestSession.sessionId);this.privRecognizer.sessionStarted&&this.privRecognizer.sessionStarted(this.privRecognizer,a),this.receiveMessage(),this.sendAudio(o).catch(l=>Kt(this,void 0,void 0,function*(){yield this.cancelRecognitionLocal(Ni.Error,Wt.RuntimeError,l)}))})}stopRecognizing(){return Kt(this,void 0,void 0,function*(){if(this.privRequestSession.isRecognizing)try{yield this.audioSource.turnOff(),yield this.sendFinalAudio(),yield this.privRequestSession.onStopRecognizing(),yield this.privRequestSession.turnCompletionPromise}finally{yield this.privRequestSession.dispose()}})}connect(){return Kt(this,void 0,void 0,function*(){return yield this.connectImpl(),Promise.resolve()})}connectAsync(t,n){this.connectImpl().then(()=>{try{t&&t()}catch(r){n&&n(r)}},r=>{try{n&&n(r)}catch{}})}disconnect(){return Kt(this,void 0,void 0,function*(){if(yield this.cancelRecognitionLocal(Ni.Error,Wt.NoError,"Disconnecting"),this.disconnectOverride!==void 0&&(yield this.disconnectOverride()),this.privConnectionPromise!==void 0)try{yield(yield this.privConnectionPromise).dispose()}catch{}this.privConnectionPromise=void 0})}sendMessage(t){}sendNetworkMessage(t,n){return Kt(this,void 0,void 0,function*(){const r=typeof n=="string"?ze.Text:ze.Binary,i=typeof n=="string"?"application/json":"";return(yield this.fetchConnection()).send(new Si(r,t,this.privRequestSession.requestId,i,n))})}set activityTemplate(t){this.privActivityTemplate=t}get activityTemplate(){return this.privActivityTemplate}sendTelemetryData(){return Kt(this,void 0,void 0,function*(){const t=this.privRequestSession.getTelemetry();if(oo.telemetryDataEnabled!==!0||this.privIsDisposed||t===null)return;if(oo.telemetryData)try{oo.telemetryData(t)}catch{}yield(yield this.fetchConnection()).send(new Si(ze.Text,"telemetry",this.privRequestSession.requestId,"application/json",t))})}cancelRecognitionLocal(t,n,r){return Kt(this,void 0,void 0,function*(){this.privRequestSession.isRecognizing&&(yield this.privRequestSession.onStopRecognizing(),this.cancelRecognition(this.privRequestSession.sessionId,this.privRequestSession.requestId,t,n,r))})}receiveMessage(){return Kt(this,void 0,void 0,function*(){try{if(this.privIsDisposed)return;let t=yield this.fetchConnection();const n=yield t.read();if(this.receiveMessageOverride!==void 0)return this.receiveMessageOverride();if(!n)return this.receiveMessage();this.privServiceHasSentMessage=!0;const r=Si.fromConnectionMessage(n);if(r.requestId.toLowerCase()===this.privRequestSession.requestId.toLowerCase())switch(r.path.toLowerCase()){case"turn.start":this.privMustReportEndOfStream=!0,this.privRequestSession.onServiceTurnStartResponse();break;case"speech.startdetected":const i=eh.fromJSON(r.textBody),o=new Jd(i.Offset,this.privRequestSession.sessionId);this.privRecognizer.speechStartDetected&&this.privRecognizer.speechStartDetected(this.privRecognizer,o);break;case"speech.enddetected":let a;r.textBody.length>0?a=r.textBody:a="{ Offset: 0 }";const s=eh.fromJSON(a),l=new Jd(s.Offset+this.privRequestSession.currentTurnAudioOffset,this.privRequestSession.sessionId);this.privRecognizer.speechEndDetected&&this.privRecognizer.speechEndDetected(this.privRecognizer,l);break;case"turn.end":yield this.sendTelemetryData(),this.privRequestSession.isSpeechEnded&&this.privMustReportEndOfStream&&(this.privMustReportEndOfStream=!1,yield this.cancelRecognitionLocal(Ni.EndOfStream,Wt.NoError,void 0));const u=new Tg(this.privRequestSession.sessionId);if(yield this.privRequestSession.onServiceTurnEndResponse(this.privRecognizerConfig.isContinuousRecognition),!this.privRecognizerConfig.isContinuousRecognition||this.privRequestSession.isSpeechEnded||!this.privRequestSession.isRecognizing){this.privRecognizer.sessionStopped&&this.privRecognizer.sessionStopped(this.privRecognizer,u);return}else t=yield this.fetchConnection(),yield this.sendPrePayloadJSON(t);break;default:(yield this.processTypeSpecificMessages(r))||this.privServiceEvents&&this.serviceEvents.onEvent(new UP(r.path.toLowerCase(),r.textBody))}return this.receiveMessage()}catch{return null}})}updateSpeakerDiarizationAudioOffset(){const n=this.privRequestSession.recognitionBytesSent/this.privAverageBytesPerMs;this.privSpeechContext.setSpeakerDiarizationAudioOffsetMs(n)}sendSpeechContext(t,n){this.privEnableSpeakerId&&this.updateSpeakerDiarizationAudioOffset();const r=this.speechContext.toJSON();if(n&&this.privRequestSession.onSpeechContext(),r)return t.send(new Si(ze.Text,"speech.context",this.privRequestSession.requestId,"application/json",r))}noOp(){}sendPrePayloadJSON(t,n=!0){return Kt(this,void 0,void 0,function*(){if(this.sendPrePayloadJSONOverride!==void 0)return this.sendPrePayloadJSONOverride(t);yield this.sendSpeechContext(t,n),yield this.sendWaveHeader(t)})}sendWaveHeader(t){return Kt(this,void 0,void 0,function*(){const n=yield this.audioSource.format;return t.send(new Si(ze.Binary,"audio",this.privRequestSession.requestId,"audio/x-wav",n.header))})}connectImpl(){return this.privConnectionPromise!==void 0?this.privConnectionPromise.then(t=>t.state()===Qt.Disconnected?(this.privConnectionId=null,this.privConnectionPromise=void 0,this.privServiceHasSentMessage=!1,this.connectImpl()):this.privConnectionPromise,()=>(this.privConnectionId=null,this.privConnectionPromise=void 0,this.privServiceHasSentMessage=!1,this.connectImpl())):(this.privConnectionPromise=this.retryableConnect(),this.privConnectionPromise.catch(()=>{}),this.postConnectImplOverride!==void 0?this.postConnectImplOverride(this.privConnectionPromise):this.privConnectionPromise)}sendSpeechServiceConfig(t,n,r){if(n.onSpeechContext(),oo.telemetryDataEnabled!==!0){const o={context:{system:JSON.parse(r).context.system}};r=JSON.stringify(o)}if(this.privRecognizerConfig.parameters.getProperty("f0f5debc-f8c9-4892-ac4b-90a7ab359fd2","false").toLowerCase()==="true"){const i=JSON.parse(r);i.context.DisableReferenceChannel="True",i.context.MicSpec="1_0_0",r=JSON.stringify(i)}if(r)return t.send(new Si(ze.Text,"speech.config",n.requestId,"application/json",r))}fetchConnection(){return Kt(this,void 0,void 0,function*(){return this.privConnectionConfigurationPromise!==void 0?this.privConnectionConfigurationPromise.then(t=>t.state()===Qt.Disconnected?(this.privConnectionId=null,this.privConnectionConfigurationPromise=void 0,this.privServiceHasSentMessage=!1,this.fetchConnection()):this.privConnectionConfigurationPromise,()=>(this.privConnectionId=null,this.privConnectionConfigurationPromise=void 0,this.privServiceHasSentMessage=!1,this.fetchConnection())):(this.privConnectionConfigurationPromise=this.configureConnection(),yield this.privConnectionConfigurationPromise)})}sendAudio(t){return Kt(this,void 0,void 0,function*(){const n=yield this.audioSource.format;this.privAverageBytesPerMs=n.avgBytesPerSec/1e3;let r=Date.now();const i=this.privRecognizerConfig.parameters.getProperty("SPEECH-TransmitLengthBeforThrottleMs","5000"),o=n.avgBytesPerSec/1e3*parseInt(i,10),a=this.privRequestSession.recogNumber,s=()=>Kt(this,void 0,void 0,function*(){if(!this.privIsDisposed&&!this.privRequestSession.isSpeechEnded&&this.privRequestSession.isRecognizing&&this.privRequestSession.recogNumber===a){const l=yield this.fetchConnection(),u=yield t.read();if(this.privRequestSession.isSpeechEnded)return;let c,f;if(!u||u.isEnd?(c=null,f=0):(c=u.buffer,this.privRequestSession.onAudioSent(c.byteLength),o>=this.privRequestSession.bytesSent?f=0:f=Math.max(0,r-Date.now())),f!==0&&(yield this.delay(f)),c!==null&&(r=Date.now()+c.byteLength*1e3/(n.avgBytesPerSec*2)),!this.privIsDisposed&&!this.privRequestSession.isSpeechEnded&&this.privRequestSession.isRecognizing&&this.privRequestSession.recogNumber===a)if(l.send(new Si(ze.Binary,"audio",this.privRequestSession.requestId,null,c)).catch(()=>{this.privRequestSession.onServiceTurnEndResponse(this.privRecognizerConfig.isContinuousRecognition).catch(()=>{})}),u!=null&&u.isEnd)this.privIsLiveAudio||this.privRequestSession.onSpeechEnded();else return s()}});return s()})}retryableConnect(){return Kt(this,void 0,void 0,function*(){let t=!1;this.privAuthFetchEventId=Pt();const n=this.privRequestSession.sessionId;this.privConnectionId=n!==void 0?n:Pt(),this.privRequestSession.onPreConnectionStart(this.privAuthFetchEventId,this.privConnectionId);let r=0,i="";for(;this.privRequestSession.numConnectionAttempts<=this.privRecognizerConfig.maxRetryCount;){const a=yield t?this.privAuthentication.fetchOnExpiry(this.privAuthFetchEventId):this.privAuthentication.fetch(this.privAuthFetchEventId);yield this.privRequestSession.onAuthCompleted(!1);const s=this.privConnectionFactory.create(this.privRecognizerConfig,a,this.privConnectionId);this.privRequestSession.listenForServiceTelemetry(s.events),s.events.attach(u=>{this.connectionEvents.onEvent(u)});const l=yield s.open();if(l.statusCode===200)return yield this.privRequestSession.onConnectionEstablishCompleted(l.statusCode),Promise.resolve(s);l.statusCode===1006&&(t=!0),r=l.statusCode,i=l.reason,this.privRequestSession.onRetryConnection()}return yield this.privRequestSession.onConnectionEstablishCompleted(r,i),Promise.reject(`Unable to contact server. StatusCode: ${r}, ${this.privRecognizerConfig.parameters.getProperty(q.SpeechServiceConnection_Endpoint)} Reason: ${i}`)})}delay(t){return new Promise(n=>this.privSetTimeout(n,t))}writeBufferToConsole(t){let n="Buffer Size: ";if(t===null)n+="null";else{const r=new Uint8Array(t);n+=`${t.byteLength}\r +`;for(let i=0;i0&&(i=this.parseHeaders(a[0]),a.length>1&&(o=a[1]))}n.resolve(new vg(t.messageType,o,i,t.id))}else if(t.messageType===ze.Binary){const r=t.binaryContent;let i={},o=null;if(!r||r.byteLength<2)throw new Error("Invalid binary message format. Header length missing.");const a=new DataView(r),s=a.getInt16(0);if(r.byteLengths+2&&(o=r.slice(2+s)),n.resolve(new vg(t.messageType,o,i,t.id))}}catch(r){n.reject(`Error formatting the message. Error: ${r}`)}return n.promise}fromConnectionMessage(t){const n=new fn;try{if(t.messageType===ze.Text){const r=`${this.makeHeaders(t)}${Uy}${t.textBody?t.textBody:""}`;n.resolve(new Zd(ze.Text,r,t.id))}else if(t.messageType===ze.Binary){const r=this.makeHeaders(t),i=t.binaryBody,o=this.stringToArrayBuffer(r),a=new Int8Array(o),s=a.byteLength,l=new Int8Array(2+s+(i?i.byteLength:0));if(l[0]=s>>8&255,l[1]=s&255,l.set(a,2),i){const c=new Int8Array(i);l.set(c,2+s)}const u=l.buffer;n.resolve(new Zd(ze.Binary,u,t.id))}}catch(r){n.reject(`Error formatting the message. ${r}`)}return n.promise}makeHeaders(t){let n="";if(t.headers)for(const r in t.headers)r&&(n+=`${r}: ${t.headers[r]}${Uy}`);return n}parseHeaders(t){const n={};if(t){const r=t.match(/[^\r\n]+/g);if(n){for(const i of r)if(i){const o=i.indexOf(":"),a=o>0?i.substr(0,o).trim().toLowerCase():i,s=o>0&&i.length>o+1?i.substr(o+1).trim():"";n[a]=s}}}return n}stringToArrayBuffer(t){const n=new ArrayBuffer(t.length),r=new DataView(n);for(let i=0;i1?i=`${s}${this.universalUri}${t.recognitionEndpointVersion}`:i=s+this.conversationRelativeUri;break;case ii.Dictation:i=s+this.dictationRelativeUri;break;default:t.recognitionEndpointVersion!==void 0&&parseInt(t.recognitionEndpointVersion,10)>1?i=`${s}${this.universalUri}${t.recognitionEndpointVersion}`:i=s+this.interactiveRelativeUri;break}const f={};n.token!==void 0&&n.token!==""&&(f[n.headerName]=n.token),f[ot.ConnectionId]=r;const d=t.parameters.getProperty("SPEECH-EnableWebsocketCompression","false")==="true",h=new ZP(i,l,f,new pO,_9.fromRecognizerConfig(t),d,r),v=h.uri;return t.parameters.setProperty(q.SpeechServiceConnection_Url,v),h}}class pf{static implTranslateRecognitionResult(t){let n=ei.Canceled;switch(t){case $e.Success:n=ei.RecognizedSpeech;break;case $e.NoMatch:case $e.InitialSilenceTimeout:case $e.BabbleTimeout:case $e.EndOfDictation:n=ei.NoMatch;break;case $e.Error:case $e.BadRequest:case $e.Forbidden:default:n=ei.Canceled;break}return n}static implTranslateCancelResult(t){let n=Ni.EndOfStream;switch(t){case $e.Success:case $e.EndOfDictation:case $e.NoMatch:n=Ni.EndOfStream;break;case $e.InitialSilenceTimeout:case $e.BabbleTimeout:case $e.Error:case $e.BadRequest:case $e.Forbidden:default:n=Ni.Error;break}return n}static implTranslateCancelErrorCode(t){let n=Wt.NoError;switch(t){case $e.Error:n=Wt.ServiceError;break;case $e.TooManyRequests:n=Wt.TooManyRequests;break;case $e.BadRequest:n=Wt.BadRequestParameters;break;case $e.Forbidden:n=Wt.Forbidden;break;default:n=Wt.NoError;break}return n}static implTranslateErrorDetails(t){let n="The speech service encountered an internal error and could not continue.";switch(t){case Wt.Forbidden:n="The recognizer is using a free subscription that ran out of quota.";break;case Wt.BadRequestParameters:n="Invalid parameter or unsupported audio format in the request.";break;case Wt.TooManyRequests:n="The number of parallel requests exceeded the number of allowed concurrent transcriptions.";break}return n}}var Ky;(function(e){e[e.Success=0]="Success",e[e.SynthesisEnd=1]="SynthesisEnd",e[e.Error=2]="Error"})(Ky||(Ky={}));var $e;(function(e){e[e.Success=0]="Success",e[e.NoMatch=1]="NoMatch",e[e.InitialSilenceTimeout=2]="InitialSilenceTimeout",e[e.BabbleTimeout=3]="BabbleTimeout",e[e.Error=4]="Error",e[e.EndOfDictation=5]="EndOfDictation",e[e.TooManyRequests=6]="TooManyRequests",e[e.BadRequest=7]="BadRequest",e[e.Forbidden=8]="Forbidden"})($e||($e={}));class eh{constructor(t){this.privSpeechStartDetected=JSON.parse(t)}static fromJSON(t){return new eh(t)}get Offset(){return this.privSpeechStartDetected.Offset}}class b9{constructor(t){this.privSpeechHypothesis=JSON.parse(t)}static fromJSON(t){return new b9(t)}get Text(){return this.privSpeechHypothesis.Text}get Offset(){return this.privSpeechHypothesis.Offset}get Duration(){return this.privSpeechHypothesis.Duration}get Language(){return this.privSpeechHypothesis.PrimaryLanguage===void 0?void 0:this.privSpeechHypothesis.PrimaryLanguage.Language}get LanguageDetectionConfidence(){return this.privSpeechHypothesis.PrimaryLanguage===void 0?void 0:this.privSpeechHypothesis.PrimaryLanguage.Confidence}get SpeakerId(){return this.privSpeechHypothesis.SpeakerId}}var gO=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class vO extends oo{constructor(t,n,r,i,o){super(t,n,r,i,o),this.privSpeechRecognizer=o}processTypeSpecificMessages(t){return gO(this,void 0,void 0,function*(){let n;const r=new W1;r.setProperty(q.SpeechServiceResponse_JsonResult,t.textBody);let i=!1;switch(t.path.toLowerCase()){case"speech.hypothesis":case"speech.fragment":const o=b9.fromJSON(t.textBody),a=o.Offset+this.privRequestSession.currentTurnAudioOffset;n=new hf(this.privRequestSession.requestId,ei.RecognizingSpeech,o.Text,o.Duration,a,o.Language,o.LanguageDetectionConfidence,void 0,void 0,t.textBody,r),this.privRequestSession.onHypothesis(a);const s=new Py(n,o.Duration,this.privRequestSession.sessionId);if(this.privSpeechRecognizer.recognizing)try{this.privSpeechRecognizer.recognizing(this.privSpeechRecognizer,s)}catch{}i=!0;break;case"speech.phrase":const l=w9.fromJSON(t.textBody),u=pf.implTranslateRecognitionResult(l.RecognitionStatus);if(this.privRequestSession.onPhraseRecognized(this.privRequestSession.currentTurnAudioOffset+l.Offset+l.Duration),ei.Canceled===u){const c=pf.implTranslateCancelResult(l.RecognitionStatus),f=pf.implTranslateCancelErrorCode(l.RecognitionStatus);yield this.cancelRecognitionLocal(c,f,pf.implTranslateErrorDetails(f))}else{if(!(this.privRequestSession.isSpeechEnded&&u===ei.NoMatch&&l.RecognitionStatus!==$e.InitialSilenceTimeout)){if(this.privRecognizerConfig.parameters.getProperty(Hl)===Dt[Dt.Simple])n=new hf(this.privRequestSession.requestId,u,l.DisplayText,l.Duration,l.Offset+this.privRequestSession.currentTurnAudioOffset,l.Language,l.LanguageDetectionConfidence,void 0,void 0,t.textBody,r);else{const f=A9.fromJSON(t.textBody),d=f.Offset+this.privRequestSession.currentTurnAudioOffset,h=f.getJsonWithCorrectedOffsets(d);n=new hf(this.privRequestSession.requestId,u,f.RecognitionStatus===$e.Success?f.NBest[0].Display:void 0,f.Duration,d,f.Language,f.LanguageDetectionConfidence,void 0,void 0,h,r)}const c=new Py(n,n.offset,this.privRequestSession.sessionId);if(this.privSpeechRecognizer.recognized)try{this.privSpeechRecognizer.recognized(this.privSpeechRecognizer,c)}catch{}}if(this.privSuccessCallback){try{this.privSuccessCallback(n)}catch(c){this.privErrorCallback&&this.privErrorCallback(c)}this.privSuccessCallback=void 0,this.privErrorCallback=void 0}}i=!0;break}return i})}cancelRecognition(t,n,r,i,o){const a=new W1;if(a.setProperty(AO,Wt[i]),this.privSpeechRecognizer.canceled){const s=new lO(r,o,i,void 0,t);try{this.privSpeechRecognizer.canceled(this.privSpeechRecognizer,s)}catch{}}if(this.privSuccessCallback){const s=new hf(n,ei.Canceled,void 0,void 0,void 0,void 0,void 0,void 0,o,void 0,a);try{this.privSuccessCallback(s),this.privSuccessCallback=void 0}catch{}}}}class A9{constructor(t){this.privDetailedSpeechPhrase=JSON.parse(t),this.privDetailedSpeechPhrase.RecognitionStatus=$e[this.privDetailedSpeechPhrase.RecognitionStatus]}static fromJSON(t){return new A9(t)}getJsonWithCorrectedOffsets(t){if(this.privDetailedSpeechPhrase.NBest){let n;for(const r of this.privDetailedSpeechPhrase.NBest)if(r.Words&&r.Words[0]){n=r.Words[0].Offset;break}if(n&&n0&&this.privPhraseLatencies.push(Date.now()-t)}hypothesisReceived(t){t>0&&this.privHypothesisLatencies.push(Date.now()-t)}onEvent(t){if(!this.privIsDisposed&&(t instanceof c6&&t.requestId===this.privRequestId&&(this.privListeningTriggerMetric={End:t.eventTime,Name:"ListeningTrigger",Start:t.eventTime}),t instanceof dc&&t.audioSourceId===this.privAudioSourceId&&t.audioNodeId===this.privAudioNodeId&&(this.privMicStartTime=t.eventTime),t instanceof hc&&t.audioSourceId===this.privAudioSourceId&&t.audioNodeId===this.privAudioNodeId&&(this.privMicStartTime=t.eventTime),t instanceof Qd&&t.audioSourceId===this.privAudioSourceId&&(this.privMicMetric||(this.privMicMetric={End:t.eventTime,Error:t.error,Name:"Microphone",Start:this.privMicStartTime})),t instanceof g9&&t.audioSourceId===this.privAudioSourceId&&t.audioNodeId===this.privAudioNodeId&&(this.privMicMetric||(this.privMicMetric={End:t.eventTime,Error:t.error,Name:"Microphone",Start:this.privMicStartTime})),t instanceof mo&&t.audioSourceId===this.privAudioSourceId&&t.audioNodeId===this.privAudioNodeId&&(this.privMicMetric||(this.privMicMetric={End:t.eventTime,Name:"Microphone",Start:this.privMicStartTime})),t instanceof f6&&t.requestId===this.privRequestId&&(this.privConnectionId=t.sessionId),t instanceof J_&&t.connectionId===this.privConnectionId&&(this.privConnectionStartTime=t.eventTime),t instanceof e6&&t.connectionId===this.privConnectionId&&(this.privConnectionEstablishMetric||(this.privConnectionEstablishMetric={End:t.eventTime,Id:this.privConnectionId,Name:"Connection",Start:this.privConnectionStartTime})),t instanceof jP&&t.connectionId===this.privConnectionId&&(this.privConnectionEstablishMetric||(this.privConnectionEstablishMetric={End:t.eventTime,Error:this.getConnectionError(t.statusCode),Id:this.privConnectionId,Name:"Connection",Start:this.privConnectionStartTime})),t instanceof gg&&t.connectionId===this.privConnectionId&&t.message&&t.message.headers&&t.message.headers.path)){this.privReceivedMessages[t.message.headers.path]||(this.privReceivedMessages[t.message.headers.path]=new Array);const n=50;this.privReceivedMessages[t.message.headers.path].length0&&t.push({PhraseLatencyMs:this.privPhraseLatencies}),this.privHypothesisLatencies.length>0&&t.push({FirstHypothesisLatencyMs:this.privHypothesisLatencies});const n={Metrics:t,ReceivedMessages:this.privReceivedMessages},r=JSON.stringify(n);return this.privReceivedMessages={},this.privListeningTriggerMetric=null,this.privMicMetric=null,this.privConnectionEstablishMetric=null,this.privPhraseLatencies=[],this.privHypothesisLatencies=[],r}get hasTelemetry(){return Object.keys(this.privReceivedMessages).length!==0||this.privListeningTriggerMetric!==null||this.privMicMetric!==null||this.privConnectionEstablishMetric!==null||this.privPhraseLatencies.length!==0||this.privHypothesisLatencies.length!==0}dispose(){this.privIsDisposed=!0}getConnectionError(t){switch(t){case 400:case 1002:case 1003:case 1005:case 1007:case 1008:case 1009:return"BadRequest";case 401:return"Unauthorized";case 403:return"Forbidden";case 503:case 1001:return"ServerUnavailable";case 500:case 1011:return"ServerError";case 408:case 504:return"Timeout";default:return"statuscode:"+t.toString()}}}var Mo=globalThis&&globalThis.__awaiter||function(e,t,n,r){function i(o){return o instanceof n?o:new n(function(a){a(o)})}return new(n||(n=Promise))(function(o,a){function s(c){try{u(r.next(c))}catch(f){a(f)}}function l(c){try{u(r.throw(c))}catch(f){a(f)}}function u(c){c.done?o(c.value):i(c.value).then(s,l)}u((r=r.apply(e,t||[])).next())})};class yO{constructor(t){this.privIsDisposed=!1,this.privDetachables=new Array,this.privIsAudioNodeDetached=!1,this.privIsRecognizing=!1,this.privIsSpeechEnded=!1,this.privTurnStartAudioOffset=0,this.privLastRecoOffset=0,this.privHypothesisReceived=!1,this.privBytesSent=0,this.privRecognitionBytesSent=0,this.privRecogNumber=0,this.privInTurn=!1,this.privConnectionAttempts=0,this.privAudioSourceId=t,this.privRequestId=Pt(),this.privAudioNodeId=Pt(),this.privTurnDeferral=new fn,this.privTurnDeferral.resolve()}get sessionId(){return this.privSessionId}get requestId(){return this.privRequestId}get audioNodeId(){return this.privAudioNodeId}get turnCompletionPromise(){return this.privTurnDeferral.promise}get isSpeechEnded(){return this.privIsSpeechEnded}get isRecognizing(){return this.privIsRecognizing}get currentTurnAudioOffset(){return this.privTurnStartAudioOffset}get recogNumber(){return this.privRecogNumber}get numConnectionAttempts(){return this.privConnectionAttempts}get bytesSent(){return this.privBytesSent}get recognitionBytesSent(){return this.privRecognitionBytesSent}listenForServiceTelemetry(t){this.privServiceTelemetryListener&&this.privDetachables.push(t.attachListener(this.privServiceTelemetryListener))}startNewRecognition(){this.privRecognitionBytesSent=0,this.privIsSpeechEnded=!1,this.privIsRecognizing=!0,this.privTurnStartAudioOffset=0,this.privLastRecoOffset=0,this.privRecogNumber++,this.privServiceTelemetryListener=new EO(this.privRequestId,this.privAudioSourceId,this.privAudioNodeId),this.onEvent(new c6(this.requestId,this.privSessionId,this.privAudioSourceId,this.privAudioNodeId))}onAudioSourceAttachCompleted(t,n){return Mo(this,void 0,void 0,function*(){this.privAudioNode=t,this.privIsAudioNodeDetached=!1,n?yield this.onComplete():this.onEvent(new fO(this.privRequestId,this.privSessionId,this.privAudioSourceId,this.privAudioNodeId))})}onPreConnectionStart(t,n){this.privAuthFetchEventId=t,this.privSessionId=n,this.onEvent(new f6(this.privRequestId,this.privAuthFetchEventId,this.privSessionId))}onAuthCompleted(t){return Mo(this,void 0,void 0,function*(){t&&(yield this.onComplete())})}onConnectionEstablishCompleted(t,n){return Mo(this,void 0,void 0,function*(){if(t===200){this.onEvent(new dO(this.requestId,this.privAudioSourceId,this.privAudioNodeId,this.privAuthFetchEventId,this.privSessionId)),this.privAudioNode&&this.privAudioNode.replay(),this.privTurnStartAudioOffset=this.privLastRecoOffset,this.privBytesSent=0;return}else t===403&&(yield this.onComplete())})}onServiceTurnEndResponse(t){return Mo(this,void 0,void 0,function*(){this.privTurnDeferral.resolve(),!t||this.isSpeechEnded?(yield this.onComplete(),this.privInTurn=!1):(this.privTurnStartAudioOffset=this.privLastRecoOffset,this.privAudioNode.replay())})}onSpeechContext(){this.privRequestId=Pt()}onServiceTurnStartResponse(){this.privTurnDeferral&&this.privInTurn&&(this.privTurnDeferral.reject("Another turn started before current completed."),this.privTurnDeferral.promise.then().catch(()=>{})),this.privInTurn=!0,this.privTurnDeferral=new fn}onHypothesis(t){this.privHypothesisReceived||(this.privHypothesisReceived=!0,this.privServiceTelemetryListener.hypothesisReceived(this.privAudioNode.findTimeAtOffset(t)))}onPhraseRecognized(t){this.privServiceTelemetryListener.phraseReceived(this.privAudioNode.findTimeAtOffset(t)),this.onServiceRecognized(t)}onServiceRecognized(t){this.privLastRecoOffset=t,this.privHypothesisReceived=!1,this.privAudioNode.shrinkBuffers(t),this.privConnectionAttempts=0}onAudioSent(t){this.privBytesSent+=t,this.privRecognitionBytesSent+=t}onRetryConnection(){this.privConnectionAttempts++}dispose(){return Mo(this,void 0,void 0,function*(){if(!this.privIsDisposed){this.privIsDisposed=!0;for(const t of this.privDetachables)yield t.detach();this.privServiceTelemetryListener&&this.privServiceTelemetryListener.dispose(),this.privIsRecognizing=!1}})}getTelemetry(){return this.privServiceTelemetryListener.hasTelemetry?this.privServiceTelemetryListener.getTelemetry():null}onStopRecognizing(){return Mo(this,void 0,void 0,function*(){yield this.onComplete()})}onSpeechEnded(){this.privIsSpeechEnded=!0}onEvent(t){this.privServiceTelemetryListener&&this.privServiceTelemetryListener.onEvent(t),In.instance.onEvent(t)}onComplete(){return Mo(this,void 0,void 0,function*(){this.privIsRecognizing&&(this.privIsRecognizing=!1,yield this.detachAudioNode())})}detachAudioNode(){return Mo(this,void 0,void 0,function*(){this.privIsAudioNodeDetached||(this.privIsAudioNodeDetached=!0,this.privAudioNode&&(yield this.privAudioNode.detach()))})}}class TO{constructor(t){this.privContext={},this.privDynamicGrammar=t}getSection(t){return this.privContext[t]||{}}setSection(t,n){this.privContext[t]=n}setPronunciationAssessmentParams(t,n,r=!1){this.privContext.phraseDetection===void 0&&(this.privContext.phraseDetection={enrichment:{pronunciationAssessment:{}}}),this.privContext.phraseDetection.enrichment===void 0&&(this.privContext.phraseDetection.enrichment={pronunciationAssessment:{}}),this.privContext.phraseDetection.enrichment.pronunciationAssessment=JSON.parse(t),r&&(this.privContext.phraseDetection.mode="Conversation"),this.setWordLevelTimings(),this.privContext.phraseOutput.detailed.options.push("PronunciationAssessment"),this.privContext.phraseOutput.detailed.options.indexOf("SNR")===-1&&this.privContext.phraseOutput.detailed.options.push("SNR"),n&&(this.privContext.phraseDetection.enrichment.contentAssessment={topic:n},this.privContext.phraseOutput.detailed.options.push("ContentAssessment"))}setDetailedOutputFormat(){this.privContext.phraseOutput===void 0&&(this.privContext.phraseOutput={detailed:{options:[]},format:{}}),this.privContext.phraseOutput.detailed===void 0&&(this.privContext.phraseOutput.detailed={options:[]}),this.privContext.phraseOutput.format="Detailed"}setWordLevelTimings(){this.privContext.phraseOutput===void 0&&(this.privContext.phraseOutput={detailed:{options:[]},format:{}}),this.privContext.phraseOutput.detailed===void 0&&(this.privContext.phraseOutput.detailed={options:[]}),this.privContext.phraseOutput.format="Detailed",this.privContext.phraseOutput.detailed.options.indexOf("WordTimings")===-1&&this.privContext.phraseOutput.detailed.options.push("WordTimings")}setSpeakerDiarizationAudioOffsetMs(t){this.privContext.phraseDetection.speakerDiarization.audioOffsetMs=t}toJSON(){const t=this.privDynamicGrammar.generateGrammarObject();return this.setSection("dgi",t),JSON.stringify(this.privContext)}}class CO{addPhrase(t){this.privPhrases||(this.privPhrases=[]),t instanceof Array?this.privPhrases=this.privPhrases.concat(t):this.privPhrases.push(t)}clearPhrases(){this.privPhrases=void 0}addReferenceGrammar(t){this.privGrammars||(this.privGrammars=[]),t instanceof Array?this.privGrammars=this.privGrammars.concat(t):this.privGrammars.push(t)}clearGrammars(){this.privGrammars=void 0}generateGrammarObject(){if(this.privGrammars===void 0&&this.privPhrases===void 0)return;const t={};if(t.ReferenceGrammars=this.privGrammars,this.privPhrases!==void 0&&this.privPhrases.length!==0){const n=[];this.privPhrases.forEach(r=>{n.push({Text:r})}),t.Groups=[{Type:"Generic",Items:n}]}return t}}class SO{toJsonString(){return JSON.stringify(this.iPrivConfig)}get(){return this.iPrivConfig}set(t){this.iPrivConfig=t}}class d6{constructor(t){this.context=t}serialize(){return JSON.stringify(this,(t,n)=>{if(n&&typeof n=="object"&&!Array.isArray(n)){const r={};for(const i in n)Object.hasOwnProperty.call(n,i)&&(r[i&&i.charAt(0).toLowerCase()+i.substring(1)]=n[i]);return r}return n})}get Context(){return this.context}get Recognition(){return this.recognition}set Recognition(t){this.recognition=t.toLowerCase()}}class h6{constructor(t){this.system=new _O,this.os=t}}class _O{constructor(){const t="1.33.1";this.name="SpeechSDK",this.version=t,this.build="JavaScript",this.lang="JavaScript"}}class bO{constructor(t,n,r){this.platform=t,this.name=n,this.version=r}}var Bl;(function(e){e.Bluetooth="Bluetooth",e.Wired="Wired",e.WiFi="WiFi",e.Cellular="Cellular",e.InBuilt="InBuilt",e.Unknown="Unknown"})(Bl||(Bl={}));var hs;(function(e){e.Phone="Phone",e.Speaker="Speaker",e.Car="Car",e.Headset="Headset",e.Thermostat="Thermostat",e.Microphones="Microphones",e.Deskphone="Deskphone",e.RemoteControl="RemoteControl",e.Unknown="Unknown",e.File="File",e.Stream="Stream"})(hs||(hs={}));const Hl="OutputFormat",AO="CancellationErrorCode",Sg="ServiceProperties",p6="ForceDictation",Wy=["http","https","mailto","tel"];function wO(e){const t=(e||"").trim(),n=t.charAt(0);if(n==="#"||n==="/")return t;const r=t.indexOf(":");if(r===-1)return t;let i=-1;for(;++ii||(i=t.indexOf("#"),i!==-1&&r>i)?t:"javascript:void(0)"}/*! + * Determine if an object is a Buffer + * + * @author Feross Aboukhadijeh + * @license MIT + */var kO=function(t){return t!=null&&t.constructor!=null&&typeof t.constructor.isBuffer=="function"&&t.constructor.isBuffer(t)};const m6=Ea(kO);function t1(e){return!e||typeof e!="object"?"":"position"in e||"type"in e?jy(e.position):"start"in e||"end"in e?jy(e):"line"in e||"column"in e?_g(e):""}function _g(e){return Gy(e&&e.line)+":"+Gy(e&&e.column)}function jy(e){return _g(e&&e.start)+"-"+_g(e&&e.end)}function Gy(e){return e&&typeof e=="number"?e:1}class Lr extends Error{constructor(t,n,r){const i=[null,null];let o={start:{line:null,column:null},end:{line:null,column:null}};if(super(),typeof n=="string"&&(r=n,n=void 0),typeof r=="string"){const a=r.indexOf(":");a===-1?i[1]=r:(i[0]=r.slice(0,a),i[1]=r.slice(a+1))}n&&("type"in n||"position"in n?n.position&&(o=n.position):"start"in n||"end"in n?o=n:("line"in n||"column"in n)&&(o.start=n)),this.name=t1(n)||"1:1",this.message=typeof t=="object"?t.message:t,this.stack="",typeof t=="object"&&t.stack&&(this.stack=t.stack),this.reason=this.message,this.fatal,this.line=o.start.line,this.column=o.start.column,this.position=o,this.source=i[0],this.ruleId=i[1],this.file,this.actual,this.expected,this.url,this.note}}Lr.prototype.file="";Lr.prototype.name="";Lr.prototype.reason="";Lr.prototype.message="";Lr.prototype.stack="";Lr.prototype.fatal=null;Lr.prototype.column=null;Lr.prototype.line=null;Lr.prototype.source=null;Lr.prototype.ruleId=null;Lr.prototype.position=null;const _i={basename:IO,dirname:xO,extname:NO,join:FO,sep:"/"};function IO(e,t){if(t!==void 0&&typeof t!="string")throw new TypeError('"ext" argument must be a string');pc(e);let n=0,r=-1,i=e.length,o;if(t===void 0||t.length===0||t.length>e.length){for(;i--;)if(e.charCodeAt(i)===47){if(o){n=i+1;break}}else r<0&&(o=!0,r=i+1);return r<0?"":e.slice(n,r)}if(t===e)return"";let a=-1,s=t.length-1;for(;i--;)if(e.charCodeAt(i)===47){if(o){n=i+1;break}}else a<0&&(o=!0,a=i+1),s>-1&&(e.charCodeAt(i)===t.charCodeAt(s--)?s<0&&(r=i):(s=-1,r=a));return n===r?r=a:r<0&&(r=e.length),e.slice(n,r)}function xO(e){if(pc(e),e.length===0)return".";let t=-1,n=e.length,r;for(;--n;)if(e.charCodeAt(n)===47){if(r){t=n;break}}else r||(r=!0);return t<0?e.charCodeAt(0)===47?"/":".":t===1&&e.charCodeAt(0)===47?"//":e.slice(0,t)}function NO(e){pc(e);let t=e.length,n=-1,r=0,i=-1,o=0,a;for(;t--;){const s=e.charCodeAt(t);if(s===47){if(a){r=t+1;break}continue}n<0&&(a=!0,n=t+1),s===46?i<0?i=t:o!==1&&(o=1):i>-1&&(o=-1)}return i<0||n<0||o===0||o===1&&i===n-1&&i===r+1?"":e.slice(i,n)}function FO(...e){let t=-1,n;for(;++t0&&e.charCodeAt(e.length-1)===47&&(n+="/"),t?"/"+n:n}function DO(e,t){let n="",r=0,i=-1,o=0,a=-1,s,l;for(;++a<=e.length;){if(a2){if(l=n.lastIndexOf("/"),l!==n.length-1){l<0?(n="",r=0):(n=n.slice(0,l),r=n.length-1-n.lastIndexOf("/")),i=a,o=0;continue}}else if(n.length>0){n="",r=0,i=a,o=0;continue}}t&&(n=n.length>0?n+"/..":"..",r=2)}else n.length>0?n+="/"+e.slice(i+1,a):n=e.slice(i+1,a),r=a-i-1;i=a,o=0}else s===46&&o>-1?o++:o=-1}return n}function pc(e){if(typeof e!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(e))}const MO={cwd:PO};function PO(){return"/"}function bg(e){return e!==null&&typeof e=="object"&&e.href&&e.origin}function OO(e){if(typeof e=="string")e=new URL(e);else if(!bg(e)){const t=new TypeError('The "path" argument must be of type string or an instance of URL. Received `'+e+"`");throw t.code="ERR_INVALID_ARG_TYPE",t}if(e.protocol!=="file:"){const t=new TypeError("The URL must be of scheme file");throw t.code="ERR_INVALID_URL_SCHEME",t}return LO(e)}function LO(e){if(e.hostname!==""){const r=new TypeError('File URL host must be "localhost" or empty on darwin');throw r.code="ERR_INVALID_FILE_URL_HOST",r}const t=e.pathname;let n=-1;for(;++n"u"||od.call(t,i)},Zy=function(t,n){qy&&n.name==="__proto__"?qy(t,n.name,{enumerable:!0,configurable:!0,value:n.newValue,writable:!0}):t[n.name]=n.newValue},Jy=function(t,n){if(n==="__proto__")if(od.call(t,n)){if(Yy)return Yy(t,n).value}else return;return t[n]},HO=function e(){var t,n,r,i,o,a,s=arguments[0],l=1,u=arguments.length,c=!1;for(typeof s=="boolean"&&(c=s,s=arguments[1]||{},l=2),(s==null||typeof s!="object"&&typeof s!="function")&&(s={});la.length;let l;s&&a.push(i);try{l=e.apply(this,a)}catch(u){const c=u;if(s&&n)throw c;return i(c)}s||(l instanceof Promise?l.then(o,i):l instanceof Error?i(l):o(l))}function i(a,...s){n||(n=!0,t(a,...s))}function o(a){i(null,a)}}const KO=y6().freeze(),E6={}.hasOwnProperty;function y6(){const e=zO(),t=[];let n={},r,i=-1;return o.data=a,o.Parser=void 0,o.Compiler=void 0,o.freeze=s,o.attachers=t,o.use=l,o.parse=u,o.stringify=c,o.run=f,o.runSync=d,o.process=h,o.processSync=v,o;function o(){const g=y6();let C=-1;for(;++C{if(b||!A||!F)w(b);else{const D=o.stringify(A,F);D==null||(GO(D)?F.value=D:F.result=D),w(b,F)}});function w(b,A){b||!A?T(b):y?y(A):C(null,A)}}}function v(g){let C;o.freeze(),Sm("processSync",o.Parser),_m("processSync",o.Compiler);const m=_u(g);return o.process(m,y),r2("processSync","process",C),m;function y(T){C=!0,Vy(T)}}}function t2(e,t){return typeof e=="function"&&e.prototype&&(WO(e.prototype)||t in e.prototype)}function WO(e){let t;for(t in e)if(E6.call(e,t))return!0;return!1}function Sm(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+e+"` without `Parser`")}function _m(e,t){if(typeof t!="function")throw new TypeError("Cannot `"+e+"` without `Compiler`")}function bm(e,t){if(t)throw new Error("Cannot call `"+e+"` on a frozen processor.\nCreate a new processor first, by calling it: use `processor()` instead of `processor`.")}function n2(e){if(!Ag(e)||typeof e.type!="string")throw new TypeError("Expected node, got `"+e+"`")}function r2(e,t,n){if(!n)throw new Error("`"+e+"` finished async. Use `"+t+"` instead")}function _u(e){return jO(e)?e:new g6(e)}function jO(e){return!!(e&&typeof e=="object"&&"message"in e&&"messages"in e)}function GO(e){return typeof e=="string"||m6(e)}const $O={};function VO(e,t){const n=t||$O,r=typeof n.includeImageAlt=="boolean"?n.includeImageAlt:!0,i=typeof n.includeHtml=="boolean"?n.includeHtml:!0;return T6(e,r,i)}function T6(e,t,n){if(qO(e)){if("value"in e)return e.type==="html"&&!n?"":e.value;if(t&&"alt"in e&&e.alt)return e.alt;if("children"in e)return i2(e.children,t,n)}return Array.isArray(e)?i2(e,t,n):""}function i2(e,t,n){const r=[];let i=-1;for(;++ii?0:i+t:t=t>i?i:t,n=n>0?n:0,r.length<1e4)a=Array.from(r),a.unshift(t,n),e.splice(...a);else for(n&&e.splice(t,n);o0?(gr(e,e.length,0,t),e):t}const o2={}.hasOwnProperty;function C6(e){const t={};let n=-1;for(;++na))return;const A=t.events.length;let F=A,D,I;for(;F--;)if(t.events[F][0]==="exit"&&t.events[F][1].type==="chunkFlow"){if(D){I=t.events[F][1].end;break}D=!0}for(m(r),b=A;bT;){const w=n[S];t.containerState=w[1],w[0].exit.call(t,e)}n.length=T}function y(){i.write([null]),o=void 0,i=void 0,t.containerState._closeFlow=void 0}}function oL(e,t,n){return Ie(e,e.attempt(this.parser.constructs.document,t,n),"linePrefix",this.parser.constructs.disable.null.includes("codeIndented")?void 0:4)}function nh(e){if(e===null||We(e)||ps(e))return 1;if(Uh(e))return 2}function Kh(e,t,n){const r=[];let i=-1;for(;++i1&&e[n][1].end.offset-e[n][1].start.offset>1?2:1;const f=Object.assign({},e[r][1].end),d=Object.assign({},e[n][1].start);s2(f,-l),s2(d,l),a={type:l>1?"strongSequence":"emphasisSequence",start:f,end:Object.assign({},e[r][1].end)},s={type:l>1?"strongSequence":"emphasisSequence",start:Object.assign({},e[n][1].start),end:d},o={type:l>1?"strongText":"emphasisText",start:Object.assign({},e[r][1].end),end:Object.assign({},e[n][1].start)},i={type:l>1?"strong":"emphasis",start:Object.assign({},a.start),end:Object.assign({},s.end)},e[r][1].end=Object.assign({},a.start),e[n][1].start=Object.assign({},s.end),u=[],e[r][1].end.offset-e[r][1].start.offset&&(u=kr(u,[["enter",e[r][1],t],["exit",e[r][1],t]])),u=kr(u,[["enter",i,t],["enter",a,t],["exit",a,t],["enter",o,t]]),u=kr(u,Kh(t.parser.constructs.insideSpan.null,e.slice(r+1,n),t)),u=kr(u,[["exit",o,t],["enter",s,t],["exit",s,t],["exit",i,t]]),e[n][1].end.offset-e[n][1].start.offset?(c=2,u=kr(u,[["enter",e[n][1],t],["exit",e[n][1],t]])):c=0,gr(e,r-1,n-r+3,u),n=r+u.length-c-2;break}}for(n=-1;++n0&&be(b)?Ie(e,y,"linePrefix",o+1)(b):y(b)}function y(b){return b===null||fe(b)?e.check(u2,g,S)(b):(e.enter("codeFlowValue"),T(b))}function T(b){return b===null||fe(b)?(e.exit("codeFlowValue"),y(b)):(e.consume(b),T)}function S(b){return e.exit("codeFenced"),t(b)}function w(b,A,F){let D=0;return I;function I(P){return b.enter("lineEnding"),b.consume(P),b.exit("lineEnding"),B}function B(P){return b.enter("codeFencedFence"),be(P)?Ie(b,M,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(P):M(P)}function M(P){return P===s?(b.enter("codeFencedFenceSequence"),oe(P)):F(P)}function oe(P){return P===s?(D++,b.consume(P),oe):D>=a?(b.exit("codeFencedFenceSequence"),be(P)?Ie(b,V,"whitespace")(P):V(P)):F(P)}function V(P){return P===null||fe(P)?(b.exit("codeFencedFence"),A(P)):F(P)}}}function vL(e,t,n){const r=this;return i;function i(a){return a===null?n(a):(e.enter("lineEnding"),e.consume(a),e.exit("lineEnding"),o)}function o(a){return r.parser.lazy[r.now().line]?n(a):t(a)}}const Am={name:"codeIndented",tokenize:yL},EL={tokenize:TL,partial:!0};function yL(e,t,n){const r=this;return i;function i(u){return e.enter("codeIndented"),Ie(e,o,"linePrefix",4+1)(u)}function o(u){const c=r.events[r.events.length-1];return c&&c[1].type==="linePrefix"&&c[2].sliceSerialize(c[1],!0).length>=4?a(u):n(u)}function a(u){return u===null?l(u):fe(u)?e.attempt(EL,a,l)(u):(e.enter("codeFlowValue"),s(u))}function s(u){return u===null||fe(u)?(e.exit("codeFlowValue"),a(u)):(e.consume(u),s)}function l(u){return e.exit("codeIndented"),t(u)}}function TL(e,t,n){const r=this;return i;function i(a){return r.parser.lazy[r.now().line]?n(a):fe(a)?(e.enter("lineEnding"),e.consume(a),e.exit("lineEnding"),i):Ie(e,o,"linePrefix",4+1)(a)}function o(a){const s=r.events[r.events.length-1];return s&&s[1].type==="linePrefix"&&s[2].sliceSerialize(s[1],!0).length>=4?t(a):fe(a)?i(a):n(a)}}const CL={name:"codeText",tokenize:bL,resolve:SL,previous:_L};function SL(e){let t=e.length-4,n=3,r,i;if((e[n][1].type==="lineEnding"||e[n][1].type==="space")&&(e[t][1].type==="lineEnding"||e[t][1].type==="space")){for(r=n;++r=4?t(a):e.interrupt(r.parser.constructs.flow,n,t)(a)}}function w6(e,t,n,r,i,o,a,s,l){const u=l||Number.POSITIVE_INFINITY;let c=0;return f;function f(m){return m===60?(e.enter(r),e.enter(i),e.enter(o),e.consume(m),e.exit(o),d):m===null||m===32||m===41||th(m)?n(m):(e.enter(r),e.enter(a),e.enter(s),e.enter("chunkString",{contentType:"string"}),g(m))}function d(m){return m===62?(e.enter(o),e.consume(m),e.exit(o),e.exit(i),e.exit(r),t):(e.enter(s),e.enter("chunkString",{contentType:"string"}),h(m))}function h(m){return m===62?(e.exit("chunkString"),e.exit(s),d(m)):m===null||m===60||fe(m)?n(m):(e.consume(m),m===92?v:h)}function v(m){return m===60||m===62||m===92?(e.consume(m),h):h(m)}function g(m){return!c&&(m===null||m===41||We(m))?(e.exit("chunkString"),e.exit(s),e.exit(a),e.exit(r),t(m)):c999||h===null||h===91||h===93&&!l||h===94&&!s&&"_hiddenFootnoteSupport"in a.parser.constructs?n(h):h===93?(e.exit(o),e.enter(i),e.consume(h),e.exit(i),e.exit(r),t):fe(h)?(e.enter("lineEnding"),e.consume(h),e.exit("lineEnding"),c):(e.enter("chunkString",{contentType:"string"}),f(h))}function f(h){return h===null||h===91||h===93||fe(h)||s++>999?(e.exit("chunkString"),c(h)):(e.consume(h),l||(l=!be(h)),h===92?d:f)}function d(h){return h===91||h===92||h===93?(e.consume(h),s++,f):f(h)}}function I6(e,t,n,r,i,o){let a;return s;function s(d){return d===34||d===39||d===40?(e.enter(r),e.enter(i),e.consume(d),e.exit(i),a=d===40?41:d,l):n(d)}function l(d){return d===a?(e.enter(i),e.consume(d),e.exit(i),e.exit(r),t):(e.enter(o),u(d))}function u(d){return d===a?(e.exit(o),l(a)):d===null?n(d):fe(d)?(e.enter("lineEnding"),e.consume(d),e.exit("lineEnding"),Ie(e,u,"linePrefix")):(e.enter("chunkString",{contentType:"string"}),c(d))}function c(d){return d===a||d===null||fe(d)?(e.exit("chunkString"),u(d)):(e.consume(d),d===92?f:c)}function f(d){return d===a||d===92?(e.consume(d),c):c(d)}}function n1(e,t){let n;return r;function r(i){return fe(i)?(e.enter("lineEnding"),e.consume(i),e.exit("lineEnding"),n=!0,r):be(i)?Ie(e,r,n?"linePrefix":"lineSuffix")(i):t(i)}}function oi(e){return e.replace(/[\t\n\r ]+/g," ").replace(/^ | $/g,"").toLowerCase().toUpperCase()}const FL={name:"definition",tokenize:DL},RL={tokenize:ML,partial:!0};function DL(e,t,n){const r=this;let i;return o;function o(h){return e.enter("definition"),a(h)}function a(h){return k6.call(r,e,s,n,"definitionLabel","definitionLabelMarker","definitionLabelString")(h)}function s(h){return i=oi(r.sliceSerialize(r.events[r.events.length-1][1]).slice(1,-1)),h===58?(e.enter("definitionMarker"),e.consume(h),e.exit("definitionMarker"),l):n(h)}function l(h){return We(h)?n1(e,u)(h):u(h)}function u(h){return w6(e,c,n,"definitionDestination","definitionDestinationLiteral","definitionDestinationLiteralMarker","definitionDestinationRaw","definitionDestinationString")(h)}function c(h){return e.attempt(RL,f,f)(h)}function f(h){return be(h)?Ie(e,d,"whitespace")(h):d(h)}function d(h){return h===null||fe(h)?(e.exit("definition"),r.parser.defined.push(i),t(h)):n(h)}}function ML(e,t,n){return r;function r(s){return We(s)?n1(e,i)(s):n(s)}function i(s){return I6(e,o,n,"definitionTitle","definitionTitleMarker","definitionTitleString")(s)}function o(s){return be(s)?Ie(e,a,"whitespace")(s):a(s)}function a(s){return s===null||fe(s)?t(s):n(s)}}const PL={name:"hardBreakEscape",tokenize:OL};function OL(e,t,n){return r;function r(o){return e.enter("hardBreakEscape"),e.consume(o),i}function i(o){return fe(o)?(e.exit("hardBreakEscape"),t(o)):n(o)}}const LL={name:"headingAtx",tokenize:HL,resolve:BL};function BL(e,t){let n=e.length-2,r=3,i,o;return e[r][1].type==="whitespace"&&(r+=2),n-2>r&&e[n][1].type==="whitespace"&&(n-=2),e[n][1].type==="atxHeadingSequence"&&(r===n-1||n-4>r&&e[n-2][1].type==="whitespace")&&(n-=r+1===n?2:4),n>r&&(i={type:"atxHeadingText",start:e[r][1].start,end:e[n][1].end},o={type:"chunkText",start:e[r][1].start,end:e[n][1].end,contentType:"text"},gr(e,r,n-r+1,[["enter",i,t],["enter",o,t],["exit",o,t],["exit",i,t]])),e}function HL(e,t,n){let r=0;return i;function i(c){return e.enter("atxHeading"),o(c)}function o(c){return e.enter("atxHeadingSequence"),a(c)}function a(c){return c===35&&r++<6?(e.consume(c),a):c===null||We(c)?(e.exit("atxHeadingSequence"),s(c)):n(c)}function s(c){return c===35?(e.enter("atxHeadingSequence"),l(c)):c===null||fe(c)?(e.exit("atxHeading"),t(c)):be(c)?Ie(e,s,"whitespace")(c):(e.enter("atxHeadingText"),u(c))}function l(c){return c===35?(e.consume(c),l):(e.exit("atxHeadingSequence"),s(c))}function u(c){return c===null||c===35||We(c)?(e.exit("atxHeadingText"),s(c)):(e.consume(c),u)}}const zL=["address","article","aside","base","basefont","blockquote","body","caption","center","col","colgroup","dd","details","dialog","dir","div","dl","dt","fieldset","figcaption","figure","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hr","html","iframe","legend","li","link","main","menu","menuitem","nav","noframes","ol","optgroup","option","p","param","search","section","summary","table","tbody","td","tfoot","th","thead","title","tr","track","ul"],f2=["pre","script","style","textarea"],UL={name:"htmlFlow",tokenize:GL,resolveTo:jL,concrete:!0},KL={tokenize:VL,partial:!0},WL={tokenize:$L,partial:!0};function jL(e){let t=e.length;for(;t--&&!(e[t][0]==="enter"&&e[t][1].type==="htmlFlow"););return t>1&&e[t-2][1].type==="linePrefix"&&(e[t][1].start=e[t-2][1].start,e[t+1][1].start=e[t-2][1].start,e.splice(t-2,2)),e}function GL(e,t,n){const r=this;let i,o,a,s,l;return u;function u(x){return c(x)}function c(x){return e.enter("htmlFlow"),e.enter("htmlFlowData"),e.consume(x),f}function f(x){return x===33?(e.consume(x),d):x===47?(e.consume(x),o=!0,g):x===63?(e.consume(x),i=3,r.interrupt?t:_):An(x)?(e.consume(x),a=String.fromCharCode(x),C):n(x)}function d(x){return x===45?(e.consume(x),i=2,h):x===91?(e.consume(x),i=5,s=0,v):An(x)?(e.consume(x),i=4,r.interrupt?t:_):n(x)}function h(x){return x===45?(e.consume(x),r.interrupt?t:_):n(x)}function v(x){const ge="CDATA[";return x===ge.charCodeAt(s++)?(e.consume(x),s===ge.length?r.interrupt?t:M:v):n(x)}function g(x){return An(x)?(e.consume(x),a=String.fromCharCode(x),C):n(x)}function C(x){if(x===null||x===47||x===62||We(x)){const ge=x===47,xt=a.toLowerCase();return!ge&&!o&&f2.includes(xt)?(i=1,r.interrupt?t(x):M(x)):zL.includes(a.toLowerCase())?(i=6,ge?(e.consume(x),m):r.interrupt?t(x):M(x)):(i=7,r.interrupt&&!r.parser.lazy[r.now().line]?n(x):o?y(x):T(x))}return x===45||Yn(x)?(e.consume(x),a+=String.fromCharCode(x),C):n(x)}function m(x){return x===62?(e.consume(x),r.interrupt?t:M):n(x)}function y(x){return be(x)?(e.consume(x),y):I(x)}function T(x){return x===47?(e.consume(x),I):x===58||x===95||An(x)?(e.consume(x),S):be(x)?(e.consume(x),T):I(x)}function S(x){return x===45||x===46||x===58||x===95||Yn(x)?(e.consume(x),S):w(x)}function w(x){return x===61?(e.consume(x),b):be(x)?(e.consume(x),w):T(x)}function b(x){return x===null||x===60||x===61||x===62||x===96?n(x):x===34||x===39?(e.consume(x),l=x,A):be(x)?(e.consume(x),b):F(x)}function A(x){return x===l?(e.consume(x),l=null,D):x===null||fe(x)?n(x):(e.consume(x),A)}function F(x){return x===null||x===34||x===39||x===47||x===60||x===61||x===62||x===96||We(x)?w(x):(e.consume(x),F)}function D(x){return x===47||x===62||be(x)?T(x):n(x)}function I(x){return x===62?(e.consume(x),B):n(x)}function B(x){return x===null||fe(x)?M(x):be(x)?(e.consume(x),B):n(x)}function M(x){return x===45&&i===2?(e.consume(x),z):x===60&&i===1?(e.consume(x),Z):x===62&&i===4?(e.consume(x),ee):x===63&&i===3?(e.consume(x),_):x===93&&i===5?(e.consume(x),U):fe(x)&&(i===6||i===7)?(e.exit("htmlFlowData"),e.check(KL,X,oe)(x)):x===null||fe(x)?(e.exit("htmlFlowData"),oe(x)):(e.consume(x),M)}function oe(x){return e.check(WL,V,X)(x)}function V(x){return e.enter("lineEnding"),e.consume(x),e.exit("lineEnding"),P}function P(x){return x===null||fe(x)?oe(x):(e.enter("htmlFlowData"),M(x))}function z(x){return x===45?(e.consume(x),_):M(x)}function Z(x){return x===47?(e.consume(x),a="",H):M(x)}function H(x){if(x===62){const ge=a.toLowerCase();return f2.includes(ge)?(e.consume(x),ee):M(x)}return An(x)&&a.length<8?(e.consume(x),a+=String.fromCharCode(x),H):M(x)}function U(x){return x===93?(e.consume(x),_):M(x)}function _(x){return x===62?(e.consume(x),ee):x===45&&i===2?(e.consume(x),_):M(x)}function ee(x){return x===null||fe(x)?(e.exit("htmlFlowData"),X(x)):(e.consume(x),ee)}function X(x){return e.exit("htmlFlow"),t(x)}}function $L(e,t,n){const r=this;return i;function i(a){return fe(a)?(e.enter("lineEnding"),e.consume(a),e.exit("lineEnding"),o):n(a)}function o(a){return r.parser.lazy[r.now().line]?n(a):t(a)}}function VL(e,t,n){return r;function r(i){return e.enter("lineEnding"),e.consume(i),e.exit("lineEnding"),e.attempt(mc,t,n)}}const qL={name:"htmlText",tokenize:YL};function YL(e,t,n){const r=this;let i,o,a;return s;function s(_){return e.enter("htmlText"),e.enter("htmlTextData"),e.consume(_),l}function l(_){return _===33?(e.consume(_),u):_===47?(e.consume(_),w):_===63?(e.consume(_),T):An(_)?(e.consume(_),F):n(_)}function u(_){return _===45?(e.consume(_),c):_===91?(e.consume(_),o=0,v):An(_)?(e.consume(_),y):n(_)}function c(_){return _===45?(e.consume(_),h):n(_)}function f(_){return _===null?n(_):_===45?(e.consume(_),d):fe(_)?(a=f,Z(_)):(e.consume(_),f)}function d(_){return _===45?(e.consume(_),h):f(_)}function h(_){return _===62?z(_):_===45?d(_):f(_)}function v(_){const ee="CDATA[";return _===ee.charCodeAt(o++)?(e.consume(_),o===ee.length?g:v):n(_)}function g(_){return _===null?n(_):_===93?(e.consume(_),C):fe(_)?(a=g,Z(_)):(e.consume(_),g)}function C(_){return _===93?(e.consume(_),m):g(_)}function m(_){return _===62?z(_):_===93?(e.consume(_),m):g(_)}function y(_){return _===null||_===62?z(_):fe(_)?(a=y,Z(_)):(e.consume(_),y)}function T(_){return _===null?n(_):_===63?(e.consume(_),S):fe(_)?(a=T,Z(_)):(e.consume(_),T)}function S(_){return _===62?z(_):T(_)}function w(_){return An(_)?(e.consume(_),b):n(_)}function b(_){return _===45||Yn(_)?(e.consume(_),b):A(_)}function A(_){return fe(_)?(a=A,Z(_)):be(_)?(e.consume(_),A):z(_)}function F(_){return _===45||Yn(_)?(e.consume(_),F):_===47||_===62||We(_)?D(_):n(_)}function D(_){return _===47?(e.consume(_),z):_===58||_===95||An(_)?(e.consume(_),I):fe(_)?(a=D,Z(_)):be(_)?(e.consume(_),D):z(_)}function I(_){return _===45||_===46||_===58||_===95||Yn(_)?(e.consume(_),I):B(_)}function B(_){return _===61?(e.consume(_),M):fe(_)?(a=B,Z(_)):be(_)?(e.consume(_),B):D(_)}function M(_){return _===null||_===60||_===61||_===62||_===96?n(_):_===34||_===39?(e.consume(_),i=_,oe):fe(_)?(a=M,Z(_)):be(_)?(e.consume(_),M):(e.consume(_),V)}function oe(_){return _===i?(e.consume(_),i=void 0,P):_===null?n(_):fe(_)?(a=oe,Z(_)):(e.consume(_),oe)}function V(_){return _===null||_===34||_===39||_===60||_===61||_===96?n(_):_===47||_===62||We(_)?D(_):(e.consume(_),V)}function P(_){return _===47||_===62||We(_)?D(_):n(_)}function z(_){return _===62?(e.consume(_),e.exit("htmlTextData"),e.exit("htmlText"),t):n(_)}function Z(_){return e.exit("htmlTextData"),e.enter("lineEnding"),e.consume(_),e.exit("lineEnding"),H}function H(_){return be(_)?Ie(e,U,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(_):U(_)}function U(_){return e.enter("htmlTextData"),a(_)}}const I9={name:"labelEnd",tokenize:tB,resolveTo:eB,resolveAll:JL},QL={tokenize:nB},XL={tokenize:rB},ZL={tokenize:iB};function JL(e){let t=-1;for(;++t=3&&(u===null||fe(u))?(e.exit("thematicBreak"),t(u)):n(u)}function l(u){return u===i?(e.consume(u),r++,l):(e.exit("thematicBreakSequence"),be(u)?Ie(e,s,"whitespace")(u):s(u))}}const zn={name:"list",tokenize:hB,continuation:{tokenize:pB},exit:gB},fB={tokenize:vB,partial:!0},dB={tokenize:mB,partial:!0};function hB(e,t,n){const r=this,i=r.events[r.events.length-1];let o=i&&i[1].type==="linePrefix"?i[2].sliceSerialize(i[1],!0).length:0,a=0;return s;function s(h){const v=r.containerState.type||(h===42||h===43||h===45?"listUnordered":"listOrdered");if(v==="listUnordered"?!r.containerState.marker||h===r.containerState.marker:wg(h)){if(r.containerState.type||(r.containerState.type=v,e.enter(v,{_container:!0})),v==="listUnordered")return e.enter("listItemPrefix"),h===42||h===45?e.check(ad,n,u)(h):u(h);if(!r.interrupt||h===49)return e.enter("listItemPrefix"),e.enter("listItemValue"),l(h)}return n(h)}function l(h){return wg(h)&&++a<10?(e.consume(h),l):(!r.interrupt||a<2)&&(r.containerState.marker?h===r.containerState.marker:h===41||h===46)?(e.exit("listItemValue"),u(h)):n(h)}function u(h){return e.enter("listItemMarker"),e.consume(h),e.exit("listItemMarker"),r.containerState.marker=r.containerState.marker||h,e.check(mc,r.interrupt?n:c,e.attempt(fB,d,f))}function c(h){return r.containerState.initialBlankLine=!0,o++,d(h)}function f(h){return be(h)?(e.enter("listItemPrefixWhitespace"),e.consume(h),e.exit("listItemPrefixWhitespace"),d):n(h)}function d(h){return r.containerState.size=o+r.sliceSerialize(e.exit("listItemPrefix"),!0).length,t(h)}}function pB(e,t,n){const r=this;return r.containerState._closeFlow=void 0,e.check(mc,i,o);function i(s){return r.containerState.furtherBlankLines=r.containerState.furtherBlankLines||r.containerState.initialBlankLine,Ie(e,t,"listItemIndent",r.containerState.size+1)(s)}function o(s){return r.containerState.furtherBlankLines||!be(s)?(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,a(s)):(r.containerState.furtherBlankLines=void 0,r.containerState.initialBlankLine=void 0,e.attempt(dB,t,a)(s))}function a(s){return r.containerState._closeFlow=!0,r.interrupt=void 0,Ie(e,e.attempt(zn,t,n),"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(s)}}function mB(e,t,n){const r=this;return Ie(e,i,"listItemIndent",r.containerState.size+1);function i(o){const a=r.events[r.events.length-1];return a&&a[1].type==="listItemIndent"&&a[2].sliceSerialize(a[1],!0).length===r.containerState.size?t(o):n(o)}}function gB(e){e.exit(this.containerState.type)}function vB(e,t,n){const r=this;return Ie(e,i,"listItemPrefixWhitespace",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4+1);function i(o){const a=r.events[r.events.length-1];return!be(o)&&a&&a[1].type==="listItemPrefixWhitespace"?t(o):n(o)}}const d2={name:"setextUnderline",tokenize:yB,resolveTo:EB};function EB(e,t){let n=e.length,r,i,o;for(;n--;)if(e[n][0]==="enter"){if(e[n][1].type==="content"){r=n;break}e[n][1].type==="paragraph"&&(i=n)}else e[n][1].type==="content"&&e.splice(n,1),!o&&e[n][1].type==="definition"&&(o=n);const a={type:"setextHeading",start:Object.assign({},e[i][1].start),end:Object.assign({},e[e.length-1][1].end)};return e[i][1].type="setextHeadingText",o?(e.splice(i,0,["enter",a,t]),e.splice(o+1,0,["exit",e[r][1],t]),e[r][1].end=Object.assign({},e[o][1].end)):e[r][1]=a,e.push(["exit",a,t]),e}function yB(e,t,n){const r=this;let i;return o;function o(u){let c=r.events.length,f;for(;c--;)if(r.events[c][1].type!=="lineEnding"&&r.events[c][1].type!=="linePrefix"&&r.events[c][1].type!=="content"){f=r.events[c][1].type==="paragraph";break}return!r.parser.lazy[r.now().line]&&(r.interrupt||f)?(e.enter("setextHeadingLine"),i=u,a(u)):n(u)}function a(u){return e.enter("setextHeadingLineSequence"),s(u)}function s(u){return u===i?(e.consume(u),s):(e.exit("setextHeadingLineSequence"),be(u)?Ie(e,l,"lineSuffix")(u):l(u))}function l(u){return u===null||fe(u)?(e.exit("setextHeadingLine"),t(u)):n(u)}}const TB={tokenize:CB};function CB(e){const t=this,n=e.attempt(mc,r,e.attempt(this.parser.constructs.flowInitial,i,Ie(e,e.attempt(this.parser.constructs.flow,i,e.attempt(wL,i)),"linePrefix")));return n;function r(o){if(o===null){e.consume(o);return}return e.enter("lineEndingBlank"),e.consume(o),e.exit("lineEndingBlank"),t.currentConstruct=void 0,n}function i(o){if(o===null){e.consume(o);return}return e.enter("lineEnding"),e.consume(o),e.exit("lineEnding"),t.currentConstruct=void 0,n}}const SB={resolveAll:N6()},_B=x6("string"),bB=x6("text");function x6(e){return{tokenize:t,resolveAll:N6(e==="text"?AB:void 0)};function t(n){const r=this,i=this.parser.constructs[e],o=n.attempt(i,a,s);return a;function a(c){return u(c)?o(c):s(c)}function s(c){if(c===null){n.consume(c);return}return n.enter("data"),n.consume(c),l}function l(c){return u(c)?(n.exit("data"),o(c)):(n.consume(c),l)}function u(c){if(c===null)return!0;const f=i[c];let d=-1;if(f)for(;++d-1){const s=a[0];typeof s=="string"?a[0]=s.slice(r):a.shift()}o>0&&a.push(e[i].slice(0,o))}return a}function IB(e,t){let n=-1;const r=[];let i;for(;++n13&&n<32||n>126&&n<160||n>55295&&n<57344||n>64975&&n<65008||(n&65535)===65535||(n&65535)===65534||n>1114111?"�":String.fromCharCode(n)}const KB=/\\([!-/:-@[-`{-~])|&(#(?:\d{1,7}|x[\da-f]{1,6})|[\da-z]{1,31});/gi;function R6(e){return e.replace(KB,WB)}function WB(e,t,n){if(t)return t;if(n.charCodeAt(0)===35){const i=n.charCodeAt(1),o=i===120||i===88;return F6(n.slice(o?2:1),o?16:10)}return k9(n)||e}const D6={}.hasOwnProperty,jB=function(e,t,n){return typeof t!="string"&&(n=t,t=void 0),GB(n)(UB(HB(n).document().write(zB()(e,t,!0))))};function GB(e){const t={transforms:[],canContainEols:["emphasis","fragment","heading","paragraph","strong"],enter:{autolink:s(ko),autolinkProtocol:B,autolinkEmail:B,atxHeading:s(hi),blockQuote:s(mn),characterEscape:B,characterReference:B,codeFenced:s(er),codeFencedFenceInfo:l,codeFencedFenceMeta:l,codeIndented:s(er,l),codeText:s(di,l),codeTextData:B,data:B,codeFlowValue:B,definition:s(Ao),definitionDestinationString:l,definitionLabelString:l,definitionTitleString:l,emphasis:s(wo),hardBreakEscape:s(On),hardBreakTrailing:s(On),htmlFlow:s(Na,l),htmlFlowData:B,htmlText:s(Na,l),htmlTextData:B,image:s(Is),label:l,link:s(ko),listItem:s(zr),listItemValue:v,listOrdered:s(Fa,h),listUnordered:s(Fa),paragraph:s(xs),reference:xt,referenceString:l,resourceDestinationString:l,resourceTitleString:l,setextHeading:s(hi),strong:s(ou),thematicBreak:s(Ns)},exit:{atxHeading:c(),atxHeadingSequence:A,autolink:c(),autolinkEmail:Ct,autolinkProtocol:Bt,blockQuote:c(),characterEscapeValue:M,characterReferenceMarkerHexadecimal:it,characterReferenceMarkerNumeric:it,characterReferenceValue:Nt,codeFenced:c(y),codeFencedFence:m,codeFencedFenceInfo:g,codeFencedFenceMeta:C,codeFlowValue:M,codeIndented:c(T),codeText:c(Z),codeTextData:M,data:M,definition:c(),definitionDestinationString:b,definitionLabelString:S,definitionTitleString:w,emphasis:c(),hardBreakEscape:c(V),hardBreakTrailing:c(V),htmlFlow:c(P),htmlFlowData:M,htmlText:c(z),htmlTextData:M,image:c(U),label:ee,labelText:_,lineEnding:oe,link:c(H),listItem:c(),listOrdered:c(),listUnordered:c(),paragraph:c(),referenceString:pe,resourceDestinationString:X,resourceTitleString:x,resource:ge,setextHeading:c(I),setextHeadingLineSequence:D,setextHeadingText:F,strong:c(),thematicBreak:c()}};M6(t,(e||{}).mdastExtensions||[]);const n={};return r;function r(O){let G={type:"root",children:[]};const de={stack:[G],tokenStack:[],config:t,enter:u,exit:f,buffer:l,resume:d,setData:o,getData:a},Ce=[];let Q=-1;for(;++Q0){const Ht=de.tokenStack[de.tokenStack.length-1];(Ht[1]||p2).call(de,void 0,Ht[0])}for(G.position={start:Po(O.length>0?O[0][1].start:{line:1,column:1,offset:0}),end:Po(O.length>0?O[O.length-2][1].end:{line:1,column:1,offset:0})},Q=-1;++Q{const r=this.data("settings");return jB(n,Object.assign({},r,e,{extensions:this.data("micromarkExtensions")||[],mdastExtensions:this.data("fromMarkdownExtensions")||[]}))}})}const Vt=function(e,t,n){const r={type:String(e)};return n==null&&(typeof t=="string"||Array.isArray(t))?n=t:Object.assign(r,t),Array.isArray(n)?r.children=n:n!=null&&(r.value=String(n)),r},sd={}.hasOwnProperty;function qB(e,t){const n=t.data||{};return"value"in t&&!(sd.call(n,"hName")||sd.call(n,"hProperties")||sd.call(n,"hChildren"))?e.augment(t,Vt("text",t.value)):e(t,"div",Pn(e,t))}function P6(e,t,n){const r=t&&t.type;let i;if(!r)throw new Error("Expected node, got `"+t+"`");return sd.call(e.handlers,r)?i=e.handlers[r]:e.passThrough&&e.passThrough.includes(r)?i=YB:i=e.unknownHandler,(typeof i=="function"?i:qB)(e,t,n)}function YB(e,t){return"children"in t?{...t,children:Pn(e,t)}:t}function Pn(e,t){const n=[];if("children"in t){const r=t.children;let i=-1;for(;++i":""))+")"})}return f;function f(){let d=[],h,v,g;if((!t||i(s,l,u[u.length-1]||null))&&(d=nH(n(s,u)),d[0]===m2))return d;if(s.children&&d[0]!==tH)for(v=(r?s.children.length:-1)+o,g=u.concat(s);v>-1&&v-1?r.offset:null}}}function rH(e){return!e||!e.position||!e.position.start||!e.position.start.line||!e.position.start.column||!e.position.end||!e.position.end.line||!e.position.end.column}const g2={}.hasOwnProperty;function iH(e){const t=Object.create(null);if(!e||!e.type)throw new Error("mdast-util-definitions expected node");return zl(e,"definition",r=>{const i=v2(r.identifier);i&&!g2.call(t,i)&&(t[i]=r)}),n;function n(r){const i=v2(r);return i&&g2.call(t,i)?t[i]:null}}function v2(e){return String(e||"").toUpperCase()}function B6(e,t){return e(t,"hr")}function ea(e,t){const n=[];let r=-1;for(t&&n.push(Vt("text",` +`));++r0&&n.push(Vt("text",` +`)),n}function H6(e,t){const n={},r=t.ordered?"ol":"ul",i=Pn(e,t);let o=-1;for(typeof t.start=="number"&&t.start!==1&&(n.start=t.start);++o"u"&&(n=!0),s=pH(t),r=0,i=e.length;r=55296&&o<=57343){if(o>=55296&&o<=56319&&r+1=56320&&a<=57343)){l+=encodeURIComponent(e[r]+e[r+1]),r++;continue}l+="%EF%BF%BD";continue}l+=encodeURIComponent(e[r])}return l}Gh.defaultChars=";/?:@&=+$,-_.!~*'()#";Gh.componentChars="-_.!~*'()";var mH=Gh;const $h=Ea(mH);function U6(e,t){const n=t.referenceType;let r="]";if(n==="collapsed"?r+="[]":n==="full"&&(r+="["+(t.label||t.identifier)+"]"),t.type==="imageReference")return Vt("text","!["+t.alt+r);const i=Pn(e,t),o=i[0];o&&o.type==="text"?o.value="["+o.value:i.unshift(Vt("text","["));const a=i[i.length-1];return a&&a.type==="text"?a.value+=r:i.push(Vt("text",r)),i}function gH(e,t){const n=e.definition(t.identifier);if(!n)return U6(e,t);const r={src:$h(n.url||""),alt:t.alt};return n.title!==null&&n.title!==void 0&&(r.title=n.title),e(t,"img",r)}function vH(e,t){const n={src:$h(t.url),alt:t.alt};return t.title!==null&&t.title!==void 0&&(n.title=t.title),e(t,"img",n)}function EH(e,t){return e(t,"code",[Vt("text",t.value.replace(/\r?\n|\r/g," "))])}function yH(e,t){const n=e.definition(t.identifier);if(!n)return U6(e,t);const r={href:$h(n.url||"")};return n.title!==null&&n.title!==void 0&&(r.title=n.title),e(t,"a",r,Pn(e,t))}function TH(e,t){const n={href:$h(t.url)};return t.title!==null&&t.title!==void 0&&(n.title=t.title),e(t,"a",n,Pn(e,t))}function CH(e,t,n){const r=Pn(e,t),i=n?SH(n):K6(t),o={},a=[];if(typeof t.checked=="boolean"){let u;r[0]&&r[0].type==="element"&&r[0].tagName==="p"?u=r[0]:(u=e(null,"p",[]),r.unshift(u)),u.children.length>0&&u.children.unshift(Vt("text"," ")),u.children.unshift(e(null,"input",{type:"checkbox",checked:t.checked,disabled:!0})),o.className=["task-list-item"]}let s=-1;for(;++s1}function _H(e,t){return e(t,"p",Pn(e,t))}function bH(e,t){return e.augment(t,Vt("root",ea(Pn(e,t))))}function AH(e,t){return e(t,"strong",Pn(e,t))}function wH(e,t){const n=t.children;let r=-1;const i=t.align||[],o=[];for(;++r{const l=String(s.identifier).toUpperCase();xH.call(i,l)||(i[l]=s)}),a;function o(s,l){if(s&&"data"in s&&s.data){const u=s.data;u.hName&&(l.type!=="element"&&(l={type:"element",tagName:"",properties:{},children:[]}),l.tagName=u.hName),l.type==="element"&&u.hProperties&&(l.properties={...l.properties,...u.hProperties}),"children"in l&&l.children&&u.hChildren&&(l.children=u.hChildren)}if(s){const u="type"in s?s:{position:s};rH(u)||(l.position={start:jh(u),end:N9(u)})}return l}function a(s,l,u,c){return Array.isArray(u)&&(c=u,u={}),o(s,{type:"element",tagName:l,properties:u||{},children:c||[]})}}function W6(e,t){const n=NH(e,t),r=P6(n,e,null),i=oH(n);return i&&r.children.push(Vt("text",` +`),i),Array.isArray(r)?{type:"root",children:r}:r}const FH=function(e,t){return e&&"run"in e?DH(e,t):MH(e)},RH=FH;function DH(e,t){return(n,r,i)=>{e.run(W6(n,t),r,o=>{i(o)})}}function MH(e){return t=>W6(t,e)}var j6={exports:{}},PH="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED",OH=PH,LH=OH;function G6(){}function $6(){}$6.resetWarningCache=G6;var BH=function(){function e(r,i,o,a,s,l){if(l!==LH){var u=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw u.name="Invariant Violation",u}}e.isRequired=e;function t(){return e}var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:$6,resetWarningCache:G6};return n.PropTypes=n,n};j6.exports=BH();var HH=j6.exports;const J=Ea(HH);class gc{constructor(t,n,r){this.property=t,this.normal=n,r&&(this.space=r)}}gc.prototype.property={};gc.prototype.normal={};gc.prototype.space=null;function V6(e,t){const n={},r={};let i=-1;for(;++i4&&n.slice(0,4)==="data"&&jH.test(t)){if(t.charAt(4)==="-"){const o=t.slice(5).replace(T2,VH);r="data"+o.charAt(0).toUpperCase()+o.slice(1)}else{const o=t.slice(4);if(!T2.test(o)){let a=o.replace(GH,$H);a.charAt(0)!=="-"&&(a="-"+a),t="data"+a}}i=F9}return new i(r,t)}function $H(e){return"-"+e.toLowerCase()}function VH(e){return e.charAt(1).toUpperCase()}const C2={classId:"classID",dataType:"datatype",itemId:"itemID",strokeDashArray:"strokeDasharray",strokeDashOffset:"strokeDashoffset",strokeLineCap:"strokeLinecap",strokeLineJoin:"strokeLinejoin",strokeMiterLimit:"strokeMiterlimit",typeOf:"typeof",xLinkActuate:"xlinkActuate",xLinkArcRole:"xlinkArcrole",xLinkHref:"xlinkHref",xLinkRole:"xlinkRole",xLinkShow:"xlinkShow",xLinkTitle:"xlinkTitle",xLinkType:"xlinkType",xmlnsXLink:"xmlnsXlink"},vc=V6([Q6,Y6,J6,eb,KH],"html"),Jl=V6([Q6,Y6,J6,eb,WH],"svg");function qH(e){if(e.allowedElements&&e.disallowedElements)throw new TypeError("Only one of `allowedElements` and `disallowedElements` should be defined");if(e.allowedElements||e.disallowedElements||e.allowElement)return t=>{zl(t,"element",(n,r,i)=>{const o=i;let a;if(e.allowedElements?a=!e.allowedElements.includes(n.tagName):e.disallowedElements&&(a=e.disallowedElements.includes(n.tagName)),!a&&e.allowElement&&typeof r=="number"&&(a=!e.allowElement(n,r,o)),a&&typeof r=="number")return e.unwrapDisallowed&&n.children?o.children.splice(r,1,...n.children):o.children.splice(r,1),r})}}var tb={exports:{}},je={};/** @license React v17.0.2 + * react-is.production.min.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var qh=60103,Yh=60106,Ec=60107,yc=60108,Tc=60114,Cc=60109,Sc=60110,_c=60112,bc=60113,R9=60120,Ac=60115,wc=60116,nb=60121,rb=60122,ib=60117,ob=60129,ab=60131;if(typeof Symbol=="function"&&Symbol.for){var qt=Symbol.for;qh=qt("react.element"),Yh=qt("react.portal"),Ec=qt("react.fragment"),yc=qt("react.strict_mode"),Tc=qt("react.profiler"),Cc=qt("react.provider"),Sc=qt("react.context"),_c=qt("react.forward_ref"),bc=qt("react.suspense"),R9=qt("react.suspense_list"),Ac=qt("react.memo"),wc=qt("react.lazy"),nb=qt("react.block"),rb=qt("react.server.block"),ib=qt("react.fundamental"),ob=qt("react.debug_trace_mode"),ab=qt("react.legacy_hidden")}function ui(e){if(typeof e=="object"&&e!==null){var t=e.$$typeof;switch(t){case qh:switch(e=e.type,e){case Ec:case Tc:case yc:case bc:case R9:return e;default:switch(e=e&&e.$$typeof,e){case Sc:case _c:case wc:case Ac:case Cc:return e;default:return t}}case Yh:return t}}}var YH=Cc,QH=qh,XH=_c,ZH=Ec,JH=wc,ez=Ac,tz=Yh,nz=Tc,rz=yc,iz=bc;je.ContextConsumer=Sc;je.ContextProvider=YH;je.Element=QH;je.ForwardRef=XH;je.Fragment=ZH;je.Lazy=JH;je.Memo=ez;je.Portal=tz;je.Profiler=nz;je.StrictMode=rz;je.Suspense=iz;je.isAsyncMode=function(){return!1};je.isConcurrentMode=function(){return!1};je.isContextConsumer=function(e){return ui(e)===Sc};je.isContextProvider=function(e){return ui(e)===Cc};je.isElement=function(e){return typeof e=="object"&&e!==null&&e.$$typeof===qh};je.isForwardRef=function(e){return ui(e)===_c};je.isFragment=function(e){return ui(e)===Ec};je.isLazy=function(e){return ui(e)===wc};je.isMemo=function(e){return ui(e)===Ac};je.isPortal=function(e){return ui(e)===Yh};je.isProfiler=function(e){return ui(e)===Tc};je.isStrictMode=function(e){return ui(e)===yc};je.isSuspense=function(e){return ui(e)===bc};je.isValidElementType=function(e){return typeof e=="string"||typeof e=="function"||e===Ec||e===Tc||e===ob||e===yc||e===bc||e===R9||e===ab||typeof e=="object"&&e!==null&&(e.$$typeof===wc||e.$$typeof===Ac||e.$$typeof===Cc||e.$$typeof===Sc||e.$$typeof===_c||e.$$typeof===ib||e.$$typeof===nb||e[0]===rb)};je.typeOf=ui;tb.exports=je;var oz=tb.exports;const az=Ea(oz);function sz(e){const t=e&&typeof e=="object"&&e.type==="text"?e.value||"":e;return typeof t=="string"&&t.replace(/[ \t\n\f\r]/g,"")===""}function S2(e){const t=String(e||"").trim();return t?t.split(/[ \t\n\r\f]+/g):[]}function sb(e){return e.join(" ").trim()}function _2(e){const t=[],n=String(e||"");let r=n.indexOf(","),i=0,o=!1;for(;!o;){r===-1&&(r=n.length,o=!0);const a=n.slice(i,r).trim();(a||!o)&&t.push(a),i=r+1,r=n.indexOf(",",i)}return t}function lb(e,t){const n=t||{};return(e[e.length-1]===""?[...e,""]:e).join((n.padRight?" ":"")+","+(n.padLeft===!1?"":" ")).trim()}var b2=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g,lz=/\n/g,uz=/^\s*/,cz=/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/,fz=/^:\s*/,dz=/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/,hz=/^[;\s]*/,pz=/^\s+|\s+$/g,mz=` +`,A2="/",w2="*",$a="",gz="comment",vz="declaration",Ez=function(e,t){if(typeof e!="string")throw new TypeError("First argument must be a string");if(!e)return[];t=t||{};var n=1,r=1;function i(v){var g=v.match(lz);g&&(n+=g.length);var C=v.lastIndexOf(mz);r=~C?v.length-C:r+v.length}function o(){var v={line:n,column:r};return function(g){return g.position=new a(v),u(),g}}function a(v){this.start=v,this.end={line:n,column:r},this.source=t.source}a.prototype.content=e;function s(v){var g=new Error(t.source+":"+n+":"+r+": "+v);if(g.reason=v,g.filename=t.source,g.line=n,g.column=r,g.source=e,!t.silent)throw g}function l(v){var g=v.exec(e);if(g){var C=g[0];return i(C),e=e.slice(C.length),g}}function u(){l(uz)}function c(v){var g;for(v=v||[];g=f();)g!==!1&&v.push(g);return v}function f(){var v=o();if(!(A2!=e.charAt(0)||w2!=e.charAt(1))){for(var g=2;$a!=e.charAt(g)&&(w2!=e.charAt(g)||A2!=e.charAt(g+1));)++g;if(g+=2,$a===e.charAt(g-1))return s("End of comment missing");var C=e.slice(2,g-2);return r+=2,i(C),e=e.slice(g),r+=2,v({type:gz,comment:C})}}function d(){var v=o(),g=l(cz);if(g){if(f(),!l(fz))return s("property missing ':'");var C=l(dz),m=v({type:vz,property:k2(g[0].replace(b2,$a)),value:C?k2(C[0].replace(b2,$a)):$a});return l(hz),m}}function h(){var v=[];c(v);for(var g;g=d();)g!==!1&&(v.push(g),c(v));return v}return u(),h()};function k2(e){return e?e.replace(pz,$a):$a}var yz=Ez;function Tz(e,t){var n=null;if(!e||typeof e!="string")return n;for(var r,i=yz(e),o=typeof t=="function",a,s,l=0,u=i.length;l0?Pi.createElement(d,s,c):Pi.createElement(d,s)}function Az(e){let t=-1;for(;++tString(t)).join("")}const I2={}.hasOwnProperty,Nz="https://github.com/remarkjs/react-markdown/blob/main/changelog.md",gf={renderers:{to:"components",id:"change-renderers-to-components"},astPlugins:{id:"remove-buggy-html-in-markdown-parser"},allowDangerousHtml:{id:"remove-buggy-html-in-markdown-parser"},escapeHtml:{id:"remove-buggy-html-in-markdown-parser"},source:{to:"children",id:"change-source-to-children"},allowNode:{to:"allowElement",id:"replace-allownode-allowedtypes-and-disallowedtypes"},allowedTypes:{to:"allowedElements",id:"replace-allownode-allowedtypes-and-disallowedtypes"},disallowedTypes:{to:"disallowedElements",id:"replace-allownode-allowedtypes-and-disallowedtypes"},includeNodeIndex:{to:"includeElementIndex",id:"change-includenodeindex-to-includeelementindex"}};function Qh(e){for(const o in gf)if(I2.call(gf,o)&&I2.call(e,o)){const a=gf[o];console.warn(`[react-markdown] Warning: please ${a.to?`use \`${a.to}\` instead of`:"remove"} \`${o}\` (see <${Nz}#${a.id}> for more info)`),delete gf[o]}const t=KO().use(VB).use(e.remarkPlugins||e.plugins||[]).use(RH,{allowDangerousHtml:!0}).use(e.rehypePlugins||[]).use(qH,e),n=new g6;typeof e.children=="string"?n.value=e.children:e.children!==void 0&&e.children!==null&&console.warn(`[react-markdown] Warning: please pass a string as \`children\` (not: \`${e.children}\`)`);const r=t.runSync(t.parse(n),n);if(r.type!=="root")throw new TypeError("Expected a `root` node");let i=Pi.createElement(Pi.Fragment,{},ub({options:e,schema:vc,listDepth:0},r));return e.className&&(i=Pi.createElement("div",{className:e.className},i)),i}Qh.defaultProps={transformLinkUri:wO};Qh.propTypes={children:J.string,className:J.string,allowElement:J.func,allowedElements:J.arrayOf(J.string),disallowedElements:J.arrayOf(J.string),unwrapDisallowed:J.bool,remarkPlugins:J.arrayOf(J.oneOfType([J.object,J.func,J.arrayOf(J.oneOfType([J.object,J.func]))])),rehypePlugins:J.arrayOf(J.oneOfType([J.object,J.func,J.arrayOf(J.oneOfType([J.object,J.func]))])),sourcePos:J.bool,rawSourcePos:J.bool,skipHtml:J.bool,includeElementIndex:J.bool,transformLinkUri:J.oneOfType([J.func,J.bool]),linkTarget:J.oneOfType([J.func,J.string]),transformImageUri:J.func,components:J.object};const Fz={tokenize:Lz,partial:!0},cb={tokenize:Bz,partial:!0},fb={tokenize:Hz,partial:!0},db={tokenize:zz,partial:!0},Rz={tokenize:Uz,partial:!0},hb={tokenize:Pz,previous:mb},pb={tokenize:Oz,previous:gb},Co={tokenize:Mz,previous:vb},Ui={},Dz={text:Ui};let Oa=48;for(;Oa<123;)Ui[Oa]=Co,Oa++,Oa===58?Oa=65:Oa===91&&(Oa=97);Ui[43]=Co;Ui[45]=Co;Ui[46]=Co;Ui[95]=Co;Ui[72]=[Co,pb];Ui[104]=[Co,pb];Ui[87]=[Co,hb];Ui[119]=[Co,hb];function Mz(e,t,n){const r=this;let i,o;return a;function a(f){return!Ng(f)||!vb.call(r,r.previous)||D9(r.events)?n(f):(e.enter("literalAutolink"),e.enter("literalAutolinkEmail"),s(f))}function s(f){return Ng(f)?(e.consume(f),s):f===64?(e.consume(f),l):n(f)}function l(f){return f===46?e.check(Rz,c,u)(f):f===45||f===95||Yn(f)?(o=!0,e.consume(f),l):c(f)}function u(f){return e.consume(f),i=!0,l}function c(f){return o&&i&&An(r.previous)?(e.exit("literalAutolinkEmail"),e.exit("literalAutolink"),t(f)):n(f)}}function Pz(e,t,n){const r=this;return i;function i(a){return a!==87&&a!==119||!mb.call(r,r.previous)||D9(r.events)?n(a):(e.enter("literalAutolink"),e.enter("literalAutolinkWww"),e.check(Fz,e.attempt(cb,e.attempt(fb,o),n),n)(a))}function o(a){return e.exit("literalAutolinkWww"),e.exit("literalAutolink"),t(a)}}function Oz(e,t,n){const r=this;let i="",o=!1;return a;function a(f){return(f===72||f===104)&&gb.call(r,r.previous)&&!D9(r.events)?(e.enter("literalAutolink"),e.enter("literalAutolinkHttp"),i+=String.fromCodePoint(f),e.consume(f),s):n(f)}function s(f){if(An(f)&&i.length<5)return i+=String.fromCodePoint(f),e.consume(f),s;if(f===58){const d=i.toLowerCase();if(d==="http"||d==="https")return e.consume(f),l}return n(f)}function l(f){return f===47?(e.consume(f),o?u:(o=!0,l)):n(f)}function u(f){return f===null||th(f)||We(f)||ps(f)||Uh(f)?n(f):e.attempt(cb,e.attempt(fb,c),n)(f)}function c(f){return e.exit("literalAutolinkHttp"),e.exit("literalAutolink"),t(f)}}function Lz(e,t,n){let r=0;return i;function i(a){return(a===87||a===119)&&r<3?(r++,e.consume(a),i):a===46&&r===3?(e.consume(a),o):n(a)}function o(a){return a===null?n(a):t(a)}}function Bz(e,t,n){let r,i,o;return a;function a(u){return u===46||u===95?e.check(db,l,s)(u):u===null||We(u)||ps(u)||u!==45&&Uh(u)?l(u):(o=!0,e.consume(u),a)}function s(u){return u===95?r=!0:(i=r,r=void 0),e.consume(u),a}function l(u){return i||r||!o?n(u):t(u)}}function Hz(e,t){let n=0,r=0;return i;function i(a){return a===40?(n++,e.consume(a),i):a===41&&r0&&!n&&(e[e.length-1][1]._gfmAutolinkLiteralWalkedInto=!0),n}const Kz={tokenize:Qz,partial:!0};function Wz(){return{document:{[91]:{tokenize:Vz,continuation:{tokenize:qz},exit:Yz}},text:{[91]:{tokenize:$z},[93]:{add:"after",tokenize:jz,resolveTo:Gz}}}}function jz(e,t,n){const r=this;let i=r.events.length;const o=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let a;for(;i--;){const l=r.events[i][1];if(l.type==="labelImage"){a=l;break}if(l.type==="gfmFootnoteCall"||l.type==="labelLink"||l.type==="label"||l.type==="image"||l.type==="link")break}return s;function s(l){if(!a||!a._balanced)return n(l);const u=oi(r.sliceSerialize({start:a.end,end:r.now()}));return u.codePointAt(0)!==94||!o.includes(u.slice(1))?n(l):(e.enter("gfmFootnoteCallLabelMarker"),e.consume(l),e.exit("gfmFootnoteCallLabelMarker"),t(l))}}function Gz(e,t){let n=e.length;for(;n--;)if(e[n][1].type==="labelImage"&&e[n][0]==="enter"){e[n][1];break}e[n+1][1].type="data",e[n+3][1].type="gfmFootnoteCallLabelMarker";const r={type:"gfmFootnoteCall",start:Object.assign({},e[n+3][1].start),end:Object.assign({},e[e.length-1][1].end)},i={type:"gfmFootnoteCallMarker",start:Object.assign({},e[n+3][1].end),end:Object.assign({},e[n+3][1].end)};i.end.column++,i.end.offset++,i.end._bufferIndex++;const o={type:"gfmFootnoteCallString",start:Object.assign({},i.end),end:Object.assign({},e[e.length-1][1].start)},a={type:"chunkString",contentType:"string",start:Object.assign({},o.start),end:Object.assign({},o.end)},s=[e[n+1],e[n+2],["enter",r,t],e[n+3],e[n+4],["enter",i,t],["exit",i,t],["enter",o,t],["enter",a,t],["exit",a,t],["exit",o,t],e[e.length-2],e[e.length-1],["exit",r,t]];return e.splice(n,e.length-n+1,...s),e}function $z(e,t,n){const r=this,i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let o=0,a;return s;function s(f){return e.enter("gfmFootnoteCall"),e.enter("gfmFootnoteCallLabelMarker"),e.consume(f),e.exit("gfmFootnoteCallLabelMarker"),l}function l(f){return f!==94?n(f):(e.enter("gfmFootnoteCallMarker"),e.consume(f),e.exit("gfmFootnoteCallMarker"),e.enter("gfmFootnoteCallString"),e.enter("chunkString").contentType="string",u)}function u(f){if(o>999||f===93&&!a||f===null||f===91||We(f))return n(f);if(f===93){e.exit("chunkString");const d=e.exit("gfmFootnoteCallString");return i.includes(oi(r.sliceSerialize(d)))?(e.enter("gfmFootnoteCallLabelMarker"),e.consume(f),e.exit("gfmFootnoteCallLabelMarker"),e.exit("gfmFootnoteCall"),t):n(f)}return We(f)||(a=!0),o++,e.consume(f),f===92?c:u}function c(f){return f===91||f===92||f===93?(e.consume(f),o++,u):u(f)}}function Vz(e,t,n){const r=this,i=r.parser.gfmFootnotes||(r.parser.gfmFootnotes=[]);let o,a=0,s;return l;function l(v){return e.enter("gfmFootnoteDefinition")._container=!0,e.enter("gfmFootnoteDefinitionLabel"),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(v),e.exit("gfmFootnoteDefinitionLabelMarker"),u}function u(v){return v===94?(e.enter("gfmFootnoteDefinitionMarker"),e.consume(v),e.exit("gfmFootnoteDefinitionMarker"),e.enter("gfmFootnoteDefinitionLabelString"),e.enter("chunkString").contentType="string",c):n(v)}function c(v){if(a>999||v===93&&!s||v===null||v===91||We(v))return n(v);if(v===93){e.exit("chunkString");const g=e.exit("gfmFootnoteDefinitionLabelString");return o=oi(r.sliceSerialize(g)),e.enter("gfmFootnoteDefinitionLabelMarker"),e.consume(v),e.exit("gfmFootnoteDefinitionLabelMarker"),e.exit("gfmFootnoteDefinitionLabel"),d}return We(v)||(s=!0),a++,e.consume(v),v===92?f:c}function f(v){return v===91||v===92||v===93?(e.consume(v),a++,c):c(v)}function d(v){return v===58?(e.enter("definitionMarker"),e.consume(v),e.exit("definitionMarker"),i.includes(o)||i.push(o),Ie(e,h,"gfmFootnoteDefinitionWhitespace")):n(v)}function h(v){return t(v)}}function qz(e,t,n){return e.check(mc,t,e.attempt(Kz,t,n))}function Yz(e){e.exit("gfmFootnoteDefinition")}function Qz(e,t,n){const r=this;return Ie(e,i,"gfmFootnoteDefinitionIndent",4+1);function i(o){const a=r.events[r.events.length-1];return a&&a[1].type==="gfmFootnoteDefinitionIndent"&&a[2].sliceSerialize(a[1],!0).length===4?t(o):n(o)}}function Xz(e){let n=(e||{}).singleTilde;const r={tokenize:o,resolveAll:i};return n==null&&(n=!0),{text:{[126]:r},insideSpan:{null:[r]},attentionMarkers:{null:[126]}};function i(a,s){let l=-1;for(;++l1?l(v):(a.consume(v),f++,h);if(f<2&&!n)return l(v);const C=a.exit("strikethroughSequenceTemporary"),m=nh(v);return C._open=!m||m===2&&!!g,C._close=!g||g===2&&!!m,s(v)}}}class Zz{constructor(){this.map=[]}add(t,n,r){Jz(this,t,n,r)}consume(t){if(this.map.sort((o,a)=>o[0]-a[0]),this.map.length===0)return;let n=this.map.length;const r=[];for(;n>0;)n-=1,r.push(t.slice(this.map[n][0]+this.map[n][1])),r.push(this.map[n][2]),t.length=this.map[n][0];r.push([...t]),t.length=0;let i=r.pop();for(;i;)t.push(...i),i=r.pop();this.map.length=0}}function Jz(e,t,n,r){let i=0;if(!(n===0&&r.length===0)){for(;i-1;){const V=r.events[B][1].type;if(V==="lineEnding"||V==="linePrefix")B--;else break}const M=B>-1?r.events[B][1].type:null,oe=M==="tableHead"||M==="tableRow"?b:l;return oe===b&&r.parser.lazy[r.now().line]?n(I):oe(I)}function l(I){return e.enter("tableHead"),e.enter("tableRow"),u(I)}function u(I){return I===124||(a=!0,o+=1),c(I)}function c(I){return I===null?n(I):fe(I)?o>1?(o=0,r.interrupt=!0,e.exit("tableRow"),e.enter("lineEnding"),e.consume(I),e.exit("lineEnding"),h):n(I):be(I)?Ie(e,c,"whitespace")(I):(o+=1,a&&(a=!1,i+=1),I===124?(e.enter("tableCellDivider"),e.consume(I),e.exit("tableCellDivider"),a=!0,c):(e.enter("data"),f(I)))}function f(I){return I===null||I===124||We(I)?(e.exit("data"),c(I)):(e.consume(I),I===92?d:f)}function d(I){return I===92||I===124?(e.consume(I),f):f(I)}function h(I){return r.interrupt=!1,r.parser.lazy[r.now().line]?n(I):(e.enter("tableDelimiterRow"),a=!1,be(I)?Ie(e,v,"linePrefix",r.parser.constructs.disable.null.includes("codeIndented")?void 0:4)(I):v(I))}function v(I){return I===45||I===58?C(I):I===124?(a=!0,e.enter("tableCellDivider"),e.consume(I),e.exit("tableCellDivider"),g):w(I)}function g(I){return be(I)?Ie(e,C,"whitespace")(I):C(I)}function C(I){return I===58?(o+=1,a=!0,e.enter("tableDelimiterMarker"),e.consume(I),e.exit("tableDelimiterMarker"),m):I===45?(o+=1,m(I)):I===null||fe(I)?S(I):w(I)}function m(I){return I===45?(e.enter("tableDelimiterFiller"),y(I)):w(I)}function y(I){return I===45?(e.consume(I),y):I===58?(a=!0,e.exit("tableDelimiterFiller"),e.enter("tableDelimiterMarker"),e.consume(I),e.exit("tableDelimiterMarker"),T):(e.exit("tableDelimiterFiller"),T(I))}function T(I){return be(I)?Ie(e,S,"whitespace")(I):S(I)}function S(I){return I===124?v(I):I===null||fe(I)?!a||i!==o?w(I):(e.exit("tableDelimiterRow"),e.exit("tableHead"),t(I)):w(I)}function w(I){return n(I)}function b(I){return e.enter("tableRow"),A(I)}function A(I){return I===124?(e.enter("tableCellDivider"),e.consume(I),e.exit("tableCellDivider"),A):I===null||fe(I)?(e.exit("tableRow"),t(I)):be(I)?Ie(e,A,"whitespace")(I):(e.enter("data"),F(I))}function F(I){return I===null||I===124||We(I)?(e.exit("data"),A(I)):(e.consume(I),I===92?D:F)}function D(I){return I===92||I===124?(e.consume(I),F):F(I)}}function rU(e,t){let n=-1,r=!0,i=0,o=[0,0,0,0],a=[0,0,0,0],s=!1,l=0,u,c,f;const d=new Zz;for(;++nn[2]+1){const v=n[2]+1,g=n[3]-n[2]-1;e.add(v,g,[])}}e.add(n[3]+1,0,[["exit",f,t]])}return i!==void 0&&(o.end=Object.assign({},js(t.events,i)),e.add(i,0,[["exit",o,t]]),o=void 0),o}function x2(e,t,n,r,i){const o=[],a=js(t.events,n);i&&(i.end=Object.assign({},a),o.push(["exit",i,t])),r.end=Object.assign({},a),o.push(["exit",r,t]),e.add(n+1,0,o)}function js(e,t){const n=e[t],r=n[0]==="enter"?"start":"end";return n[1][r]}const iU={tokenize:aU},oU={text:{[91]:iU}};function aU(e,t,n){const r=this;return i;function i(l){return r.previous!==null||!r._gfmTasklistFirstContentOfListItem?n(l):(e.enter("taskListCheck"),e.enter("taskListCheckMarker"),e.consume(l),e.exit("taskListCheckMarker"),o)}function o(l){return We(l)?(e.enter("taskListCheckValueUnchecked"),e.consume(l),e.exit("taskListCheckValueUnchecked"),a):l===88||l===120?(e.enter("taskListCheckValueChecked"),e.consume(l),e.exit("taskListCheckValueChecked"),a):n(l)}function a(l){return l===93?(e.enter("taskListCheckMarker"),e.consume(l),e.exit("taskListCheckMarker"),e.exit("taskListCheck"),s):n(l)}function s(l){return fe(l)?t(l):be(l)?e.check({tokenize:sU},t,n)(l):n(l)}}function sU(e,t,n){return Ie(e,r,"whitespace");function r(i){return i===null?n(i):t(i)}}function lU(e){return C6([Dz,Wz(),Xz(e),tU,oU])}function N2(e,t){const n=String(e);if(typeof t!="string")throw new TypeError("Expected character");let r=0,i=n.indexOf(t);for(;i!==-1;)r++,i=n.indexOf(t,i+t.length);return r}function uU(e){if(typeof e!="string")throw new TypeError("Expected a string");return e.replace(/[|\\{}()[\]^$+*?.]/g,"\\$&").replace(/-/g,"\\x2d")}const cU={}.hasOwnProperty,fU=function(e,t,n,r){let i,o;typeof t=="string"||t instanceof RegExp?(o=[[t,n]],i=r):(o=t,i=n),i||(i={});const a=x9(i.ignore||[]),s=dU(o);let l=-1;for(;++l0?{type:"text",value:A}:void 0),A!==!1&&(C!==w&&T.push({type:"text",value:f.value.slice(C,w)}),Array.isArray(A)?T.push(...A):A&&T.push(A),C=w+S[0].length,y=!0),!v.global)break;S=v.exec(f.value)}return y?(Ce}const xm="phrasing",Nm=["autolink","link","image","label"],hU={transforms:[TU],enter:{literalAutolink:mU,literalAutolinkEmail:Fm,literalAutolinkHttp:Fm,literalAutolinkWww:Fm},exit:{literalAutolink:yU,literalAutolinkEmail:EU,literalAutolinkHttp:gU,literalAutolinkWww:vU}},pU={unsafe:[{character:"@",before:"[+\\-.\\w]",after:"[\\-.\\w]",inConstruct:xm,notInConstruct:Nm},{character:".",before:"[Ww]",after:"[\\-.\\w]",inConstruct:xm,notInConstruct:Nm},{character:":",before:"[ps]",after:"\\/",inConstruct:xm,notInConstruct:Nm}]};function mU(e){this.enter({type:"link",title:null,url:"",children:[]},e)}function Fm(e){this.config.enter.autolinkProtocol.call(this,e)}function gU(e){this.config.exit.autolinkProtocol.call(this,e)}function vU(e){this.config.exit.data.call(this,e);const t=this.stack[this.stack.length-1];t.url="http://"+this.sliceSerialize(e)}function EU(e){this.config.exit.autolinkEmail.call(this,e)}function yU(e){this.exit(e)}function TU(e){fU(e,[[/(https?:\/\/|www(?=\.))([-.\w]+)([^ \t\r\n]*)/gi,CU],[/([-.\w+]+)@([-\w]+(?:\.[-\w]+)+)/g,SU]],{ignore:["link","linkReference"]})}function CU(e,t,n,r,i){let o="";if(!Eb(i)||(/^w/i.test(t)&&(n=t+n,t="",o="http://"),!_U(n)))return!1;const a=bU(n+r);if(!a[0])return!1;const s={type:"link",title:null,url:o+t+a[0],children:[{type:"text",value:t+a[0]}]};return a[1]?[s,{type:"text",value:a[1]}]:s}function SU(e,t,n,r){return!Eb(r,!0)||/[-\d_]$/.test(n)?!1:{type:"link",title:null,url:"mailto:"+t+"@"+n,children:[{type:"text",value:t+"@"+n}]}}function _U(e){const t=e.split(".");return!(t.length<2||t[t.length-1]&&(/_/.test(t[t.length-1])||!/[a-zA-Z\d]/.test(t[t.length-1]))||t[t.length-2]&&(/_/.test(t[t.length-2])||!/[a-zA-Z\d]/.test(t[t.length-2])))}function bU(e){const t=/[!"&'),.:;<>?\]}]+$/.exec(e);if(!t)return[e,void 0];e=e.slice(0,t.index);let n=t[0],r=n.indexOf(")");const i=N2(e,"(");let o=N2(e,")");for(;r!==-1&&i>o;)e+=n.slice(0,r+1),n=n.slice(r+1),r=n.indexOf(")"),o++;return[e,n]}function Eb(e,t){const n=e.input.charCodeAt(e.index-1);return(e.index===0||ps(n)||Uh(n))&&(!t||n!==47)}function yb(e){return e.label||!e.identifier?e.label||"":R6(e.identifier)}function AU(e,t,n){const r=t.indexStack,i=e.children||[],o=t.createTracker(n),a=[];let s=-1;for(r.push(-1);++s + +`}return` + +`}const kU=/\r?\n|\r/g;function IU(e,t){const n=[];let r=0,i=0,o;for(;o=kU.exec(e);)a(e.slice(r,o.index)),n.push(o[0]),r=o.index+o[0].length,i++;return a(e.slice(r)),n.join("");function a(s){n.push(t(s,i,!s))}}function Tb(e){if(!e._compiled){const t=(e.atBreak?"[\\r\\n][\\t ]*":"")+(e.before?"(?:"+e.before+")":"");e._compiled=new RegExp((t?"("+t+")":"")+(/[|\\{}()[\]^$+*?.-]/.test(e.character)?"\\":"")+e.character+(e.after?"(?:"+e.after+")":""),"g")}return e._compiled}function xU(e,t){return D2(e,t.inConstruct,!0)&&!D2(e,t.notInConstruct,!1)}function D2(e,t,n){if(typeof t=="string"&&(t=[t]),!t||t.length===0)return n;let r=-1;for(;++r=u||c+10?" ":"")),i.shift(4),o+=i.move(IU(AU(e,n,i.current()),WU)),a(),o}function WU(e,t,n){return t===0?e:(n?"":" ")+e}function _b(e,t,n){const r=t.indexStack,i=e.children||[],o=[];let a=-1,s=n.before;r.push(-1);let l=t.createTracker(n);for(;++a0&&(s==="\r"||s===` +`)&&u.type==="html"&&(o[o.length-1]=o[o.length-1].replace(/(\r?\n|\r)$/," "),s=" ",l=t.createTracker(n),l.move(o.join(""))),o.push(l.move(t.handle(u,e,t,{...l.current(),before:s,after:c}))),s=o[o.length-1].slice(-1)}return r.pop(),o.join("")}const jU=["autolink","destinationLiteral","destinationRaw","reference","titleQuote","titleApostrophe"];bb.peek=YU;const GU={canContainEols:["delete"],enter:{strikethrough:VU},exit:{strikethrough:qU}},$U={unsafe:[{character:"~",inConstruct:"phrasing",notInConstruct:jU}],handlers:{delete:bb}};function VU(e){this.enter({type:"delete",children:[]},e)}function qU(e){this.exit(e)}function bb(e,t,n,r){const i=Xh(r),o=n.enter("strikethrough");let a=i.move("~~");return a+=_b(e,n,{...i.current(),before:a,after:"~"}),a+=i.move("~~"),o(),a}function YU(){return"~"}Ab.peek=QU;function Ab(e,t,n){let r=e.value||"",i="`",o=-1;for(;new RegExp("(^|[^`])"+i+"([^`]|$)").test(r);)i+="`";for(/[^ \r\n]/.test(r)&&(/^[ \r\n]/.test(r)&&/[ \r\n]$/.test(r)||/^`|`$/.test(r))&&(r=" "+r+" ");++ol&&(l=e[u].length);++Cs[C])&&(s[C]=y)}v.push(m)}o[u]=v,a[u]=g}let c=-1;if(typeof n=="object"&&"length"in n)for(;++cs[c]&&(s[c]=m),d[c]=m),f[c]=y}o.splice(1,0,f),a.splice(1,0,d),u=-1;const h=[];for(;++un==="none"?null:n),children:[]},e),this.setData("inTable",!0)}function nK(e){this.exit(e),this.setData("inTable")}function rK(e){this.enter({type:"tableRow",children:[]},e)}function Rm(e){this.exit(e)}function O2(e){this.enter({type:"tableCell",children:[]},e)}function iK(e){let t=this.resume();this.getData("inTable")&&(t=t.replace(/\\([\\|])/g,oK));const n=this.stack[this.stack.length-1];n.value=t,this.exit(e)}function oK(e,t){return t==="|"?t:e}function aK(e){const t=e||{},n=t.tableCellPadding,r=t.tablePipeAlign,i=t.stringLength,o=n?" ":"|";return{unsafe:[{character:"\r",inConstruct:"tableCell"},{character:` +`,inConstruct:"tableCell"},{atBreak:!0,character:"|",after:"[ :-]"},{character:"|",inConstruct:"tableCell"},{atBreak:!0,character:":",after:"-"},{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{table:a,tableRow:s,tableCell:l,inlineCode:d}};function a(h,v,g,C){return u(c(h,g,C),h.align)}function s(h,v,g,C){const m=f(h,g,C),y=u([m]);return y.slice(0,y.indexOf(` +`))}function l(h,v,g,C){const m=g.enter("tableCell"),y=g.enter("phrasing"),T=_b(h,g,{...C,before:o,after:o});return y(),m(),T}function u(h,v){return XU(h,{align:v,alignDelimiters:r,padding:n,stringLength:i})}function c(h,v,g){const C=h.children;let m=-1;const y=[],T=v.enter("table");for(;++m-1?t.start:1)+(n.options.incrementListMarker===!1?0:t.children.indexOf(e))+o);let a=o.length+1;(i==="tab"||i==="mixed"&&(t&&t.type==="list"&&t.spread||e.spread))&&(a=Math.ceil(a/4)*4);const s=n.createTracker(r);s.move(o+" ".repeat(a-o.length)),s.shift(a);const l=n.enter("listItem"),u=n.indentLines(n.containerFlow(e,s.current()),c);return l(),u;function c(f,d,h){return d?(h?"":" ".repeat(a))+f:(h?o:o+" ".repeat(a-o.length))+f}}const cK={exit:{taskListCheckValueChecked:L2,taskListCheckValueUnchecked:L2,paragraph:dK}},fK={unsafe:[{atBreak:!0,character:"-",after:"[:|-]"}],handlers:{listItem:hK}};function L2(e){const t=this.stack[this.stack.length-2];t.checked=e.type==="taskListCheckValueChecked"}function dK(e){const t=this.stack[this.stack.length-2];if(t&&t.type==="listItem"&&typeof t.checked=="boolean"){const n=this.stack[this.stack.length-1],r=n.children[0];if(r&&r.type==="text"){const i=t.children;let o=-1,a;for(;++o=55296&&e<=57343};ci.isSurrogatePair=function(e){return e>=56320&&e<=57343};ci.getSurrogatePairCodePoint=function(e,t){return(e-55296)*1024+9216+t};ci.isControlCodePoint=function(e){return e!==32&&e!==10&&e!==13&&e!==9&&e!==12&&e>=1&&e<=31||e>=127&&e<=159};ci.isUndefinedCodePoint=function(e){return e>=64976&&e<=65007||gK.indexOf(e)>-1};var M9={controlCharacterInInputStream:"control-character-in-input-stream",noncharacterInInputStream:"noncharacter-in-input-stream",surrogateInInputStream:"surrogate-in-input-stream",nonVoidHtmlElementStartTagWithTrailingSolidus:"non-void-html-element-start-tag-with-trailing-solidus",endTagWithAttributes:"end-tag-with-attributes",endTagWithTrailingSolidus:"end-tag-with-trailing-solidus",unexpectedSolidusInTag:"unexpected-solidus-in-tag",unexpectedNullCharacter:"unexpected-null-character",unexpectedQuestionMarkInsteadOfTagName:"unexpected-question-mark-instead-of-tag-name",invalidFirstCharacterOfTagName:"invalid-first-character-of-tag-name",unexpectedEqualsSignBeforeAttributeName:"unexpected-equals-sign-before-attribute-name",missingEndTagName:"missing-end-tag-name",unexpectedCharacterInAttributeName:"unexpected-character-in-attribute-name",unknownNamedCharacterReference:"unknown-named-character-reference",missingSemicolonAfterCharacterReference:"missing-semicolon-after-character-reference",unexpectedCharacterAfterDoctypeSystemIdentifier:"unexpected-character-after-doctype-system-identifier",unexpectedCharacterInUnquotedAttributeValue:"unexpected-character-in-unquoted-attribute-value",eofBeforeTagName:"eof-before-tag-name",eofInTag:"eof-in-tag",missingAttributeValue:"missing-attribute-value",missingWhitespaceBetweenAttributes:"missing-whitespace-between-attributes",missingWhitespaceAfterDoctypePublicKeyword:"missing-whitespace-after-doctype-public-keyword",missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers:"missing-whitespace-between-doctype-public-and-system-identifiers",missingWhitespaceAfterDoctypeSystemKeyword:"missing-whitespace-after-doctype-system-keyword",missingQuoteBeforeDoctypePublicIdentifier:"missing-quote-before-doctype-public-identifier",missingQuoteBeforeDoctypeSystemIdentifier:"missing-quote-before-doctype-system-identifier",missingDoctypePublicIdentifier:"missing-doctype-public-identifier",missingDoctypeSystemIdentifier:"missing-doctype-system-identifier",abruptDoctypePublicIdentifier:"abrupt-doctype-public-identifier",abruptDoctypeSystemIdentifier:"abrupt-doctype-system-identifier",cdataInHtmlContent:"cdata-in-html-content",incorrectlyOpenedComment:"incorrectly-opened-comment",eofInScriptHtmlCommentLikeText:"eof-in-script-html-comment-like-text",eofInDoctype:"eof-in-doctype",nestedComment:"nested-comment",abruptClosingOfEmptyComment:"abrupt-closing-of-empty-comment",eofInComment:"eof-in-comment",incorrectlyClosedComment:"incorrectly-closed-comment",eofInCdata:"eof-in-cdata",absenceOfDigitsInNumericCharacterReference:"absence-of-digits-in-numeric-character-reference",nullCharacterReference:"null-character-reference",surrogateCharacterReference:"surrogate-character-reference",characterReferenceOutsideUnicodeRange:"character-reference-outside-unicode-range",controlCharacterReference:"control-character-reference",noncharacterCharacterReference:"noncharacter-character-reference",missingWhitespaceBeforeDoctypeName:"missing-whitespace-before-doctype-name",missingDoctypeName:"missing-doctype-name",invalidCharacterSequenceAfterDoctypeName:"invalid-character-sequence-after-doctype-name",duplicateAttribute:"duplicate-attribute",nonConformingDoctype:"non-conforming-doctype",missingDoctype:"missing-doctype",misplacedDoctype:"misplaced-doctype",endTagWithoutMatchingOpenElement:"end-tag-without-matching-open-element",closingOfElementWithOpenChildElements:"closing-of-element-with-open-child-elements",disallowedContentInNoscriptInHead:"disallowed-content-in-noscript-in-head",openElementsLeftAfterEof:"open-elements-left-after-eof",abandonedHeadElementChild:"abandoned-head-element-child",misplacedStartTagForHeadElement:"misplaced-start-tag-for-head-element",nestedNoscriptInHead:"nested-noscript-in-head",eofInElementThatCanContainOnlyText:"eof-in-element-that-can-contain-only-text"};const Gs=ci,Dm=M9,La=Gs.CODE_POINTS,vK=65536;let EK=class{constructor(){this.html=null,this.pos=-1,this.lastGapPos=-1,this.lastCharPos=-1,this.gapStack=[],this.skipNextNewLine=!1,this.lastChunkWritten=!1,this.endOfChunkHit=!1,this.bufferWaterline=vK}_err(){}_addGap(){this.gapStack.push(this.lastGapPos),this.lastGapPos=this.pos}_processSurrogate(t){if(this.pos!==this.lastCharPos){const n=this.html.charCodeAt(this.pos+1);if(Gs.isSurrogatePair(n))return this.pos++,this._addGap(),Gs.getSurrogatePairCodePoint(t,n)}else if(!this.lastChunkWritten)return this.endOfChunkHit=!0,La.EOF;return this._err(Dm.surrogateInInputStream),t}dropParsedChunk(){this.pos>this.bufferWaterline&&(this.lastCharPos-=this.pos,this.html=this.html.substring(this.pos),this.pos=0,this.lastGapPos=-1,this.gapStack=[])}write(t,n){this.html?this.html+=t:this.html=t,this.lastCharPos=this.html.length-1,this.endOfChunkHit=!1,this.lastChunkWritten=n}insertHtmlAtCurrentPos(t){this.html=this.html.substring(0,this.pos+1)+t+this.html.substring(this.pos+1,this.html.length),this.lastCharPos=this.html.length-1,this.endOfChunkHit=!1}advance(){if(this.pos++,this.pos>this.lastCharPos)return this.endOfChunkHit=!this.lastChunkWritten,La.EOF;let t=this.html.charCodeAt(this.pos);return this.skipNextNewLine&&t===La.LINE_FEED?(this.skipNextNewLine=!1,this._addGap(),this.advance()):t===La.CARRIAGE_RETURN?(this.skipNextNewLine=!0,La.LINE_FEED):(this.skipNextNewLine=!1,Gs.isSurrogate(t)&&(t=this._processSurrogate(t)),t>31&&t<127||t===La.LINE_FEED||t===La.CARRIAGE_RETURN||t>159&&t<64976||this._checkForProblematicCharacters(t),t)}_checkForProblematicCharacters(t){Gs.isControlCodePoint(t)?this._err(Dm.controlCharacterInInputStream):Gs.isUndefinedCodePoint(t)&&this._err(Dm.noncharacterInInputStream)}retreat(){this.pos===this.lastGapPos&&(this.lastGapPos=this.gapStack.pop(),this.pos--),this.pos--}};var yK=EK,TK=new Uint16Array([4,52,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,106,303,412,810,1432,1701,1796,1987,2114,2360,2420,2484,3170,3251,4140,4393,4575,4610,5106,5512,5728,6117,6274,6315,6345,6427,6516,7002,7910,8733,9323,9870,10170,10631,10893,11318,11386,11467,12773,13092,14474,14922,15448,15542,16419,17666,18166,18611,19004,19095,19298,19397,4,16,69,77,97,98,99,102,103,108,109,110,111,112,114,115,116,117,140,150,158,169,176,194,199,210,216,222,226,242,256,266,283,294,108,105,103,5,198,1,59,148,1,198,80,5,38,1,59,156,1,38,99,117,116,101,5,193,1,59,167,1,193,114,101,118,101,59,1,258,4,2,105,121,182,191,114,99,5,194,1,59,189,1,194,59,1,1040,114,59,3,55349,56580,114,97,118,101,5,192,1,59,208,1,192,112,104,97,59,1,913,97,99,114,59,1,256,100,59,1,10835,4,2,103,112,232,237,111,110,59,1,260,102,59,3,55349,56632,112,108,121,70,117,110,99,116,105,111,110,59,1,8289,105,110,103,5,197,1,59,264,1,197,4,2,99,115,272,277,114,59,3,55349,56476,105,103,110,59,1,8788,105,108,100,101,5,195,1,59,292,1,195,109,108,5,196,1,59,301,1,196,4,8,97,99,101,102,111,114,115,117,321,350,354,383,388,394,400,405,4,2,99,114,327,336,107,115,108,97,115,104,59,1,8726,4,2,118,119,342,345,59,1,10983,101,100,59,1,8966,121,59,1,1041,4,3,99,114,116,362,369,379,97,117,115,101,59,1,8757,110,111,117,108,108,105,115,59,1,8492,97,59,1,914,114,59,3,55349,56581,112,102,59,3,55349,56633,101,118,101,59,1,728,99,114,59,1,8492,109,112,101,113,59,1,8782,4,14,72,79,97,99,100,101,102,104,105,108,111,114,115,117,442,447,456,504,542,547,569,573,577,616,678,784,790,796,99,121,59,1,1063,80,89,5,169,1,59,454,1,169,4,3,99,112,121,464,470,497,117,116,101,59,1,262,4,2,59,105,476,478,1,8914,116,97,108,68,105,102,102,101,114,101,110,116,105,97,108,68,59,1,8517,108,101,121,115,59,1,8493,4,4,97,101,105,111,514,520,530,535,114,111,110,59,1,268,100,105,108,5,199,1,59,528,1,199,114,99,59,1,264,110,105,110,116,59,1,8752,111,116,59,1,266,4,2,100,110,553,560,105,108,108,97,59,1,184,116,101,114,68,111,116,59,1,183,114,59,1,8493,105,59,1,935,114,99,108,101,4,4,68,77,80,84,591,596,603,609,111,116,59,1,8857,105,110,117,115,59,1,8854,108,117,115,59,1,8853,105,109,101,115,59,1,8855,111,4,2,99,115,623,646,107,119,105,115,101,67,111,110,116,111,117,114,73,110,116,101,103,114,97,108,59,1,8754,101,67,117,114,108,121,4,2,68,81,658,671,111,117,98,108,101,81,117,111,116,101,59,1,8221,117,111,116,101,59,1,8217,4,4,108,110,112,117,688,701,736,753,111,110,4,2,59,101,696,698,1,8759,59,1,10868,4,3,103,105,116,709,717,722,114,117,101,110,116,59,1,8801,110,116,59,1,8751,111,117,114,73,110,116,101,103,114,97,108,59,1,8750,4,2,102,114,742,745,59,1,8450,111,100,117,99,116,59,1,8720,110,116,101,114,67,108,111,99,107,119,105,115,101,67,111,110,116,111,117,114,73,110,116,101,103,114,97,108,59,1,8755,111,115,115,59,1,10799,99,114,59,3,55349,56478,112,4,2,59,67,803,805,1,8915,97,112,59,1,8781,4,11,68,74,83,90,97,99,101,102,105,111,115,834,850,855,860,865,888,903,916,921,1011,1415,4,2,59,111,840,842,1,8517,116,114,97,104,100,59,1,10513,99,121,59,1,1026,99,121,59,1,1029,99,121,59,1,1039,4,3,103,114,115,873,879,883,103,101,114,59,1,8225,114,59,1,8609,104,118,59,1,10980,4,2,97,121,894,900,114,111,110,59,1,270,59,1,1044,108,4,2,59,116,910,912,1,8711,97,59,1,916,114,59,3,55349,56583,4,2,97,102,927,998,4,2,99,109,933,992,114,105,116,105,99,97,108,4,4,65,68,71,84,950,957,978,985,99,117,116,101,59,1,180,111,4,2,116,117,964,967,59,1,729,98,108,101,65,99,117,116,101,59,1,733,114,97,118,101,59,1,96,105,108,100,101,59,1,732,111,110,100,59,1,8900,102,101,114,101,110,116,105,97,108,68,59,1,8518,4,4,112,116,117,119,1021,1026,1048,1249,102,59,3,55349,56635,4,3,59,68,69,1034,1036,1041,1,168,111,116,59,1,8412,113,117,97,108,59,1,8784,98,108,101,4,6,67,68,76,82,85,86,1065,1082,1101,1189,1211,1236,111,110,116,111,117,114,73,110,116,101,103,114,97,108,59,1,8751,111,4,2,116,119,1089,1092,59,1,168,110,65,114,114,111,119,59,1,8659,4,2,101,111,1107,1141,102,116,4,3,65,82,84,1117,1124,1136,114,114,111,119,59,1,8656,105,103,104,116,65,114,114,111,119,59,1,8660,101,101,59,1,10980,110,103,4,2,76,82,1149,1177,101,102,116,4,2,65,82,1158,1165,114,114,111,119,59,1,10232,105,103,104,116,65,114,114,111,119,59,1,10234,105,103,104,116,65,114,114,111,119,59,1,10233,105,103,104,116,4,2,65,84,1199,1206,114,114,111,119,59,1,8658,101,101,59,1,8872,112,4,2,65,68,1218,1225,114,114,111,119,59,1,8657,111,119,110,65,114,114,111,119,59,1,8661,101,114,116,105,99,97,108,66,97,114,59,1,8741,110,4,6,65,66,76,82,84,97,1264,1292,1299,1352,1391,1408,114,114,111,119,4,3,59,66,85,1276,1278,1283,1,8595,97,114,59,1,10515,112,65,114,114,111,119,59,1,8693,114,101,118,101,59,1,785,101,102,116,4,3,82,84,86,1310,1323,1334,105,103,104,116,86,101,99,116,111,114,59,1,10576,101,101,86,101,99,116,111,114,59,1,10590,101,99,116,111,114,4,2,59,66,1345,1347,1,8637,97,114,59,1,10582,105,103,104,116,4,2,84,86,1362,1373,101,101,86,101,99,116,111,114,59,1,10591,101,99,116,111,114,4,2,59,66,1384,1386,1,8641,97,114,59,1,10583,101,101,4,2,59,65,1399,1401,1,8868,114,114,111,119,59,1,8615,114,114,111,119,59,1,8659,4,2,99,116,1421,1426,114,59,3,55349,56479,114,111,107,59,1,272,4,16,78,84,97,99,100,102,103,108,109,111,112,113,115,116,117,120,1466,1470,1478,1489,1515,1520,1525,1536,1544,1593,1609,1617,1650,1664,1668,1677,71,59,1,330,72,5,208,1,59,1476,1,208,99,117,116,101,5,201,1,59,1487,1,201,4,3,97,105,121,1497,1503,1512,114,111,110,59,1,282,114,99,5,202,1,59,1510,1,202,59,1,1069,111,116,59,1,278,114,59,3,55349,56584,114,97,118,101,5,200,1,59,1534,1,200,101,109,101,110,116,59,1,8712,4,2,97,112,1550,1555,99,114,59,1,274,116,121,4,2,83,86,1563,1576,109,97,108,108,83,113,117,97,114,101,59,1,9723,101,114,121,83,109,97,108,108,83,113,117,97,114,101,59,1,9643,4,2,103,112,1599,1604,111,110,59,1,280,102,59,3,55349,56636,115,105,108,111,110,59,1,917,117,4,2,97,105,1624,1640,108,4,2,59,84,1631,1633,1,10869,105,108,100,101,59,1,8770,108,105,98,114,105,117,109,59,1,8652,4,2,99,105,1656,1660,114,59,1,8496,109,59,1,10867,97,59,1,919,109,108,5,203,1,59,1675,1,203,4,2,105,112,1683,1689,115,116,115,59,1,8707,111,110,101,110,116,105,97,108,69,59,1,8519,4,5,99,102,105,111,115,1713,1717,1722,1762,1791,121,59,1,1060,114,59,3,55349,56585,108,108,101,100,4,2,83,86,1732,1745,109,97,108,108,83,113,117,97,114,101,59,1,9724,101,114,121,83,109,97,108,108,83,113,117,97,114,101,59,1,9642,4,3,112,114,117,1770,1775,1781,102,59,3,55349,56637,65,108,108,59,1,8704,114,105,101,114,116,114,102,59,1,8497,99,114,59,1,8497,4,12,74,84,97,98,99,100,102,103,111,114,115,116,1822,1827,1834,1848,1855,1877,1882,1887,1890,1896,1978,1984,99,121,59,1,1027,5,62,1,59,1832,1,62,109,109,97,4,2,59,100,1843,1845,1,915,59,1,988,114,101,118,101,59,1,286,4,3,101,105,121,1863,1869,1874,100,105,108,59,1,290,114,99,59,1,284,59,1,1043,111,116,59,1,288,114,59,3,55349,56586,59,1,8921,112,102,59,3,55349,56638,101,97,116,101,114,4,6,69,70,71,76,83,84,1915,1933,1944,1953,1959,1971,113,117,97,108,4,2,59,76,1925,1927,1,8805,101,115,115,59,1,8923,117,108,108,69,113,117,97,108,59,1,8807,114,101,97,116,101,114,59,1,10914,101,115,115,59,1,8823,108,97,110,116,69,113,117,97,108,59,1,10878,105,108,100,101,59,1,8819,99,114,59,3,55349,56482,59,1,8811,4,8,65,97,99,102,105,111,115,117,2005,2012,2026,2032,2036,2049,2073,2089,82,68,99,121,59,1,1066,4,2,99,116,2018,2023,101,107,59,1,711,59,1,94,105,114,99,59,1,292,114,59,1,8460,108,98,101,114,116,83,112,97,99,101,59,1,8459,4,2,112,114,2055,2059,102,59,1,8461,105,122,111,110,116,97,108,76,105,110,101,59,1,9472,4,2,99,116,2079,2083,114,59,1,8459,114,111,107,59,1,294,109,112,4,2,68,69,2097,2107,111,119,110,72,117,109,112,59,1,8782,113,117,97,108,59,1,8783,4,14,69,74,79,97,99,100,102,103,109,110,111,115,116,117,2144,2149,2155,2160,2171,2189,2194,2198,2209,2245,2307,2329,2334,2341,99,121,59,1,1045,108,105,103,59,1,306,99,121,59,1,1025,99,117,116,101,5,205,1,59,2169,1,205,4,2,105,121,2177,2186,114,99,5,206,1,59,2184,1,206,59,1,1048,111,116,59,1,304,114,59,1,8465,114,97,118,101,5,204,1,59,2207,1,204,4,3,59,97,112,2217,2219,2238,1,8465,4,2,99,103,2225,2229,114,59,1,298,105,110,97,114,121,73,59,1,8520,108,105,101,115,59,1,8658,4,2,116,118,2251,2281,4,2,59,101,2257,2259,1,8748,4,2,103,114,2265,2271,114,97,108,59,1,8747,115,101,99,116,105,111,110,59,1,8898,105,115,105,98,108,101,4,2,67,84,2293,2300,111,109,109,97,59,1,8291,105,109,101,115,59,1,8290,4,3,103,112,116,2315,2320,2325,111,110,59,1,302,102,59,3,55349,56640,97,59,1,921,99,114,59,1,8464,105,108,100,101,59,1,296,4,2,107,109,2347,2352,99,121,59,1,1030,108,5,207,1,59,2358,1,207,4,5,99,102,111,115,117,2372,2386,2391,2397,2414,4,2,105,121,2378,2383,114,99,59,1,308,59,1,1049,114,59,3,55349,56589,112,102,59,3,55349,56641,4,2,99,101,2403,2408,114,59,3,55349,56485,114,99,121,59,1,1032,107,99,121,59,1,1028,4,7,72,74,97,99,102,111,115,2436,2441,2446,2452,2467,2472,2478,99,121,59,1,1061,99,121,59,1,1036,112,112,97,59,1,922,4,2,101,121,2458,2464,100,105,108,59,1,310,59,1,1050,114,59,3,55349,56590,112,102,59,3,55349,56642,99,114,59,3,55349,56486,4,11,74,84,97,99,101,102,108,109,111,115,116,2508,2513,2520,2562,2585,2981,2986,3004,3011,3146,3167,99,121,59,1,1033,5,60,1,59,2518,1,60,4,5,99,109,110,112,114,2532,2538,2544,2548,2558,117,116,101,59,1,313,98,100,97,59,1,923,103,59,1,10218,108,97,99,101,116,114,102,59,1,8466,114,59,1,8606,4,3,97,101,121,2570,2576,2582,114,111,110,59,1,317,100,105,108,59,1,315,59,1,1051,4,2,102,115,2591,2907,116,4,10,65,67,68,70,82,84,85,86,97,114,2614,2663,2672,2728,2735,2760,2820,2870,2888,2895,4,2,110,114,2620,2633,103,108,101,66,114,97,99,107,101,116,59,1,10216,114,111,119,4,3,59,66,82,2644,2646,2651,1,8592,97,114,59,1,8676,105,103,104,116,65,114,114,111,119,59,1,8646,101,105,108,105,110,103,59,1,8968,111,4,2,117,119,2679,2692,98,108,101,66,114,97,99,107,101,116,59,1,10214,110,4,2,84,86,2699,2710,101,101,86,101,99,116,111,114,59,1,10593,101,99,116,111,114,4,2,59,66,2721,2723,1,8643,97,114,59,1,10585,108,111,111,114,59,1,8970,105,103,104,116,4,2,65,86,2745,2752,114,114,111,119,59,1,8596,101,99,116,111,114,59,1,10574,4,2,101,114,2766,2792,101,4,3,59,65,86,2775,2777,2784,1,8867,114,114,111,119,59,1,8612,101,99,116,111,114,59,1,10586,105,97,110,103,108,101,4,3,59,66,69,2806,2808,2813,1,8882,97,114,59,1,10703,113,117,97,108,59,1,8884,112,4,3,68,84,86,2829,2841,2852,111,119,110,86,101,99,116,111,114,59,1,10577,101,101,86,101,99,116,111,114,59,1,10592,101,99,116,111,114,4,2,59,66,2863,2865,1,8639,97,114,59,1,10584,101,99,116,111,114,4,2,59,66,2881,2883,1,8636,97,114,59,1,10578,114,114,111,119,59,1,8656,105,103,104,116,97,114,114,111,119,59,1,8660,115,4,6,69,70,71,76,83,84,2922,2936,2947,2956,2962,2974,113,117,97,108,71,114,101,97,116,101,114,59,1,8922,117,108,108,69,113,117,97,108,59,1,8806,114,101,97,116,101,114,59,1,8822,101,115,115,59,1,10913,108,97,110,116,69,113,117,97,108,59,1,10877,105,108,100,101,59,1,8818,114,59,3,55349,56591,4,2,59,101,2992,2994,1,8920,102,116,97,114,114,111,119,59,1,8666,105,100,111,116,59,1,319,4,3,110,112,119,3019,3110,3115,103,4,4,76,82,108,114,3030,3058,3070,3098,101,102,116,4,2,65,82,3039,3046,114,114,111,119,59,1,10229,105,103,104,116,65,114,114,111,119,59,1,10231,105,103,104,116,65,114,114,111,119,59,1,10230,101,102,116,4,2,97,114,3079,3086,114,114,111,119,59,1,10232,105,103,104,116,97,114,114,111,119,59,1,10234,105,103,104,116,97,114,114,111,119,59,1,10233,102,59,3,55349,56643,101,114,4,2,76,82,3123,3134,101,102,116,65,114,114,111,119,59,1,8601,105,103,104,116,65,114,114,111,119,59,1,8600,4,3,99,104,116,3154,3158,3161,114,59,1,8466,59,1,8624,114,111,107,59,1,321,59,1,8810,4,8,97,99,101,102,105,111,115,117,3188,3192,3196,3222,3227,3237,3243,3248,112,59,1,10501,121,59,1,1052,4,2,100,108,3202,3213,105,117,109,83,112,97,99,101,59,1,8287,108,105,110,116,114,102,59,1,8499,114,59,3,55349,56592,110,117,115,80,108,117,115,59,1,8723,112,102,59,3,55349,56644,99,114,59,1,8499,59,1,924,4,9,74,97,99,101,102,111,115,116,117,3271,3276,3283,3306,3422,3427,4120,4126,4137,99,121,59,1,1034,99,117,116,101,59,1,323,4,3,97,101,121,3291,3297,3303,114,111,110,59,1,327,100,105,108,59,1,325,59,1,1053,4,3,103,115,119,3314,3380,3415,97,116,105,118,101,4,3,77,84,86,3327,3340,3365,101,100,105,117,109,83,112,97,99,101,59,1,8203,104,105,4,2,99,110,3348,3357,107,83,112,97,99,101,59,1,8203,83,112,97,99,101,59,1,8203,101,114,121,84,104,105,110,83,112,97,99,101,59,1,8203,116,101,100,4,2,71,76,3389,3405,114,101,97,116,101,114,71,114,101,97,116,101,114,59,1,8811,101,115,115,76,101,115,115,59,1,8810,76,105,110,101,59,1,10,114,59,3,55349,56593,4,4,66,110,112,116,3437,3444,3460,3464,114,101,97,107,59,1,8288,66,114,101,97,107,105,110,103,83,112,97,99,101,59,1,160,102,59,1,8469,4,13,59,67,68,69,71,72,76,78,80,82,83,84,86,3492,3494,3517,3536,3578,3657,3685,3784,3823,3860,3915,4066,4107,1,10988,4,2,111,117,3500,3510,110,103,114,117,101,110,116,59,1,8802,112,67,97,112,59,1,8813,111,117,98,108,101,86,101,114,116,105,99,97,108,66,97,114,59,1,8742,4,3,108,113,120,3544,3552,3571,101,109,101,110,116,59,1,8713,117,97,108,4,2,59,84,3561,3563,1,8800,105,108,100,101,59,3,8770,824,105,115,116,115,59,1,8708,114,101,97,116,101,114,4,7,59,69,70,71,76,83,84,3600,3602,3609,3621,3631,3637,3650,1,8815,113,117,97,108,59,1,8817,117,108,108,69,113,117,97,108,59,3,8807,824,114,101,97,116,101,114,59,3,8811,824,101,115,115,59,1,8825,108,97,110,116,69,113,117,97,108,59,3,10878,824,105,108,100,101,59,1,8821,117,109,112,4,2,68,69,3666,3677,111,119,110,72,117,109,112,59,3,8782,824,113,117,97,108,59,3,8783,824,101,4,2,102,115,3692,3724,116,84,114,105,97,110,103,108,101,4,3,59,66,69,3709,3711,3717,1,8938,97,114,59,3,10703,824,113,117,97,108,59,1,8940,115,4,6,59,69,71,76,83,84,3739,3741,3748,3757,3764,3777,1,8814,113,117,97,108,59,1,8816,114,101,97,116,101,114,59,1,8824,101,115,115,59,3,8810,824,108,97,110,116,69,113,117,97,108,59,3,10877,824,105,108,100,101,59,1,8820,101,115,116,101,100,4,2,71,76,3795,3812,114,101,97,116,101,114,71,114,101,97,116,101,114,59,3,10914,824,101,115,115,76,101,115,115,59,3,10913,824,114,101,99,101,100,101,115,4,3,59,69,83,3838,3840,3848,1,8832,113,117,97,108,59,3,10927,824,108,97,110,116,69,113,117,97,108,59,1,8928,4,2,101,105,3866,3881,118,101,114,115,101,69,108,101,109,101,110,116,59,1,8716,103,104,116,84,114,105,97,110,103,108,101,4,3,59,66,69,3900,3902,3908,1,8939,97,114,59,3,10704,824,113,117,97,108,59,1,8941,4,2,113,117,3921,3973,117,97,114,101,83,117,4,2,98,112,3933,3952,115,101,116,4,2,59,69,3942,3945,3,8847,824,113,117,97,108,59,1,8930,101,114,115,101,116,4,2,59,69,3963,3966,3,8848,824,113,117,97,108,59,1,8931,4,3,98,99,112,3981,4e3,4045,115,101,116,4,2,59,69,3990,3993,3,8834,8402,113,117,97,108,59,1,8840,99,101,101,100,115,4,4,59,69,83,84,4015,4017,4025,4037,1,8833,113,117,97,108,59,3,10928,824,108,97,110,116,69,113,117,97,108,59,1,8929,105,108,100,101,59,3,8831,824,101,114,115,101,116,4,2,59,69,4056,4059,3,8835,8402,113,117,97,108,59,1,8841,105,108,100,101,4,4,59,69,70,84,4080,4082,4089,4100,1,8769,113,117,97,108,59,1,8772,117,108,108,69,113,117,97,108,59,1,8775,105,108,100,101,59,1,8777,101,114,116,105,99,97,108,66,97,114,59,1,8740,99,114,59,3,55349,56489,105,108,100,101,5,209,1,59,4135,1,209,59,1,925,4,14,69,97,99,100,102,103,109,111,112,114,115,116,117,118,4170,4176,4187,4205,4212,4217,4228,4253,4259,4292,4295,4316,4337,4346,108,105,103,59,1,338,99,117,116,101,5,211,1,59,4185,1,211,4,2,105,121,4193,4202,114,99,5,212,1,59,4200,1,212,59,1,1054,98,108,97,99,59,1,336,114,59,3,55349,56594,114,97,118,101,5,210,1,59,4226,1,210,4,3,97,101,105,4236,4241,4246,99,114,59,1,332,103,97,59,1,937,99,114,111,110,59,1,927,112,102,59,3,55349,56646,101,110,67,117,114,108,121,4,2,68,81,4272,4285,111,117,98,108,101,81,117,111,116,101,59,1,8220,117,111,116,101,59,1,8216,59,1,10836,4,2,99,108,4301,4306,114,59,3,55349,56490,97,115,104,5,216,1,59,4314,1,216,105,4,2,108,109,4323,4332,100,101,5,213,1,59,4330,1,213,101,115,59,1,10807,109,108,5,214,1,59,4344,1,214,101,114,4,2,66,80,4354,4380,4,2,97,114,4360,4364,114,59,1,8254,97,99,4,2,101,107,4372,4375,59,1,9182,101,116,59,1,9140,97,114,101,110,116,104,101,115,105,115,59,1,9180,4,9,97,99,102,104,105,108,111,114,115,4413,4422,4426,4431,4435,4438,4448,4471,4561,114,116,105,97,108,68,59,1,8706,121,59,1,1055,114,59,3,55349,56595,105,59,1,934,59,1,928,117,115,77,105,110,117,115,59,1,177,4,2,105,112,4454,4467,110,99,97,114,101,112,108,97,110,101,59,1,8460,102,59,1,8473,4,4,59,101,105,111,4481,4483,4526,4531,1,10939,99,101,100,101,115,4,4,59,69,83,84,4498,4500,4507,4519,1,8826,113,117,97,108,59,1,10927,108,97,110,116,69,113,117,97,108,59,1,8828,105,108,100,101,59,1,8830,109,101,59,1,8243,4,2,100,112,4537,4543,117,99,116,59,1,8719,111,114,116,105,111,110,4,2,59,97,4555,4557,1,8759,108,59,1,8733,4,2,99,105,4567,4572,114,59,3,55349,56491,59,1,936,4,4,85,102,111,115,4585,4594,4599,4604,79,84,5,34,1,59,4592,1,34,114,59,3,55349,56596,112,102,59,1,8474,99,114,59,3,55349,56492,4,12,66,69,97,99,101,102,104,105,111,114,115,117,4636,4642,4650,4681,4704,4763,4767,4771,5047,5069,5081,5094,97,114,114,59,1,10512,71,5,174,1,59,4648,1,174,4,3,99,110,114,4658,4664,4668,117,116,101,59,1,340,103,59,1,10219,114,4,2,59,116,4675,4677,1,8608,108,59,1,10518,4,3,97,101,121,4689,4695,4701,114,111,110,59,1,344,100,105,108,59,1,342,59,1,1056,4,2,59,118,4710,4712,1,8476,101,114,115,101,4,2,69,85,4722,4748,4,2,108,113,4728,4736,101,109,101,110,116,59,1,8715,117,105,108,105,98,114,105,117,109,59,1,8651,112,69,113,117,105,108,105,98,114,105,117,109,59,1,10607,114,59,1,8476,111,59,1,929,103,104,116,4,8,65,67,68,70,84,85,86,97,4792,4840,4849,4905,4912,4972,5022,5040,4,2,110,114,4798,4811,103,108,101,66,114,97,99,107,101,116,59,1,10217,114,111,119,4,3,59,66,76,4822,4824,4829,1,8594,97,114,59,1,8677,101,102,116,65,114,114,111,119,59,1,8644,101,105,108,105,110,103,59,1,8969,111,4,2,117,119,4856,4869,98,108,101,66,114,97,99,107,101,116,59,1,10215,110,4,2,84,86,4876,4887,101,101,86,101,99,116,111,114,59,1,10589,101,99,116,111,114,4,2,59,66,4898,4900,1,8642,97,114,59,1,10581,108,111,111,114,59,1,8971,4,2,101,114,4918,4944,101,4,3,59,65,86,4927,4929,4936,1,8866,114,114,111,119,59,1,8614,101,99,116,111,114,59,1,10587,105,97,110,103,108,101,4,3,59,66,69,4958,4960,4965,1,8883,97,114,59,1,10704,113,117,97,108,59,1,8885,112,4,3,68,84,86,4981,4993,5004,111,119,110,86,101,99,116,111,114,59,1,10575,101,101,86,101,99,116,111,114,59,1,10588,101,99,116,111,114,4,2,59,66,5015,5017,1,8638,97,114,59,1,10580,101,99,116,111,114,4,2,59,66,5033,5035,1,8640,97,114,59,1,10579,114,114,111,119,59,1,8658,4,2,112,117,5053,5057,102,59,1,8477,110,100,73,109,112,108,105,101,115,59,1,10608,105,103,104,116,97,114,114,111,119,59,1,8667,4,2,99,104,5087,5091,114,59,1,8475,59,1,8625,108,101,68,101,108,97,121,101,100,59,1,10740,4,13,72,79,97,99,102,104,105,109,111,113,115,116,117,5134,5150,5157,5164,5198,5203,5259,5265,5277,5283,5374,5380,5385,4,2,67,99,5140,5146,72,99,121,59,1,1065,121,59,1,1064,70,84,99,121,59,1,1068,99,117,116,101,59,1,346,4,5,59,97,101,105,121,5176,5178,5184,5190,5195,1,10940,114,111,110,59,1,352,100,105,108,59,1,350,114,99,59,1,348,59,1,1057,114,59,3,55349,56598,111,114,116,4,4,68,76,82,85,5216,5227,5238,5250,111,119,110,65,114,114,111,119,59,1,8595,101,102,116,65,114,114,111,119,59,1,8592,105,103,104,116,65,114,114,111,119,59,1,8594,112,65,114,114,111,119,59,1,8593,103,109,97,59,1,931,97,108,108,67,105,114,99,108,101,59,1,8728,112,102,59,3,55349,56650,4,2,114,117,5289,5293,116,59,1,8730,97,114,101,4,4,59,73,83,85,5306,5308,5322,5367,1,9633,110,116,101,114,115,101,99,116,105,111,110,59,1,8851,117,4,2,98,112,5329,5347,115,101,116,4,2,59,69,5338,5340,1,8847,113,117,97,108,59,1,8849,101,114,115,101,116,4,2,59,69,5358,5360,1,8848,113,117,97,108,59,1,8850,110,105,111,110,59,1,8852,99,114,59,3,55349,56494,97,114,59,1,8902,4,4,98,99,109,112,5395,5420,5475,5478,4,2,59,115,5401,5403,1,8912,101,116,4,2,59,69,5411,5413,1,8912,113,117,97,108,59,1,8838,4,2,99,104,5426,5468,101,101,100,115,4,4,59,69,83,84,5440,5442,5449,5461,1,8827,113,117,97,108,59,1,10928,108,97,110,116,69,113,117,97,108,59,1,8829,105,108,100,101,59,1,8831,84,104,97,116,59,1,8715,59,1,8721,4,3,59,101,115,5486,5488,5507,1,8913,114,115,101,116,4,2,59,69,5498,5500,1,8835,113,117,97,108,59,1,8839,101,116,59,1,8913,4,11,72,82,83,97,99,102,104,105,111,114,115,5536,5546,5552,5567,5579,5602,5607,5655,5695,5701,5711,79,82,78,5,222,1,59,5544,1,222,65,68,69,59,1,8482,4,2,72,99,5558,5563,99,121,59,1,1035,121,59,1,1062,4,2,98,117,5573,5576,59,1,9,59,1,932,4,3,97,101,121,5587,5593,5599,114,111,110,59,1,356,100,105,108,59,1,354,59,1,1058,114,59,3,55349,56599,4,2,101,105,5613,5631,4,2,114,116,5619,5627,101,102,111,114,101,59,1,8756,97,59,1,920,4,2,99,110,5637,5647,107,83,112,97,99,101,59,3,8287,8202,83,112,97,99,101,59,1,8201,108,100,101,4,4,59,69,70,84,5668,5670,5677,5688,1,8764,113,117,97,108,59,1,8771,117,108,108,69,113,117,97,108,59,1,8773,105,108,100,101,59,1,8776,112,102,59,3,55349,56651,105,112,108,101,68,111,116,59,1,8411,4,2,99,116,5717,5722,114,59,3,55349,56495,114,111,107,59,1,358,4,14,97,98,99,100,102,103,109,110,111,112,114,115,116,117,5758,5789,5805,5823,5830,5835,5846,5852,5921,5937,6089,6095,6101,6108,4,2,99,114,5764,5774,117,116,101,5,218,1,59,5772,1,218,114,4,2,59,111,5781,5783,1,8607,99,105,114,59,1,10569,114,4,2,99,101,5796,5800,121,59,1,1038,118,101,59,1,364,4,2,105,121,5811,5820,114,99,5,219,1,59,5818,1,219,59,1,1059,98,108,97,99,59,1,368,114,59,3,55349,56600,114,97,118,101,5,217,1,59,5844,1,217,97,99,114,59,1,362,4,2,100,105,5858,5905,101,114,4,2,66,80,5866,5892,4,2,97,114,5872,5876,114,59,1,95,97,99,4,2,101,107,5884,5887,59,1,9183,101,116,59,1,9141,97,114,101,110,116,104,101,115,105,115,59,1,9181,111,110,4,2,59,80,5913,5915,1,8899,108,117,115,59,1,8846,4,2,103,112,5927,5932,111,110,59,1,370,102,59,3,55349,56652,4,8,65,68,69,84,97,100,112,115,5955,5985,5996,6009,6026,6033,6044,6075,114,114,111,119,4,3,59,66,68,5967,5969,5974,1,8593,97,114,59,1,10514,111,119,110,65,114,114,111,119,59,1,8645,111,119,110,65,114,114,111,119,59,1,8597,113,117,105,108,105,98,114,105,117,109,59,1,10606,101,101,4,2,59,65,6017,6019,1,8869,114,114,111,119,59,1,8613,114,114,111,119,59,1,8657,111,119,110,97,114,114,111,119,59,1,8661,101,114,4,2,76,82,6052,6063,101,102,116,65,114,114,111,119,59,1,8598,105,103,104,116,65,114,114,111,119,59,1,8599,105,4,2,59,108,6082,6084,1,978,111,110,59,1,933,105,110,103,59,1,366,99,114,59,3,55349,56496,105,108,100,101,59,1,360,109,108,5,220,1,59,6115,1,220,4,9,68,98,99,100,101,102,111,115,118,6137,6143,6148,6152,6166,6250,6255,6261,6267,97,115,104,59,1,8875,97,114,59,1,10987,121,59,1,1042,97,115,104,4,2,59,108,6161,6163,1,8873,59,1,10982,4,2,101,114,6172,6175,59,1,8897,4,3,98,116,121,6183,6188,6238,97,114,59,1,8214,4,2,59,105,6194,6196,1,8214,99,97,108,4,4,66,76,83,84,6209,6214,6220,6231,97,114,59,1,8739,105,110,101,59,1,124,101,112,97,114,97,116,111,114,59,1,10072,105,108,100,101,59,1,8768,84,104,105,110,83,112,97,99,101,59,1,8202,114,59,3,55349,56601,112,102,59,3,55349,56653,99,114,59,3,55349,56497,100,97,115,104,59,1,8874,4,5,99,101,102,111,115,6286,6292,6298,6303,6309,105,114,99,59,1,372,100,103,101,59,1,8896,114,59,3,55349,56602,112,102,59,3,55349,56654,99,114,59,3,55349,56498,4,4,102,105,111,115,6325,6330,6333,6339,114,59,3,55349,56603,59,1,926,112,102,59,3,55349,56655,99,114,59,3,55349,56499,4,9,65,73,85,97,99,102,111,115,117,6365,6370,6375,6380,6391,6405,6410,6416,6422,99,121,59,1,1071,99,121,59,1,1031,99,121,59,1,1070,99,117,116,101,5,221,1,59,6389,1,221,4,2,105,121,6397,6402,114,99,59,1,374,59,1,1067,114,59,3,55349,56604,112,102,59,3,55349,56656,99,114,59,3,55349,56500,109,108,59,1,376,4,8,72,97,99,100,101,102,111,115,6445,6450,6457,6472,6477,6501,6505,6510,99,121,59,1,1046,99,117,116,101,59,1,377,4,2,97,121,6463,6469,114,111,110,59,1,381,59,1,1047,111,116,59,1,379,4,2,114,116,6483,6497,111,87,105,100,116,104,83,112,97,99,101,59,1,8203,97,59,1,918,114,59,1,8488,112,102,59,1,8484,99,114,59,3,55349,56501,4,16,97,98,99,101,102,103,108,109,110,111,112,114,115,116,117,119,6550,6561,6568,6612,6622,6634,6645,6672,6699,6854,6870,6923,6933,6963,6974,6983,99,117,116,101,5,225,1,59,6559,1,225,114,101,118,101,59,1,259,4,6,59,69,100,105,117,121,6582,6584,6588,6591,6600,6609,1,8766,59,3,8766,819,59,1,8767,114,99,5,226,1,59,6598,1,226,116,101,5,180,1,59,6607,1,180,59,1,1072,108,105,103,5,230,1,59,6620,1,230,4,2,59,114,6628,6630,1,8289,59,3,55349,56606,114,97,118,101,5,224,1,59,6643,1,224,4,2,101,112,6651,6667,4,2,102,112,6657,6663,115,121,109,59,1,8501,104,59,1,8501,104,97,59,1,945,4,2,97,112,6678,6692,4,2,99,108,6684,6688,114,59,1,257,103,59,1,10815,5,38,1,59,6697,1,38,4,2,100,103,6705,6737,4,5,59,97,100,115,118,6717,6719,6724,6727,6734,1,8743,110,100,59,1,10837,59,1,10844,108,111,112,101,59,1,10840,59,1,10842,4,7,59,101,108,109,114,115,122,6753,6755,6758,6762,6814,6835,6848,1,8736,59,1,10660,101,59,1,8736,115,100,4,2,59,97,6770,6772,1,8737,4,8,97,98,99,100,101,102,103,104,6790,6793,6796,6799,6802,6805,6808,6811,59,1,10664,59,1,10665,59,1,10666,59,1,10667,59,1,10668,59,1,10669,59,1,10670,59,1,10671,116,4,2,59,118,6821,6823,1,8735,98,4,2,59,100,6830,6832,1,8894,59,1,10653,4,2,112,116,6841,6845,104,59,1,8738,59,1,197,97,114,114,59,1,9084,4,2,103,112,6860,6865,111,110,59,1,261,102,59,3,55349,56658,4,7,59,69,97,101,105,111,112,6886,6888,6891,6897,6900,6904,6908,1,8776,59,1,10864,99,105,114,59,1,10863,59,1,8778,100,59,1,8779,115,59,1,39,114,111,120,4,2,59,101,6917,6919,1,8776,113,59,1,8778,105,110,103,5,229,1,59,6931,1,229,4,3,99,116,121,6941,6946,6949,114,59,3,55349,56502,59,1,42,109,112,4,2,59,101,6957,6959,1,8776,113,59,1,8781,105,108,100,101,5,227,1,59,6972,1,227,109,108,5,228,1,59,6981,1,228,4,2,99,105,6989,6997,111,110,105,110,116,59,1,8755,110,116,59,1,10769,4,16,78,97,98,99,100,101,102,105,107,108,110,111,112,114,115,117,7036,7041,7119,7135,7149,7155,7219,7224,7347,7354,7463,7489,7786,7793,7814,7866,111,116,59,1,10989,4,2,99,114,7047,7094,107,4,4,99,101,112,115,7058,7064,7073,7080,111,110,103,59,1,8780,112,115,105,108,111,110,59,1,1014,114,105,109,101,59,1,8245,105,109,4,2,59,101,7088,7090,1,8765,113,59,1,8909,4,2,118,119,7100,7105,101,101,59,1,8893,101,100,4,2,59,103,7113,7115,1,8965,101,59,1,8965,114,107,4,2,59,116,7127,7129,1,9141,98,114,107,59,1,9142,4,2,111,121,7141,7146,110,103,59,1,8780,59,1,1073,113,117,111,59,1,8222,4,5,99,109,112,114,116,7167,7181,7188,7193,7199,97,117,115,4,2,59,101,7176,7178,1,8757,59,1,8757,112,116,121,118,59,1,10672,115,105,59,1,1014,110,111,117,59,1,8492,4,3,97,104,119,7207,7210,7213,59,1,946,59,1,8502,101,101,110,59,1,8812,114,59,3,55349,56607,103,4,7,99,111,115,116,117,118,119,7241,7262,7288,7305,7328,7335,7340,4,3,97,105,117,7249,7253,7258,112,59,1,8898,114,99,59,1,9711,112,59,1,8899,4,3,100,112,116,7270,7275,7281,111,116,59,1,10752,108,117,115,59,1,10753,105,109,101,115,59,1,10754,4,2,113,116,7294,7300,99,117,112,59,1,10758,97,114,59,1,9733,114,105,97,110,103,108,101,4,2,100,117,7318,7324,111,119,110,59,1,9661,112,59,1,9651,112,108,117,115,59,1,10756,101,101,59,1,8897,101,100,103,101,59,1,8896,97,114,111,119,59,1,10509,4,3,97,107,111,7362,7436,7458,4,2,99,110,7368,7432,107,4,3,108,115,116,7377,7386,7394,111,122,101,110,103,101,59,1,10731,113,117,97,114,101,59,1,9642,114,105,97,110,103,108,101,4,4,59,100,108,114,7411,7413,7419,7425,1,9652,111,119,110,59,1,9662,101,102,116,59,1,9666,105,103,104,116,59,1,9656,107,59,1,9251,4,2,49,51,7442,7454,4,2,50,52,7448,7451,59,1,9618,59,1,9617,52,59,1,9619,99,107,59,1,9608,4,2,101,111,7469,7485,4,2,59,113,7475,7478,3,61,8421,117,105,118,59,3,8801,8421,116,59,1,8976,4,4,112,116,119,120,7499,7504,7517,7523,102,59,3,55349,56659,4,2,59,116,7510,7512,1,8869,111,109,59,1,8869,116,105,101,59,1,8904,4,12,68,72,85,86,98,100,104,109,112,116,117,118,7549,7571,7597,7619,7655,7660,7682,7708,7715,7721,7728,7750,4,4,76,82,108,114,7559,7562,7565,7568,59,1,9559,59,1,9556,59,1,9558,59,1,9555,4,5,59,68,85,100,117,7583,7585,7588,7591,7594,1,9552,59,1,9574,59,1,9577,59,1,9572,59,1,9575,4,4,76,82,108,114,7607,7610,7613,7616,59,1,9565,59,1,9562,59,1,9564,59,1,9561,4,7,59,72,76,82,104,108,114,7635,7637,7640,7643,7646,7649,7652,1,9553,59,1,9580,59,1,9571,59,1,9568,59,1,9579,59,1,9570,59,1,9567,111,120,59,1,10697,4,4,76,82,108,114,7670,7673,7676,7679,59,1,9557,59,1,9554,59,1,9488,59,1,9484,4,5,59,68,85,100,117,7694,7696,7699,7702,7705,1,9472,59,1,9573,59,1,9576,59,1,9516,59,1,9524,105,110,117,115,59,1,8863,108,117,115,59,1,8862,105,109,101,115,59,1,8864,4,4,76,82,108,114,7738,7741,7744,7747,59,1,9563,59,1,9560,59,1,9496,59,1,9492,4,7,59,72,76,82,104,108,114,7766,7768,7771,7774,7777,7780,7783,1,9474,59,1,9578,59,1,9569,59,1,9566,59,1,9532,59,1,9508,59,1,9500,114,105,109,101,59,1,8245,4,2,101,118,7799,7804,118,101,59,1,728,98,97,114,5,166,1,59,7812,1,166,4,4,99,101,105,111,7824,7829,7834,7846,114,59,3,55349,56503,109,105,59,1,8271,109,4,2,59,101,7841,7843,1,8765,59,1,8909,108,4,3,59,98,104,7855,7857,7860,1,92,59,1,10693,115,117,98,59,1,10184,4,2,108,109,7872,7885,108,4,2,59,101,7879,7881,1,8226,116,59,1,8226,112,4,3,59,69,101,7894,7896,7899,1,8782,59,1,10926,4,2,59,113,7905,7907,1,8783,59,1,8783,4,15,97,99,100,101,102,104,105,108,111,114,115,116,117,119,121,7942,8021,8075,8080,8121,8126,8157,8279,8295,8430,8446,8485,8491,8707,8726,4,3,99,112,114,7950,7956,8007,117,116,101,59,1,263,4,6,59,97,98,99,100,115,7970,7972,7977,7984,7998,8003,1,8745,110,100,59,1,10820,114,99,117,112,59,1,10825,4,2,97,117,7990,7994,112,59,1,10827,112,59,1,10823,111,116,59,1,10816,59,3,8745,65024,4,2,101,111,8013,8017,116,59,1,8257,110,59,1,711,4,4,97,101,105,117,8031,8046,8056,8061,4,2,112,114,8037,8041,115,59,1,10829,111,110,59,1,269,100,105,108,5,231,1,59,8054,1,231,114,99,59,1,265,112,115,4,2,59,115,8069,8071,1,10828,109,59,1,10832,111,116,59,1,267,4,3,100,109,110,8088,8097,8104,105,108,5,184,1,59,8095,1,184,112,116,121,118,59,1,10674,116,5,162,2,59,101,8112,8114,1,162,114,100,111,116,59,1,183,114,59,3,55349,56608,4,3,99,101,105,8134,8138,8154,121,59,1,1095,99,107,4,2,59,109,8146,8148,1,10003,97,114,107,59,1,10003,59,1,967,114,4,7,59,69,99,101,102,109,115,8174,8176,8179,8258,8261,8268,8273,1,9675,59,1,10691,4,3,59,101,108,8187,8189,8193,1,710,113,59,1,8791,101,4,2,97,100,8200,8223,114,114,111,119,4,2,108,114,8210,8216,101,102,116,59,1,8634,105,103,104,116,59,1,8635,4,5,82,83,97,99,100,8235,8238,8241,8246,8252,59,1,174,59,1,9416,115,116,59,1,8859,105,114,99,59,1,8858,97,115,104,59,1,8861,59,1,8791,110,105,110,116,59,1,10768,105,100,59,1,10991,99,105,114,59,1,10690,117,98,115,4,2,59,117,8288,8290,1,9827,105,116,59,1,9827,4,4,108,109,110,112,8305,8326,8376,8400,111,110,4,2,59,101,8313,8315,1,58,4,2,59,113,8321,8323,1,8788,59,1,8788,4,2,109,112,8332,8344,97,4,2,59,116,8339,8341,1,44,59,1,64,4,3,59,102,108,8352,8354,8358,1,8705,110,59,1,8728,101,4,2,109,120,8365,8371,101,110,116,59,1,8705,101,115,59,1,8450,4,2,103,105,8382,8395,4,2,59,100,8388,8390,1,8773,111,116,59,1,10861,110,116,59,1,8750,4,3,102,114,121,8408,8412,8417,59,3,55349,56660,111,100,59,1,8720,5,169,2,59,115,8424,8426,1,169,114,59,1,8471,4,2,97,111,8436,8441,114,114,59,1,8629,115,115,59,1,10007,4,2,99,117,8452,8457,114,59,3,55349,56504,4,2,98,112,8463,8474,4,2,59,101,8469,8471,1,10959,59,1,10961,4,2,59,101,8480,8482,1,10960,59,1,10962,100,111,116,59,1,8943,4,7,100,101,108,112,114,118,119,8507,8522,8536,8550,8600,8697,8702,97,114,114,4,2,108,114,8516,8519,59,1,10552,59,1,10549,4,2,112,115,8528,8532,114,59,1,8926,99,59,1,8927,97,114,114,4,2,59,112,8545,8547,1,8630,59,1,10557,4,6,59,98,99,100,111,115,8564,8566,8573,8587,8592,8596,1,8746,114,99,97,112,59,1,10824,4,2,97,117,8579,8583,112,59,1,10822,112,59,1,10826,111,116,59,1,8845,114,59,1,10821,59,3,8746,65024,4,4,97,108,114,118,8610,8623,8663,8672,114,114,4,2,59,109,8618,8620,1,8631,59,1,10556,121,4,3,101,118,119,8632,8651,8656,113,4,2,112,115,8639,8645,114,101,99,59,1,8926,117,99,99,59,1,8927,101,101,59,1,8910,101,100,103,101,59,1,8911,101,110,5,164,1,59,8670,1,164,101,97,114,114,111,119,4,2,108,114,8684,8690,101,102,116,59,1,8630,105,103,104,116,59,1,8631,101,101,59,1,8910,101,100,59,1,8911,4,2,99,105,8713,8721,111,110,105,110,116,59,1,8754,110,116,59,1,8753,108,99,116,121,59,1,9005,4,19,65,72,97,98,99,100,101,102,104,105,106,108,111,114,115,116,117,119,122,8773,8778,8783,8821,8839,8854,8887,8914,8930,8944,9036,9041,9058,9197,9227,9258,9281,9297,9305,114,114,59,1,8659,97,114,59,1,10597,4,4,103,108,114,115,8793,8799,8805,8809,103,101,114,59,1,8224,101,116,104,59,1,8504,114,59,1,8595,104,4,2,59,118,8816,8818,1,8208,59,1,8867,4,2,107,108,8827,8834,97,114,111,119,59,1,10511,97,99,59,1,733,4,2,97,121,8845,8851,114,111,110,59,1,271,59,1,1076,4,3,59,97,111,8862,8864,8880,1,8518,4,2,103,114,8870,8876,103,101,114,59,1,8225,114,59,1,8650,116,115,101,113,59,1,10871,4,3,103,108,109,8895,8902,8907,5,176,1,59,8900,1,176,116,97,59,1,948,112,116,121,118,59,1,10673,4,2,105,114,8920,8926,115,104,116,59,1,10623,59,3,55349,56609,97,114,4,2,108,114,8938,8941,59,1,8643,59,1,8642,4,5,97,101,103,115,118,8956,8986,8989,8996,9001,109,4,3,59,111,115,8965,8967,8983,1,8900,110,100,4,2,59,115,8975,8977,1,8900,117,105,116,59,1,9830,59,1,9830,59,1,168,97,109,109,97,59,1,989,105,110,59,1,8946,4,3,59,105,111,9009,9011,9031,1,247,100,101,5,247,2,59,111,9020,9022,1,247,110,116,105,109,101,115,59,1,8903,110,120,59,1,8903,99,121,59,1,1106,99,4,2,111,114,9048,9053,114,110,59,1,8990,111,112,59,1,8973,4,5,108,112,116,117,119,9070,9076,9081,9130,9144,108,97,114,59,1,36,102,59,3,55349,56661,4,5,59,101,109,112,115,9093,9095,9109,9116,9122,1,729,113,4,2,59,100,9102,9104,1,8784,111,116,59,1,8785,105,110,117,115,59,1,8760,108,117,115,59,1,8724,113,117,97,114,101,59,1,8865,98,108,101,98,97,114,119,101,100,103,101,59,1,8966,110,4,3,97,100,104,9153,9160,9172,114,114,111,119,59,1,8595,111,119,110,97,114,114,111,119,115,59,1,8650,97,114,112,111,111,110,4,2,108,114,9184,9190,101,102,116,59,1,8643,105,103,104,116,59,1,8642,4,2,98,99,9203,9211,107,97,114,111,119,59,1,10512,4,2,111,114,9217,9222,114,110,59,1,8991,111,112,59,1,8972,4,3,99,111,116,9235,9248,9252,4,2,114,121,9241,9245,59,3,55349,56505,59,1,1109,108,59,1,10742,114,111,107,59,1,273,4,2,100,114,9264,9269,111,116,59,1,8945,105,4,2,59,102,9276,9278,1,9663,59,1,9662,4,2,97,104,9287,9292,114,114,59,1,8693,97,114,59,1,10607,97,110,103,108,101,59,1,10662,4,2,99,105,9311,9315,121,59,1,1119,103,114,97,114,114,59,1,10239,4,18,68,97,99,100,101,102,103,108,109,110,111,112,113,114,115,116,117,120,9361,9376,9398,9439,9444,9447,9462,9495,9531,9585,9598,9614,9659,9755,9771,9792,9808,9826,4,2,68,111,9367,9372,111,116,59,1,10871,116,59,1,8785,4,2,99,115,9382,9392,117,116,101,5,233,1,59,9390,1,233,116,101,114,59,1,10862,4,4,97,105,111,121,9408,9414,9430,9436,114,111,110,59,1,283,114,4,2,59,99,9421,9423,1,8790,5,234,1,59,9428,1,234,108,111,110,59,1,8789,59,1,1101,111,116,59,1,279,59,1,8519,4,2,68,114,9453,9458,111,116,59,1,8786,59,3,55349,56610,4,3,59,114,115,9470,9472,9482,1,10906,97,118,101,5,232,1,59,9480,1,232,4,2,59,100,9488,9490,1,10902,111,116,59,1,10904,4,4,59,105,108,115,9505,9507,9515,9518,1,10905,110,116,101,114,115,59,1,9191,59,1,8467,4,2,59,100,9524,9526,1,10901,111,116,59,1,10903,4,3,97,112,115,9539,9544,9564,99,114,59,1,275,116,121,4,3,59,115,118,9554,9556,9561,1,8709,101,116,59,1,8709,59,1,8709,112,4,2,49,59,9571,9583,4,2,51,52,9577,9580,59,1,8196,59,1,8197,1,8195,4,2,103,115,9591,9594,59,1,331,112,59,1,8194,4,2,103,112,9604,9609,111,110,59,1,281,102,59,3,55349,56662,4,3,97,108,115,9622,9635,9640,114,4,2,59,115,9629,9631,1,8917,108,59,1,10723,117,115,59,1,10865,105,4,3,59,108,118,9649,9651,9656,1,949,111,110,59,1,949,59,1,1013,4,4,99,115,117,118,9669,9686,9716,9747,4,2,105,111,9675,9680,114,99,59,1,8790,108,111,110,59,1,8789,4,2,105,108,9692,9696,109,59,1,8770,97,110,116,4,2,103,108,9705,9710,116,114,59,1,10902,101,115,115,59,1,10901,4,3,97,101,105,9724,9729,9734,108,115,59,1,61,115,116,59,1,8799,118,4,2,59,68,9741,9743,1,8801,68,59,1,10872,112,97,114,115,108,59,1,10725,4,2,68,97,9761,9766,111,116,59,1,8787,114,114,59,1,10609,4,3,99,100,105,9779,9783,9788,114,59,1,8495,111,116,59,1,8784,109,59,1,8770,4,2,97,104,9798,9801,59,1,951,5,240,1,59,9806,1,240,4,2,109,114,9814,9822,108,5,235,1,59,9820,1,235,111,59,1,8364,4,3,99,105,112,9834,9838,9843,108,59,1,33,115,116,59,1,8707,4,2,101,111,9849,9859,99,116,97,116,105,111,110,59,1,8496,110,101,110,116,105,97,108,101,59,1,8519,4,12,97,99,101,102,105,106,108,110,111,112,114,115,9896,9910,9914,9921,9954,9960,9967,9989,9994,10027,10036,10164,108,108,105,110,103,100,111,116,115,101,113,59,1,8786,121,59,1,1092,109,97,108,101,59,1,9792,4,3,105,108,114,9929,9935,9950,108,105,103,59,1,64259,4,2,105,108,9941,9945,103,59,1,64256,105,103,59,1,64260,59,3,55349,56611,108,105,103,59,1,64257,108,105,103,59,3,102,106,4,3,97,108,116,9975,9979,9984,116,59,1,9837,105,103,59,1,64258,110,115,59,1,9649,111,102,59,1,402,4,2,112,114,1e4,10005,102,59,3,55349,56663,4,2,97,107,10011,10016,108,108,59,1,8704,4,2,59,118,10022,10024,1,8916,59,1,10969,97,114,116,105,110,116,59,1,10765,4,2,97,111,10042,10159,4,2,99,115,10048,10155,4,6,49,50,51,52,53,55,10062,10102,10114,10135,10139,10151,4,6,50,51,52,53,54,56,10076,10083,10086,10093,10096,10099,5,189,1,59,10081,1,189,59,1,8531,5,188,1,59,10091,1,188,59,1,8533,59,1,8537,59,1,8539,4,2,51,53,10108,10111,59,1,8532,59,1,8534,4,3,52,53,56,10122,10129,10132,5,190,1,59,10127,1,190,59,1,8535,59,1,8540,53,59,1,8536,4,2,54,56,10145,10148,59,1,8538,59,1,8541,56,59,1,8542,108,59,1,8260,119,110,59,1,8994,99,114,59,3,55349,56507,4,17,69,97,98,99,100,101,102,103,105,106,108,110,111,114,115,116,118,10206,10217,10247,10254,10268,10273,10358,10363,10374,10380,10385,10406,10458,10464,10470,10497,10610,4,2,59,108,10212,10214,1,8807,59,1,10892,4,3,99,109,112,10225,10231,10244,117,116,101,59,1,501,109,97,4,2,59,100,10239,10241,1,947,59,1,989,59,1,10886,114,101,118,101,59,1,287,4,2,105,121,10260,10265,114,99,59,1,285,59,1,1075,111,116,59,1,289,4,4,59,108,113,115,10283,10285,10288,10308,1,8805,59,1,8923,4,3,59,113,115,10296,10298,10301,1,8805,59,1,8807,108,97,110,116,59,1,10878,4,4,59,99,100,108,10318,10320,10324,10345,1,10878,99,59,1,10921,111,116,4,2,59,111,10332,10334,1,10880,4,2,59,108,10340,10342,1,10882,59,1,10884,4,2,59,101,10351,10354,3,8923,65024,115,59,1,10900,114,59,3,55349,56612,4,2,59,103,10369,10371,1,8811,59,1,8921,109,101,108,59,1,8503,99,121,59,1,1107,4,4,59,69,97,106,10395,10397,10400,10403,1,8823,59,1,10898,59,1,10917,59,1,10916,4,4,69,97,101,115,10416,10419,10434,10453,59,1,8809,112,4,2,59,112,10426,10428,1,10890,114,111,120,59,1,10890,4,2,59,113,10440,10442,1,10888,4,2,59,113,10448,10450,1,10888,59,1,8809,105,109,59,1,8935,112,102,59,3,55349,56664,97,118,101,59,1,96,4,2,99,105,10476,10480,114,59,1,8458,109,4,3,59,101,108,10489,10491,10494,1,8819,59,1,10894,59,1,10896,5,62,6,59,99,100,108,113,114,10512,10514,10527,10532,10538,10545,1,62,4,2,99,105,10520,10523,59,1,10919,114,59,1,10874,111,116,59,1,8919,80,97,114,59,1,10645,117,101,115,116,59,1,10876,4,5,97,100,101,108,115,10557,10574,10579,10599,10605,4,2,112,114,10563,10570,112,114,111,120,59,1,10886,114,59,1,10616,111,116,59,1,8919,113,4,2,108,113,10586,10592,101,115,115,59,1,8923,108,101,115,115,59,1,10892,101,115,115,59,1,8823,105,109,59,1,8819,4,2,101,110,10616,10626,114,116,110,101,113,113,59,3,8809,65024,69,59,3,8809,65024,4,10,65,97,98,99,101,102,107,111,115,121,10653,10658,10713,10718,10724,10760,10765,10786,10850,10875,114,114,59,1,8660,4,4,105,108,109,114,10668,10674,10678,10684,114,115,112,59,1,8202,102,59,1,189,105,108,116,59,1,8459,4,2,100,114,10690,10695,99,121,59,1,1098,4,3,59,99,119,10703,10705,10710,1,8596,105,114,59,1,10568,59,1,8621,97,114,59,1,8463,105,114,99,59,1,293,4,3,97,108,114,10732,10748,10754,114,116,115,4,2,59,117,10741,10743,1,9829,105,116,59,1,9829,108,105,112,59,1,8230,99,111,110,59,1,8889,114,59,3,55349,56613,115,4,2,101,119,10772,10779,97,114,111,119,59,1,10533,97,114,111,119,59,1,10534,4,5,97,109,111,112,114,10798,10803,10809,10839,10844,114,114,59,1,8703,116,104,116,59,1,8763,107,4,2,108,114,10816,10827,101,102,116,97,114,114,111,119,59,1,8617,105,103,104,116,97,114,114,111,119,59,1,8618,102,59,3,55349,56665,98,97,114,59,1,8213,4,3,99,108,116,10858,10863,10869,114,59,3,55349,56509,97,115,104,59,1,8463,114,111,107,59,1,295,4,2,98,112,10881,10887,117,108,108,59,1,8259,104,101,110,59,1,8208,4,15,97,99,101,102,103,105,106,109,110,111,112,113,115,116,117,10925,10936,10958,10977,10990,11001,11039,11045,11101,11192,11220,11226,11237,11285,11299,99,117,116,101,5,237,1,59,10934,1,237,4,3,59,105,121,10944,10946,10955,1,8291,114,99,5,238,1,59,10953,1,238,59,1,1080,4,2,99,120,10964,10968,121,59,1,1077,99,108,5,161,1,59,10975,1,161,4,2,102,114,10983,10986,59,1,8660,59,3,55349,56614,114,97,118,101,5,236,1,59,10999,1,236,4,4,59,105,110,111,11011,11013,11028,11034,1,8520,4,2,105,110,11019,11024,110,116,59,1,10764,116,59,1,8749,102,105,110,59,1,10716,116,97,59,1,8489,108,105,103,59,1,307,4,3,97,111,112,11053,11092,11096,4,3,99,103,116,11061,11065,11088,114,59,1,299,4,3,101,108,112,11073,11076,11082,59,1,8465,105,110,101,59,1,8464,97,114,116,59,1,8465,104,59,1,305,102,59,1,8887,101,100,59,1,437,4,5,59,99,102,111,116,11113,11115,11121,11136,11142,1,8712,97,114,101,59,1,8453,105,110,4,2,59,116,11129,11131,1,8734,105,101,59,1,10717,100,111,116,59,1,305,4,5,59,99,101,108,112,11154,11156,11161,11179,11186,1,8747,97,108,59,1,8890,4,2,103,114,11167,11173,101,114,115,59,1,8484,99,97,108,59,1,8890,97,114,104,107,59,1,10775,114,111,100,59,1,10812,4,4,99,103,112,116,11202,11206,11211,11216,121,59,1,1105,111,110,59,1,303,102,59,3,55349,56666,97,59,1,953,114,111,100,59,1,10812,117,101,115,116,5,191,1,59,11235,1,191,4,2,99,105,11243,11248,114,59,3,55349,56510,110,4,5,59,69,100,115,118,11261,11263,11266,11271,11282,1,8712,59,1,8953,111,116,59,1,8949,4,2,59,118,11277,11279,1,8948,59,1,8947,59,1,8712,4,2,59,105,11291,11293,1,8290,108,100,101,59,1,297,4,2,107,109,11305,11310,99,121,59,1,1110,108,5,239,1,59,11316,1,239,4,6,99,102,109,111,115,117,11332,11346,11351,11357,11363,11380,4,2,105,121,11338,11343,114,99,59,1,309,59,1,1081,114,59,3,55349,56615,97,116,104,59,1,567,112,102,59,3,55349,56667,4,2,99,101,11369,11374,114,59,3,55349,56511,114,99,121,59,1,1112,107,99,121,59,1,1108,4,8,97,99,102,103,104,106,111,115,11404,11418,11433,11438,11445,11450,11455,11461,112,112,97,4,2,59,118,11413,11415,1,954,59,1,1008,4,2,101,121,11424,11430,100,105,108,59,1,311,59,1,1082,114,59,3,55349,56616,114,101,101,110,59,1,312,99,121,59,1,1093,99,121,59,1,1116,112,102,59,3,55349,56668,99,114,59,3,55349,56512,4,23,65,66,69,72,97,98,99,100,101,102,103,104,106,108,109,110,111,112,114,115,116,117,118,11515,11538,11544,11555,11560,11721,11780,11818,11868,12136,12160,12171,12203,12208,12246,12275,12327,12509,12523,12569,12641,12732,12752,4,3,97,114,116,11523,11528,11532,114,114,59,1,8666,114,59,1,8656,97,105,108,59,1,10523,97,114,114,59,1,10510,4,2,59,103,11550,11552,1,8806,59,1,10891,97,114,59,1,10594,4,9,99,101,103,109,110,112,113,114,116,11580,11586,11594,11600,11606,11624,11627,11636,11694,117,116,101,59,1,314,109,112,116,121,118,59,1,10676,114,97,110,59,1,8466,98,100,97,59,1,955,103,4,3,59,100,108,11615,11617,11620,1,10216,59,1,10641,101,59,1,10216,59,1,10885,117,111,5,171,1,59,11634,1,171,114,4,8,59,98,102,104,108,112,115,116,11655,11657,11669,11673,11677,11681,11685,11690,1,8592,4,2,59,102,11663,11665,1,8676,115,59,1,10527,115,59,1,10525,107,59,1,8617,112,59,1,8619,108,59,1,10553,105,109,59,1,10611,108,59,1,8610,4,3,59,97,101,11702,11704,11709,1,10923,105,108,59,1,10521,4,2,59,115,11715,11717,1,10925,59,3,10925,65024,4,3,97,98,114,11729,11734,11739,114,114,59,1,10508,114,107,59,1,10098,4,2,97,107,11745,11758,99,4,2,101,107,11752,11755,59,1,123,59,1,91,4,2,101,115,11764,11767,59,1,10635,108,4,2,100,117,11774,11777,59,1,10639,59,1,10637,4,4,97,101,117,121,11790,11796,11811,11815,114,111,110,59,1,318,4,2,100,105,11802,11807,105,108,59,1,316,108,59,1,8968,98,59,1,123,59,1,1083,4,4,99,113,114,115,11828,11832,11845,11864,97,59,1,10550,117,111,4,2,59,114,11840,11842,1,8220,59,1,8222,4,2,100,117,11851,11857,104,97,114,59,1,10599,115,104,97,114,59,1,10571,104,59,1,8626,4,5,59,102,103,113,115,11880,11882,12008,12011,12031,1,8804,116,4,5,97,104,108,114,116,11895,11913,11935,11947,11996,114,114,111,119,4,2,59,116,11905,11907,1,8592,97,105,108,59,1,8610,97,114,112,111,111,110,4,2,100,117,11925,11931,111,119,110,59,1,8637,112,59,1,8636,101,102,116,97,114,114,111,119,115,59,1,8647,105,103,104,116,4,3,97,104,115,11959,11974,11984,114,114,111,119,4,2,59,115,11969,11971,1,8596,59,1,8646,97,114,112,111,111,110,115,59,1,8651,113,117,105,103,97,114,114,111,119,59,1,8621,104,114,101,101,116,105,109,101,115,59,1,8907,59,1,8922,4,3,59,113,115,12019,12021,12024,1,8804,59,1,8806,108,97,110,116,59,1,10877,4,5,59,99,100,103,115,12043,12045,12049,12070,12083,1,10877,99,59,1,10920,111,116,4,2,59,111,12057,12059,1,10879,4,2,59,114,12065,12067,1,10881,59,1,10883,4,2,59,101,12076,12079,3,8922,65024,115,59,1,10899,4,5,97,100,101,103,115,12095,12103,12108,12126,12131,112,112,114,111,120,59,1,10885,111,116,59,1,8918,113,4,2,103,113,12115,12120,116,114,59,1,8922,103,116,114,59,1,10891,116,114,59,1,8822,105,109,59,1,8818,4,3,105,108,114,12144,12150,12156,115,104,116,59,1,10620,111,111,114,59,1,8970,59,3,55349,56617,4,2,59,69,12166,12168,1,8822,59,1,10897,4,2,97,98,12177,12198,114,4,2,100,117,12184,12187,59,1,8637,4,2,59,108,12193,12195,1,8636,59,1,10602,108,107,59,1,9604,99,121,59,1,1113,4,5,59,97,99,104,116,12220,12222,12227,12235,12241,1,8810,114,114,59,1,8647,111,114,110,101,114,59,1,8990,97,114,100,59,1,10603,114,105,59,1,9722,4,2,105,111,12252,12258,100,111,116,59,1,320,117,115,116,4,2,59,97,12267,12269,1,9136,99,104,101,59,1,9136,4,4,69,97,101,115,12285,12288,12303,12322,59,1,8808,112,4,2,59,112,12295,12297,1,10889,114,111,120,59,1,10889,4,2,59,113,12309,12311,1,10887,4,2,59,113,12317,12319,1,10887,59,1,8808,105,109,59,1,8934,4,8,97,98,110,111,112,116,119,122,12345,12359,12364,12421,12446,12467,12474,12490,4,2,110,114,12351,12355,103,59,1,10220,114,59,1,8701,114,107,59,1,10214,103,4,3,108,109,114,12373,12401,12409,101,102,116,4,2,97,114,12382,12389,114,114,111,119,59,1,10229,105,103,104,116,97,114,114,111,119,59,1,10231,97,112,115,116,111,59,1,10236,105,103,104,116,97,114,114,111,119,59,1,10230,112,97,114,114,111,119,4,2,108,114,12433,12439,101,102,116,59,1,8619,105,103,104,116,59,1,8620,4,3,97,102,108,12454,12458,12462,114,59,1,10629,59,3,55349,56669,117,115,59,1,10797,105,109,101,115,59,1,10804,4,2,97,98,12480,12485,115,116,59,1,8727,97,114,59,1,95,4,3,59,101,102,12498,12500,12506,1,9674,110,103,101,59,1,9674,59,1,10731,97,114,4,2,59,108,12517,12519,1,40,116,59,1,10643,4,5,97,99,104,109,116,12535,12540,12548,12561,12564,114,114,59,1,8646,111,114,110,101,114,59,1,8991,97,114,4,2,59,100,12556,12558,1,8651,59,1,10605,59,1,8206,114,105,59,1,8895,4,6,97,99,104,105,113,116,12583,12589,12594,12597,12614,12635,113,117,111,59,1,8249,114,59,3,55349,56513,59,1,8624,109,4,3,59,101,103,12606,12608,12611,1,8818,59,1,10893,59,1,10895,4,2,98,117,12620,12623,59,1,91,111,4,2,59,114,12630,12632,1,8216,59,1,8218,114,111,107,59,1,322,5,60,8,59,99,100,104,105,108,113,114,12660,12662,12675,12680,12686,12692,12698,12705,1,60,4,2,99,105,12668,12671,59,1,10918,114,59,1,10873,111,116,59,1,8918,114,101,101,59,1,8907,109,101,115,59,1,8905,97,114,114,59,1,10614,117,101,115,116,59,1,10875,4,2,80,105,12711,12716,97,114,59,1,10646,4,3,59,101,102,12724,12726,12729,1,9667,59,1,8884,59,1,9666,114,4,2,100,117,12739,12746,115,104,97,114,59,1,10570,104,97,114,59,1,10598,4,2,101,110,12758,12768,114,116,110,101,113,113,59,3,8808,65024,69,59,3,8808,65024,4,14,68,97,99,100,101,102,104,105,108,110,111,112,115,117,12803,12809,12893,12908,12914,12928,12933,12937,13011,13025,13032,13049,13052,13069,68,111,116,59,1,8762,4,4,99,108,112,114,12819,12827,12849,12887,114,5,175,1,59,12825,1,175,4,2,101,116,12833,12836,59,1,9794,4,2,59,101,12842,12844,1,10016,115,101,59,1,10016,4,2,59,115,12855,12857,1,8614,116,111,4,4,59,100,108,117,12869,12871,12877,12883,1,8614,111,119,110,59,1,8615,101,102,116,59,1,8612,112,59,1,8613,107,101,114,59,1,9646,4,2,111,121,12899,12905,109,109,97,59,1,10793,59,1,1084,97,115,104,59,1,8212,97,115,117,114,101,100,97,110,103,108,101,59,1,8737,114,59,3,55349,56618,111,59,1,8487,4,3,99,100,110,12945,12954,12985,114,111,5,181,1,59,12952,1,181,4,4,59,97,99,100,12964,12966,12971,12976,1,8739,115,116,59,1,42,105,114,59,1,10992,111,116,5,183,1,59,12983,1,183,117,115,4,3,59,98,100,12995,12997,13e3,1,8722,59,1,8863,4,2,59,117,13006,13008,1,8760,59,1,10794,4,2,99,100,13017,13021,112,59,1,10971,114,59,1,8230,112,108,117,115,59,1,8723,4,2,100,112,13038,13044,101,108,115,59,1,8871,102,59,3,55349,56670,59,1,8723,4,2,99,116,13058,13063,114,59,3,55349,56514,112,111,115,59,1,8766,4,3,59,108,109,13077,13079,13087,1,956,116,105,109,97,112,59,1,8888,97,112,59,1,8888,4,24,71,76,82,86,97,98,99,100,101,102,103,104,105,106,108,109,111,112,114,115,116,117,118,119,13142,13165,13217,13229,13247,13330,13359,13414,13420,13508,13513,13579,13602,13626,13631,13762,13767,13855,13936,13995,14214,14285,14312,14432,4,2,103,116,13148,13152,59,3,8921,824,4,2,59,118,13158,13161,3,8811,8402,59,3,8811,824,4,3,101,108,116,13173,13200,13204,102,116,4,2,97,114,13181,13188,114,114,111,119,59,1,8653,105,103,104,116,97,114,114,111,119,59,1,8654,59,3,8920,824,4,2,59,118,13210,13213,3,8810,8402,59,3,8810,824,105,103,104,116,97,114,114,111,119,59,1,8655,4,2,68,100,13235,13241,97,115,104,59,1,8879,97,115,104,59,1,8878,4,5,98,99,110,112,116,13259,13264,13270,13275,13308,108,97,59,1,8711,117,116,101,59,1,324,103,59,3,8736,8402,4,5,59,69,105,111,112,13287,13289,13293,13298,13302,1,8777,59,3,10864,824,100,59,3,8779,824,115,59,1,329,114,111,120,59,1,8777,117,114,4,2,59,97,13316,13318,1,9838,108,4,2,59,115,13325,13327,1,9838,59,1,8469,4,2,115,117,13336,13344,112,5,160,1,59,13342,1,160,109,112,4,2,59,101,13352,13355,3,8782,824,59,3,8783,824,4,5,97,101,111,117,121,13371,13385,13391,13407,13411,4,2,112,114,13377,13380,59,1,10819,111,110,59,1,328,100,105,108,59,1,326,110,103,4,2,59,100,13399,13401,1,8775,111,116,59,3,10861,824,112,59,1,10818,59,1,1085,97,115,104,59,1,8211,4,7,59,65,97,100,113,115,120,13436,13438,13443,13466,13472,13478,13494,1,8800,114,114,59,1,8663,114,4,2,104,114,13450,13454,107,59,1,10532,4,2,59,111,13460,13462,1,8599,119,59,1,8599,111,116,59,3,8784,824,117,105,118,59,1,8802,4,2,101,105,13484,13489,97,114,59,1,10536,109,59,3,8770,824,105,115,116,4,2,59,115,13503,13505,1,8708,59,1,8708,114,59,3,55349,56619,4,4,69,101,115,116,13523,13527,13563,13568,59,3,8807,824,4,3,59,113,115,13535,13537,13559,1,8817,4,3,59,113,115,13545,13547,13551,1,8817,59,3,8807,824,108,97,110,116,59,3,10878,824,59,3,10878,824,105,109,59,1,8821,4,2,59,114,13574,13576,1,8815,59,1,8815,4,3,65,97,112,13587,13592,13597,114,114,59,1,8654,114,114,59,1,8622,97,114,59,1,10994,4,3,59,115,118,13610,13612,13623,1,8715,4,2,59,100,13618,13620,1,8956,59,1,8954,59,1,8715,99,121,59,1,1114,4,7,65,69,97,100,101,115,116,13647,13652,13656,13661,13665,13737,13742,114,114,59,1,8653,59,3,8806,824,114,114,59,1,8602,114,59,1,8229,4,4,59,102,113,115,13675,13677,13703,13725,1,8816,116,4,2,97,114,13684,13691,114,114,111,119,59,1,8602,105,103,104,116,97,114,114,111,119,59,1,8622,4,3,59,113,115,13711,13713,13717,1,8816,59,3,8806,824,108,97,110,116,59,3,10877,824,4,2,59,115,13731,13734,3,10877,824,59,1,8814,105,109,59,1,8820,4,2,59,114,13748,13750,1,8814,105,4,2,59,101,13757,13759,1,8938,59,1,8940,105,100,59,1,8740,4,2,112,116,13773,13778,102,59,3,55349,56671,5,172,3,59,105,110,13787,13789,13829,1,172,110,4,4,59,69,100,118,13800,13802,13806,13812,1,8713,59,3,8953,824,111,116,59,3,8949,824,4,3,97,98,99,13820,13823,13826,59,1,8713,59,1,8951,59,1,8950,105,4,2,59,118,13836,13838,1,8716,4,3,97,98,99,13846,13849,13852,59,1,8716,59,1,8958,59,1,8957,4,3,97,111,114,13863,13892,13899,114,4,4,59,97,115,116,13874,13876,13883,13888,1,8742,108,108,101,108,59,1,8742,108,59,3,11005,8421,59,3,8706,824,108,105,110,116,59,1,10772,4,3,59,99,101,13907,13909,13914,1,8832,117,101,59,1,8928,4,2,59,99,13920,13923,3,10927,824,4,2,59,101,13929,13931,1,8832,113,59,3,10927,824,4,4,65,97,105,116,13946,13951,13971,13982,114,114,59,1,8655,114,114,4,3,59,99,119,13961,13963,13967,1,8603,59,3,10547,824,59,3,8605,824,103,104,116,97,114,114,111,119,59,1,8603,114,105,4,2,59,101,13990,13992,1,8939,59,1,8941,4,7,99,104,105,109,112,113,117,14011,14036,14060,14080,14085,14090,14106,4,4,59,99,101,114,14021,14023,14028,14032,1,8833,117,101,59,1,8929,59,3,10928,824,59,3,55349,56515,111,114,116,4,2,109,112,14045,14050,105,100,59,1,8740,97,114,97,108,108,101,108,59,1,8742,109,4,2,59,101,14067,14069,1,8769,4,2,59,113,14075,14077,1,8772,59,1,8772,105,100,59,1,8740,97,114,59,1,8742,115,117,4,2,98,112,14098,14102,101,59,1,8930,101,59,1,8931,4,3,98,99,112,14114,14157,14171,4,4,59,69,101,115,14124,14126,14130,14133,1,8836,59,3,10949,824,59,1,8840,101,116,4,2,59,101,14141,14144,3,8834,8402,113,4,2,59,113,14151,14153,1,8840,59,3,10949,824,99,4,2,59,101,14164,14166,1,8833,113,59,3,10928,824,4,4,59,69,101,115,14181,14183,14187,14190,1,8837,59,3,10950,824,59,1,8841,101,116,4,2,59,101,14198,14201,3,8835,8402,113,4,2,59,113,14208,14210,1,8841,59,3,10950,824,4,4,103,105,108,114,14224,14228,14238,14242,108,59,1,8825,108,100,101,5,241,1,59,14236,1,241,103,59,1,8824,105,97,110,103,108,101,4,2,108,114,14254,14269,101,102,116,4,2,59,101,14263,14265,1,8938,113,59,1,8940,105,103,104,116,4,2,59,101,14279,14281,1,8939,113,59,1,8941,4,2,59,109,14291,14293,1,957,4,3,59,101,115,14301,14303,14308,1,35,114,111,59,1,8470,112,59,1,8199,4,9,68,72,97,100,103,105,108,114,115,14332,14338,14344,14349,14355,14369,14376,14408,14426,97,115,104,59,1,8877,97,114,114,59,1,10500,112,59,3,8781,8402,97,115,104,59,1,8876,4,2,101,116,14361,14365,59,3,8805,8402,59,3,62,8402,110,102,105,110,59,1,10718,4,3,65,101,116,14384,14389,14393,114,114,59,1,10498,59,3,8804,8402,4,2,59,114,14399,14402,3,60,8402,105,101,59,3,8884,8402,4,2,65,116,14414,14419,114,114,59,1,10499,114,105,101,59,3,8885,8402,105,109,59,3,8764,8402,4,3,65,97,110,14440,14445,14468,114,114,59,1,8662,114,4,2,104,114,14452,14456,107,59,1,10531,4,2,59,111,14462,14464,1,8598,119,59,1,8598,101,97,114,59,1,10535,4,18,83,97,99,100,101,102,103,104,105,108,109,111,112,114,115,116,117,118,14512,14515,14535,14560,14597,14603,14618,14643,14657,14662,14701,14741,14747,14769,14851,14877,14907,14916,59,1,9416,4,2,99,115,14521,14531,117,116,101,5,243,1,59,14529,1,243,116,59,1,8859,4,2,105,121,14541,14557,114,4,2,59,99,14548,14550,1,8858,5,244,1,59,14555,1,244,59,1,1086,4,5,97,98,105,111,115,14572,14577,14583,14587,14591,115,104,59,1,8861,108,97,99,59,1,337,118,59,1,10808,116,59,1,8857,111,108,100,59,1,10684,108,105,103,59,1,339,4,2,99,114,14609,14614,105,114,59,1,10687,59,3,55349,56620,4,3,111,114,116,14626,14630,14640,110,59,1,731,97,118,101,5,242,1,59,14638,1,242,59,1,10689,4,2,98,109,14649,14654,97,114,59,1,10677,59,1,937,110,116,59,1,8750,4,4,97,99,105,116,14672,14677,14693,14698,114,114,59,1,8634,4,2,105,114,14683,14687,114,59,1,10686,111,115,115,59,1,10683,110,101,59,1,8254,59,1,10688,4,3,97,101,105,14709,14714,14719,99,114,59,1,333,103,97,59,1,969,4,3,99,100,110,14727,14733,14736,114,111,110,59,1,959,59,1,10678,117,115,59,1,8854,112,102,59,3,55349,56672,4,3,97,101,108,14755,14759,14764,114,59,1,10679,114,112,59,1,10681,117,115,59,1,8853,4,7,59,97,100,105,111,115,118,14785,14787,14792,14831,14837,14841,14848,1,8744,114,114,59,1,8635,4,4,59,101,102,109,14802,14804,14817,14824,1,10845,114,4,2,59,111,14811,14813,1,8500,102,59,1,8500,5,170,1,59,14822,1,170,5,186,1,59,14829,1,186,103,111,102,59,1,8886,114,59,1,10838,108,111,112,101,59,1,10839,59,1,10843,4,3,99,108,111,14859,14863,14873,114,59,1,8500,97,115,104,5,248,1,59,14871,1,248,108,59,1,8856,105,4,2,108,109,14884,14893,100,101,5,245,1,59,14891,1,245,101,115,4,2,59,97,14901,14903,1,8855,115,59,1,10806,109,108,5,246,1,59,14914,1,246,98,97,114,59,1,9021,4,12,97,99,101,102,104,105,108,109,111,114,115,117,14948,14992,14996,15033,15038,15068,15090,15189,15192,15222,15427,15441,114,4,4,59,97,115,116,14959,14961,14976,14989,1,8741,5,182,2,59,108,14968,14970,1,182,108,101,108,59,1,8741,4,2,105,108,14982,14986,109,59,1,10995,59,1,11005,59,1,8706,121,59,1,1087,114,4,5,99,105,109,112,116,15009,15014,15019,15024,15027,110,116,59,1,37,111,100,59,1,46,105,108,59,1,8240,59,1,8869,101,110,107,59,1,8241,114,59,3,55349,56621,4,3,105,109,111,15046,15057,15063,4,2,59,118,15052,15054,1,966,59,1,981,109,97,116,59,1,8499,110,101,59,1,9742,4,3,59,116,118,15076,15078,15087,1,960,99,104,102,111,114,107,59,1,8916,59,1,982,4,2,97,117,15096,15119,110,4,2,99,107,15103,15115,107,4,2,59,104,15110,15112,1,8463,59,1,8462,118,59,1,8463,115,4,9,59,97,98,99,100,101,109,115,116,15140,15142,15148,15151,15156,15168,15171,15179,15184,1,43,99,105,114,59,1,10787,59,1,8862,105,114,59,1,10786,4,2,111,117,15162,15165,59,1,8724,59,1,10789,59,1,10866,110,5,177,1,59,15177,1,177,105,109,59,1,10790,119,111,59,1,10791,59,1,177,4,3,105,112,117,15200,15208,15213,110,116,105,110,116,59,1,10773,102,59,3,55349,56673,110,100,5,163,1,59,15220,1,163,4,10,59,69,97,99,101,105,110,111,115,117,15244,15246,15249,15253,15258,15334,15347,15367,15416,15421,1,8826,59,1,10931,112,59,1,10935,117,101,59,1,8828,4,2,59,99,15264,15266,1,10927,4,6,59,97,99,101,110,115,15280,15282,15290,15299,15303,15329,1,8826,112,112,114,111,120,59,1,10935,117,114,108,121,101,113,59,1,8828,113,59,1,10927,4,3,97,101,115,15311,15319,15324,112,112,114,111,120,59,1,10937,113,113,59,1,10933,105,109,59,1,8936,105,109,59,1,8830,109,101,4,2,59,115,15342,15344,1,8242,59,1,8473,4,3,69,97,115,15355,15358,15362,59,1,10933,112,59,1,10937,105,109,59,1,8936,4,3,100,102,112,15375,15378,15404,59,1,8719,4,3,97,108,115,15386,15392,15398,108,97,114,59,1,9006,105,110,101,59,1,8978,117,114,102,59,1,8979,4,2,59,116,15410,15412,1,8733,111,59,1,8733,105,109,59,1,8830,114,101,108,59,1,8880,4,2,99,105,15433,15438,114,59,3,55349,56517,59,1,968,110,99,115,112,59,1,8200,4,6,102,105,111,112,115,117,15462,15467,15472,15478,15485,15491,114,59,3,55349,56622,110,116,59,1,10764,112,102,59,3,55349,56674,114,105,109,101,59,1,8279,99,114,59,3,55349,56518,4,3,97,101,111,15499,15520,15534,116,4,2,101,105,15506,15515,114,110,105,111,110,115,59,1,8461,110,116,59,1,10774,115,116,4,2,59,101,15528,15530,1,63,113,59,1,8799,116,5,34,1,59,15540,1,34,4,21,65,66,72,97,98,99,100,101,102,104,105,108,109,110,111,112,114,115,116,117,120,15586,15609,15615,15620,15796,15855,15893,15931,15977,16001,16039,16183,16204,16222,16228,16285,16312,16318,16363,16408,16416,4,3,97,114,116,15594,15599,15603,114,114,59,1,8667,114,59,1,8658,97,105,108,59,1,10524,97,114,114,59,1,10511,97,114,59,1,10596,4,7,99,100,101,110,113,114,116,15636,15651,15656,15664,15687,15696,15770,4,2,101,117,15642,15646,59,3,8765,817,116,101,59,1,341,105,99,59,1,8730,109,112,116,121,118,59,1,10675,103,4,4,59,100,101,108,15675,15677,15680,15683,1,10217,59,1,10642,59,1,10661,101,59,1,10217,117,111,5,187,1,59,15694,1,187,114,4,11,59,97,98,99,102,104,108,112,115,116,119,15721,15723,15727,15739,15742,15746,15750,15754,15758,15763,15767,1,8594,112,59,1,10613,4,2,59,102,15733,15735,1,8677,115,59,1,10528,59,1,10547,115,59,1,10526,107,59,1,8618,112,59,1,8620,108,59,1,10565,105,109,59,1,10612,108,59,1,8611,59,1,8605,4,2,97,105,15776,15781,105,108,59,1,10522,111,4,2,59,110,15788,15790,1,8758,97,108,115,59,1,8474,4,3,97,98,114,15804,15809,15814,114,114,59,1,10509,114,107,59,1,10099,4,2,97,107,15820,15833,99,4,2,101,107,15827,15830,59,1,125,59,1,93,4,2,101,115,15839,15842,59,1,10636,108,4,2,100,117,15849,15852,59,1,10638,59,1,10640,4,4,97,101,117,121,15865,15871,15886,15890,114,111,110,59,1,345,4,2,100,105,15877,15882,105,108,59,1,343,108,59,1,8969,98,59,1,125,59,1,1088,4,4,99,108,113,115,15903,15907,15914,15927,97,59,1,10551,100,104,97,114,59,1,10601,117,111,4,2,59,114,15922,15924,1,8221,59,1,8221,104,59,1,8627,4,3,97,99,103,15939,15966,15970,108,4,4,59,105,112,115,15950,15952,15957,15963,1,8476,110,101,59,1,8475,97,114,116,59,1,8476,59,1,8477,116,59,1,9645,5,174,1,59,15975,1,174,4,3,105,108,114,15985,15991,15997,115,104,116,59,1,10621,111,111,114,59,1,8971,59,3,55349,56623,4,2,97,111,16007,16028,114,4,2,100,117,16014,16017,59,1,8641,4,2,59,108,16023,16025,1,8640,59,1,10604,4,2,59,118,16034,16036,1,961,59,1,1009,4,3,103,110,115,16047,16167,16171,104,116,4,6,97,104,108,114,115,116,16063,16081,16103,16130,16143,16155,114,114,111,119,4,2,59,116,16073,16075,1,8594,97,105,108,59,1,8611,97,114,112,111,111,110,4,2,100,117,16093,16099,111,119,110,59,1,8641,112,59,1,8640,101,102,116,4,2,97,104,16112,16120,114,114,111,119,115,59,1,8644,97,114,112,111,111,110,115,59,1,8652,105,103,104,116,97,114,114,111,119,115,59,1,8649,113,117,105,103,97,114,114,111,119,59,1,8605,104,114,101,101,116,105,109,101,115,59,1,8908,103,59,1,730,105,110,103,100,111,116,115,101,113,59,1,8787,4,3,97,104,109,16191,16196,16201,114,114,59,1,8644,97,114,59,1,8652,59,1,8207,111,117,115,116,4,2,59,97,16214,16216,1,9137,99,104,101,59,1,9137,109,105,100,59,1,10990,4,4,97,98,112,116,16238,16252,16257,16278,4,2,110,114,16244,16248,103,59,1,10221,114,59,1,8702,114,107,59,1,10215,4,3,97,102,108,16265,16269,16273,114,59,1,10630,59,3,55349,56675,117,115,59,1,10798,105,109,101,115,59,1,10805,4,2,97,112,16291,16304,114,4,2,59,103,16298,16300,1,41,116,59,1,10644,111,108,105,110,116,59,1,10770,97,114,114,59,1,8649,4,4,97,99,104,113,16328,16334,16339,16342,113,117,111,59,1,8250,114,59,3,55349,56519,59,1,8625,4,2,98,117,16348,16351,59,1,93,111,4,2,59,114,16358,16360,1,8217,59,1,8217,4,3,104,105,114,16371,16377,16383,114,101,101,59,1,8908,109,101,115,59,1,8906,105,4,4,59,101,102,108,16394,16396,16399,16402,1,9657,59,1,8885,59,1,9656,116,114,105,59,1,10702,108,117,104,97,114,59,1,10600,59,1,8478,4,19,97,98,99,100,101,102,104,105,108,109,111,112,113,114,115,116,117,119,122,16459,16466,16472,16572,16590,16672,16687,16746,16844,16850,16924,16963,16988,17115,17121,17154,17206,17614,17656,99,117,116,101,59,1,347,113,117,111,59,1,8218,4,10,59,69,97,99,101,105,110,112,115,121,16494,16496,16499,16513,16518,16531,16536,16556,16564,16569,1,8827,59,1,10932,4,2,112,114,16505,16508,59,1,10936,111,110,59,1,353,117,101,59,1,8829,4,2,59,100,16524,16526,1,10928,105,108,59,1,351,114,99,59,1,349,4,3,69,97,115,16544,16547,16551,59,1,10934,112,59,1,10938,105,109,59,1,8937,111,108,105,110,116,59,1,10771,105,109,59,1,8831,59,1,1089,111,116,4,3,59,98,101,16582,16584,16587,1,8901,59,1,8865,59,1,10854,4,7,65,97,99,109,115,116,120,16606,16611,16634,16642,16646,16652,16668,114,114,59,1,8664,114,4,2,104,114,16618,16622,107,59,1,10533,4,2,59,111,16628,16630,1,8600,119,59,1,8600,116,5,167,1,59,16640,1,167,105,59,1,59,119,97,114,59,1,10537,109,4,2,105,110,16659,16665,110,117,115,59,1,8726,59,1,8726,116,59,1,10038,114,4,2,59,111,16679,16682,3,55349,56624,119,110,59,1,8994,4,4,97,99,111,121,16697,16702,16716,16739,114,112,59,1,9839,4,2,104,121,16708,16713,99,121,59,1,1097,59,1,1096,114,116,4,2,109,112,16724,16729,105,100,59,1,8739,97,114,97,108,108,101,108,59,1,8741,5,173,1,59,16744,1,173,4,2,103,109,16752,16770,109,97,4,3,59,102,118,16762,16764,16767,1,963,59,1,962,59,1,962,4,8,59,100,101,103,108,110,112,114,16788,16790,16795,16806,16817,16828,16832,16838,1,8764,111,116,59,1,10858,4,2,59,113,16801,16803,1,8771,59,1,8771,4,2,59,69,16812,16814,1,10910,59,1,10912,4,2,59,69,16823,16825,1,10909,59,1,10911,101,59,1,8774,108,117,115,59,1,10788,97,114,114,59,1,10610,97,114,114,59,1,8592,4,4,97,101,105,116,16860,16883,16891,16904,4,2,108,115,16866,16878,108,115,101,116,109,105,110,117,115,59,1,8726,104,112,59,1,10803,112,97,114,115,108,59,1,10724,4,2,100,108,16897,16900,59,1,8739,101,59,1,8995,4,2,59,101,16910,16912,1,10922,4,2,59,115,16918,16920,1,10924,59,3,10924,65024,4,3,102,108,112,16932,16938,16958,116,99,121,59,1,1100,4,2,59,98,16944,16946,1,47,4,2,59,97,16952,16954,1,10692,114,59,1,9023,102,59,3,55349,56676,97,4,2,100,114,16970,16985,101,115,4,2,59,117,16978,16980,1,9824,105,116,59,1,9824,59,1,8741,4,3,99,115,117,16996,17028,17089,4,2,97,117,17002,17015,112,4,2,59,115,17009,17011,1,8851,59,3,8851,65024,112,4,2,59,115,17022,17024,1,8852,59,3,8852,65024,117,4,2,98,112,17035,17062,4,3,59,101,115,17043,17045,17048,1,8847,59,1,8849,101,116,4,2,59,101,17056,17058,1,8847,113,59,1,8849,4,3,59,101,115,17070,17072,17075,1,8848,59,1,8850,101,116,4,2,59,101,17083,17085,1,8848,113,59,1,8850,4,3,59,97,102,17097,17099,17112,1,9633,114,4,2,101,102,17106,17109,59,1,9633,59,1,9642,59,1,9642,97,114,114,59,1,8594,4,4,99,101,109,116,17131,17136,17142,17148,114,59,3,55349,56520,116,109,110,59,1,8726,105,108,101,59,1,8995,97,114,102,59,1,8902,4,2,97,114,17160,17172,114,4,2,59,102,17167,17169,1,9734,59,1,9733,4,2,97,110,17178,17202,105,103,104,116,4,2,101,112,17188,17197,112,115,105,108,111,110,59,1,1013,104,105,59,1,981,115,59,1,175,4,5,98,99,109,110,112,17218,17351,17420,17423,17427,4,9,59,69,100,101,109,110,112,114,115,17238,17240,17243,17248,17261,17267,17279,17285,17291,1,8834,59,1,10949,111,116,59,1,10941,4,2,59,100,17254,17256,1,8838,111,116,59,1,10947,117,108,116,59,1,10945,4,2,69,101,17273,17276,59,1,10955,59,1,8842,108,117,115,59,1,10943,97,114,114,59,1,10617,4,3,101,105,117,17299,17335,17339,116,4,3,59,101,110,17308,17310,17322,1,8834,113,4,2,59,113,17317,17319,1,8838,59,1,10949,101,113,4,2,59,113,17330,17332,1,8842,59,1,10955,109,59,1,10951,4,2,98,112,17345,17348,59,1,10965,59,1,10963,99,4,6,59,97,99,101,110,115,17366,17368,17376,17385,17389,17415,1,8827,112,112,114,111,120,59,1,10936,117,114,108,121,101,113,59,1,8829,113,59,1,10928,4,3,97,101,115,17397,17405,17410,112,112,114,111,120,59,1,10938,113,113,59,1,10934,105,109,59,1,8937,105,109,59,1,8831,59,1,8721,103,59,1,9834,4,13,49,50,51,59,69,100,101,104,108,109,110,112,115,17455,17462,17469,17476,17478,17481,17496,17509,17524,17530,17536,17548,17554,5,185,1,59,17460,1,185,5,178,1,59,17467,1,178,5,179,1,59,17474,1,179,1,8835,59,1,10950,4,2,111,115,17487,17491,116,59,1,10942,117,98,59,1,10968,4,2,59,100,17502,17504,1,8839,111,116,59,1,10948,115,4,2,111,117,17516,17520,108,59,1,10185,98,59,1,10967,97,114,114,59,1,10619,117,108,116,59,1,10946,4,2,69,101,17542,17545,59,1,10956,59,1,8843,108,117,115,59,1,10944,4,3,101,105,117,17562,17598,17602,116,4,3,59,101,110,17571,17573,17585,1,8835,113,4,2,59,113,17580,17582,1,8839,59,1,10950,101,113,4,2,59,113,17593,17595,1,8843,59,1,10956,109,59,1,10952,4,2,98,112,17608,17611,59,1,10964,59,1,10966,4,3,65,97,110,17622,17627,17650,114,114,59,1,8665,114,4,2,104,114,17634,17638,107,59,1,10534,4,2,59,111,17644,17646,1,8601,119,59,1,8601,119,97,114,59,1,10538,108,105,103,5,223,1,59,17664,1,223,4,13,97,98,99,100,101,102,104,105,111,112,114,115,119,17694,17709,17714,17737,17742,17749,17754,17860,17905,17957,17964,18090,18122,4,2,114,117,17700,17706,103,101,116,59,1,8982,59,1,964,114,107,59,1,9140,4,3,97,101,121,17722,17728,17734,114,111,110,59,1,357,100,105,108,59,1,355,59,1,1090,111,116,59,1,8411,108,114,101,99,59,1,8981,114,59,3,55349,56625,4,4,101,105,107,111,17764,17805,17836,17851,4,2,114,116,17770,17786,101,4,2,52,102,17777,17780,59,1,8756,111,114,101,59,1,8756,97,4,3,59,115,118,17795,17797,17802,1,952,121,109,59,1,977,59,1,977,4,2,99,110,17811,17831,107,4,2,97,115,17818,17826,112,112,114,111,120,59,1,8776,105,109,59,1,8764,115,112,59,1,8201,4,2,97,115,17842,17846,112,59,1,8776,105,109,59,1,8764,114,110,5,254,1,59,17858,1,254,4,3,108,109,110,17868,17873,17901,100,101,59,1,732,101,115,5,215,3,59,98,100,17884,17886,17898,1,215,4,2,59,97,17892,17894,1,8864,114,59,1,10801,59,1,10800,116,59,1,8749,4,3,101,112,115,17913,17917,17953,97,59,1,10536,4,4,59,98,99,102,17927,17929,17934,17939,1,8868,111,116,59,1,9014,105,114,59,1,10993,4,2,59,111,17945,17948,3,55349,56677,114,107,59,1,10970,97,59,1,10537,114,105,109,101,59,1,8244,4,3,97,105,112,17972,17977,18082,100,101,59,1,8482,4,7,97,100,101,109,112,115,116,17993,18051,18056,18059,18066,18072,18076,110,103,108,101,4,5,59,100,108,113,114,18009,18011,18017,18032,18035,1,9653,111,119,110,59,1,9663,101,102,116,4,2,59,101,18026,18028,1,9667,113,59,1,8884,59,1,8796,105,103,104,116,4,2,59,101,18045,18047,1,9657,113,59,1,8885,111,116,59,1,9708,59,1,8796,105,110,117,115,59,1,10810,108,117,115,59,1,10809,98,59,1,10701,105,109,101,59,1,10811,101,122,105,117,109,59,1,9186,4,3,99,104,116,18098,18111,18116,4,2,114,121,18104,18108,59,3,55349,56521,59,1,1094,99,121,59,1,1115,114,111,107,59,1,359,4,2,105,111,18128,18133,120,116,59,1,8812,104,101,97,100,4,2,108,114,18143,18154,101,102,116,97,114,114,111,119,59,1,8606,105,103,104,116,97,114,114,111,119,59,1,8608,4,18,65,72,97,98,99,100,102,103,104,108,109,111,112,114,115,116,117,119,18204,18209,18214,18234,18250,18268,18292,18308,18319,18343,18379,18397,18413,18504,18547,18553,18584,18603,114,114,59,1,8657,97,114,59,1,10595,4,2,99,114,18220,18230,117,116,101,5,250,1,59,18228,1,250,114,59,1,8593,114,4,2,99,101,18241,18245,121,59,1,1118,118,101,59,1,365,4,2,105,121,18256,18265,114,99,5,251,1,59,18263,1,251,59,1,1091,4,3,97,98,104,18276,18281,18287,114,114,59,1,8645,108,97,99,59,1,369,97,114,59,1,10606,4,2,105,114,18298,18304,115,104,116,59,1,10622,59,3,55349,56626,114,97,118,101,5,249,1,59,18317,1,249,4,2,97,98,18325,18338,114,4,2,108,114,18332,18335,59,1,8639,59,1,8638,108,107,59,1,9600,4,2,99,116,18349,18374,4,2,111,114,18355,18369,114,110,4,2,59,101,18363,18365,1,8988,114,59,1,8988,111,112,59,1,8975,114,105,59,1,9720,4,2,97,108,18385,18390,99,114,59,1,363,5,168,1,59,18395,1,168,4,2,103,112,18403,18408,111,110,59,1,371,102,59,3,55349,56678,4,6,97,100,104,108,115,117,18427,18434,18445,18470,18475,18494,114,114,111,119,59,1,8593,111,119,110,97,114,114,111,119,59,1,8597,97,114,112,111,111,110,4,2,108,114,18457,18463,101,102,116,59,1,8639,105,103,104,116,59,1,8638,117,115,59,1,8846,105,4,3,59,104,108,18484,18486,18489,1,965,59,1,978,111,110,59,1,965,112,97,114,114,111,119,115,59,1,8648,4,3,99,105,116,18512,18537,18542,4,2,111,114,18518,18532,114,110,4,2,59,101,18526,18528,1,8989,114,59,1,8989,111,112,59,1,8974,110,103,59,1,367,114,105,59,1,9721,99,114,59,3,55349,56522,4,3,100,105,114,18561,18566,18572,111,116,59,1,8944,108,100,101,59,1,361,105,4,2,59,102,18579,18581,1,9653,59,1,9652,4,2,97,109,18590,18595,114,114,59,1,8648,108,5,252,1,59,18601,1,252,97,110,103,108,101,59,1,10663,4,15,65,66,68,97,99,100,101,102,108,110,111,112,114,115,122,18643,18648,18661,18667,18847,18851,18857,18904,18909,18915,18931,18937,18943,18949,18996,114,114,59,1,8661,97,114,4,2,59,118,18656,18658,1,10984,59,1,10985,97,115,104,59,1,8872,4,2,110,114,18673,18679,103,114,116,59,1,10652,4,7,101,107,110,112,114,115,116,18695,18704,18711,18720,18742,18754,18810,112,115,105,108,111,110,59,1,1013,97,112,112,97,59,1,1008,111,116,104,105,110,103,59,1,8709,4,3,104,105,114,18728,18732,18735,105,59,1,981,59,1,982,111,112,116,111,59,1,8733,4,2,59,104,18748,18750,1,8597,111,59,1,1009,4,2,105,117,18760,18766,103,109,97,59,1,962,4,2,98,112,18772,18791,115,101,116,110,101,113,4,2,59,113,18784,18787,3,8842,65024,59,3,10955,65024,115,101,116,110,101,113,4,2,59,113,18803,18806,3,8843,65024,59,3,10956,65024,4,2,104,114,18816,18822,101,116,97,59,1,977,105,97,110,103,108,101,4,2,108,114,18834,18840,101,102,116,59,1,8882,105,103,104,116,59,1,8883,121,59,1,1074,97,115,104,59,1,8866,4,3,101,108,114,18865,18884,18890,4,3,59,98,101,18873,18875,18880,1,8744,97,114,59,1,8891,113,59,1,8794,108,105,112,59,1,8942,4,2,98,116,18896,18901,97,114,59,1,124,59,1,124,114,59,3,55349,56627,116,114,105,59,1,8882,115,117,4,2,98,112,18923,18927,59,3,8834,8402,59,3,8835,8402,112,102,59,3,55349,56679,114,111,112,59,1,8733,116,114,105,59,1,8883,4,2,99,117,18955,18960,114,59,3,55349,56523,4,2,98,112,18966,18981,110,4,2,69,101,18973,18977,59,3,10955,65024,59,3,8842,65024,110,4,2,69,101,18988,18992,59,3,10956,65024,59,3,8843,65024,105,103,122,97,103,59,1,10650,4,7,99,101,102,111,112,114,115,19020,19026,19061,19066,19072,19075,19089,105,114,99,59,1,373,4,2,100,105,19032,19055,4,2,98,103,19038,19043,97,114,59,1,10847,101,4,2,59,113,19050,19052,1,8743,59,1,8793,101,114,112,59,1,8472,114,59,3,55349,56628,112,102,59,3,55349,56680,59,1,8472,4,2,59,101,19081,19083,1,8768,97,116,104,59,1,8768,99,114,59,3,55349,56524,4,14,99,100,102,104,105,108,109,110,111,114,115,117,118,119,19125,19146,19152,19157,19173,19176,19192,19197,19202,19236,19252,19269,19286,19291,4,3,97,105,117,19133,19137,19142,112,59,1,8898,114,99,59,1,9711,112,59,1,8899,116,114,105,59,1,9661,114,59,3,55349,56629,4,2,65,97,19163,19168,114,114,59,1,10234,114,114,59,1,10231,59,1,958,4,2,65,97,19182,19187,114,114,59,1,10232,114,114,59,1,10229,97,112,59,1,10236,105,115,59,1,8955,4,3,100,112,116,19210,19215,19230,111,116,59,1,10752,4,2,102,108,19221,19225,59,3,55349,56681,117,115,59,1,10753,105,109,101,59,1,10754,4,2,65,97,19242,19247,114,114,59,1,10233,114,114,59,1,10230,4,2,99,113,19258,19263,114,59,3,55349,56525,99,117,112,59,1,10758,4,2,112,116,19275,19281,108,117,115,59,1,10756,114,105,59,1,9651,101,101,59,1,8897,101,100,103,101,59,1,8896,4,8,97,99,101,102,105,111,115,117,19316,19335,19349,19357,19362,19367,19373,19379,99,4,2,117,121,19323,19332,116,101,5,253,1,59,19330,1,253,59,1,1103,4,2,105,121,19341,19346,114,99,59,1,375,59,1,1099,110,5,165,1,59,19355,1,165,114,59,3,55349,56630,99,121,59,1,1111,112,102,59,3,55349,56682,99,114,59,3,55349,56526,4,2,99,109,19385,19389,121,59,1,1102,108,5,255,1,59,19395,1,255,4,10,97,99,100,101,102,104,105,111,115,119,19419,19426,19441,19446,19462,19467,19472,19480,19486,19492,99,117,116,101,59,1,378,4,2,97,121,19432,19438,114,111,110,59,1,382,59,1,1079,111,116,59,1,380,4,2,101,116,19452,19458,116,114,102,59,1,8488,97,59,1,950,114,59,3,55349,56631,99,121,59,1,1078,103,114,97,114,114,59,1,8669,112,102,59,3,55349,56683,99,114,59,3,55349,56527,4,2,106,110,19498,19501,59,1,8205,106,59,1,8204]);const CK=yK,Be=ci,Za=TK,L=M9,k=Be.CODE_POINTS,Ba=Be.CODE_POINT_SEQUENCES,SK={128:8364,130:8218,131:402,132:8222,133:8230,134:8224,135:8225,136:710,137:8240,138:352,139:8249,140:338,142:381,145:8216,146:8217,147:8220,148:8221,149:8226,150:8211,151:8212,152:732,153:8482,154:353,155:8250,156:339,158:382,159:376},kb=1,Ib=2,xb=4,_K=kb|Ib|xb,Se="DATA_STATE",$s="RCDATA_STATE",Bu="RAWTEXT_STATE",Xi="SCRIPT_DATA_STATE",Nb="PLAINTEXT_STATE",B2="TAG_OPEN_STATE",H2="END_TAG_OPEN_STATE",Mm="TAG_NAME_STATE",z2="RCDATA_LESS_THAN_SIGN_STATE",U2="RCDATA_END_TAG_OPEN_STATE",K2="RCDATA_END_TAG_NAME_STATE",W2="RAWTEXT_LESS_THAN_SIGN_STATE",j2="RAWTEXT_END_TAG_OPEN_STATE",G2="RAWTEXT_END_TAG_NAME_STATE",$2="SCRIPT_DATA_LESS_THAN_SIGN_STATE",V2="SCRIPT_DATA_END_TAG_OPEN_STATE",q2="SCRIPT_DATA_END_TAG_NAME_STATE",Y2="SCRIPT_DATA_ESCAPE_START_STATE",Q2="SCRIPT_DATA_ESCAPE_START_DASH_STATE",Gr="SCRIPT_DATA_ESCAPED_STATE",X2="SCRIPT_DATA_ESCAPED_DASH_STATE",Pm="SCRIPT_DATA_ESCAPED_DASH_DASH_STATE",Ef="SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE",Z2="SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE",J2="SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE",eT="SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE",ji="SCRIPT_DATA_DOUBLE_ESCAPED_STATE",tT="SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE",nT="SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE",yf="SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE",rT="SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE",yi="BEFORE_ATTRIBUTE_NAME_STATE",Tf="ATTRIBUTE_NAME_STATE",Om="AFTER_ATTRIBUTE_NAME_STATE",Lm="BEFORE_ATTRIBUTE_VALUE_STATE",Cf="ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE",Sf="ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE",_f="ATTRIBUTE_VALUE_UNQUOTED_STATE",Bm="AFTER_ATTRIBUTE_VALUE_QUOTED_STATE",Oo="SELF_CLOSING_START_TAG_STATE",bu="BOGUS_COMMENT_STATE",iT="MARKUP_DECLARATION_OPEN_STATE",oT="COMMENT_START_STATE",aT="COMMENT_START_DASH_STATE",Lo="COMMENT_STATE",sT="COMMENT_LESS_THAN_SIGN_STATE",lT="COMMENT_LESS_THAN_SIGN_BANG_STATE",uT="COMMENT_LESS_THAN_SIGN_BANG_DASH_STATE",cT="COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH_STATE",bf="COMMENT_END_DASH_STATE",Af="COMMENT_END_STATE",fT="COMMENT_END_BANG_STATE",dT="DOCTYPE_STATE",wf="BEFORE_DOCTYPE_NAME_STATE",kf="DOCTYPE_NAME_STATE",hT="AFTER_DOCTYPE_NAME_STATE",pT="AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE",mT="BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE",Hm="DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE",zm="DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE",Um="AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE",gT="BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE",vT="AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE",ET="BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE",Au="DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE",wu="DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE",Km="AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE",Gi="BOGUS_DOCTYPE_STATE",If="CDATA_SECTION_STATE",yT="CDATA_SECTION_BRACKET_STATE",TT="CDATA_SECTION_END_STATE",zs="CHARACTER_REFERENCE_STATE",CT="NAMED_CHARACTER_REFERENCE_STATE",ST="AMBIGUOS_AMPERSAND_STATE",_T="NUMERIC_CHARACTER_REFERENCE_STATE",bT="HEXADEMICAL_CHARACTER_REFERENCE_START_STATE",AT="DECIMAL_CHARACTER_REFERENCE_START_STATE",wT="HEXADEMICAL_CHARACTER_REFERENCE_STATE",kT="DECIMAL_CHARACTER_REFERENCE_STATE",ku="NUMERIC_CHARACTER_REFERENCE_END_STATE";function Ye(e){return e===k.SPACE||e===k.LINE_FEED||e===k.TABULATION||e===k.FORM_FEED}function r1(e){return e>=k.DIGIT_0&&e<=k.DIGIT_9}function $r(e){return e>=k.LATIN_CAPITAL_A&&e<=k.LATIN_CAPITAL_Z}function Ka(e){return e>=k.LATIN_SMALL_A&&e<=k.LATIN_SMALL_Z}function Uo(e){return Ka(e)||$r(e)}function Wm(e){return Uo(e)||r1(e)}function Fb(e){return e>=k.LATIN_CAPITAL_A&&e<=k.LATIN_CAPITAL_F}function Rb(e){return e>=k.LATIN_SMALL_A&&e<=k.LATIN_SMALL_F}function bK(e){return r1(e)||Fb(e)||Rb(e)}function ld(e){return e+32}function mt(e){return e<=65535?String.fromCharCode(e):(e-=65536,String.fromCharCode(e>>>10&1023|55296)+String.fromCharCode(56320|e&1023))}function Bo(e){return String.fromCharCode(ld(e))}function IT(e,t){const n=Za[++e];let r=++e,i=r+n-1;for(;r<=i;){const o=r+i>>>1,a=Za[o];if(at)i=o-1;else return Za[o+n]}return-1}let Hr=class Un{constructor(){this.preprocessor=new CK,this.tokenQueue=[],this.allowCDATA=!1,this.state=Se,this.returnState="",this.charRefCode=-1,this.tempBuff=[],this.lastStartTagName="",this.consumedAfterSnapshot=-1,this.active=!1,this.currentCharacterToken=null,this.currentToken=null,this.currentAttr=null}_err(){}_errOnNextCodePoint(t){this._consume(),this._err(t),this._unconsume()}getNextToken(){for(;!this.tokenQueue.length&&this.active;){this.consumedAfterSnapshot=0;const t=this._consume();this._ensureHibernation()||this[this.state](t)}return this.tokenQueue.shift()}write(t,n){this.active=!0,this.preprocessor.write(t,n)}insertHtmlAtCurrentPos(t){this.active=!0,this.preprocessor.insertHtmlAtCurrentPos(t)}_ensureHibernation(){if(this.preprocessor.endOfChunkHit){for(;this.consumedAfterSnapshot>0;this.consumedAfterSnapshot--)this.preprocessor.retreat();return this.active=!1,this.tokenQueue.push({type:Un.HIBERNATION_TOKEN}),!0}return!1}_consume(){return this.consumedAfterSnapshot++,this.preprocessor.advance()}_unconsume(){this.consumedAfterSnapshot--,this.preprocessor.retreat()}_reconsumeInState(t){this.state=t,this._unconsume()}_consumeSequenceIfMatch(t,n,r){let i=0,o=!0;const a=t.length;let s=0,l=n,u;for(;s0&&(l=this._consume(),i++),l===k.EOF){o=!1;break}if(u=t[s],l!==u&&(r||l!==ld(u))){o=!1;break}}if(!o)for(;i--;)this._unconsume();return o}_isTempBufferEqualToScriptString(){if(this.tempBuff.length!==Ba.SCRIPT_STRING.length)return!1;for(let t=0;t0&&this._err(L.endTagWithAttributes),t.selfClosing&&this._err(L.endTagWithTrailingSolidus)),this.tokenQueue.push(t)}_emitCurrentCharacterToken(){this.currentCharacterToken&&(this.tokenQueue.push(this.currentCharacterToken),this.currentCharacterToken=null)}_emitEOFToken(){this._createEOFToken(),this._emitCurrentToken()}_appendCharToCurrentCharacterToken(t,n){this.currentCharacterToken&&this.currentCharacterToken.type!==t&&this._emitCurrentCharacterToken(),this.currentCharacterToken?this.currentCharacterToken.chars+=n:this._createCharacterToken(t,n)}_emitCodePoint(t){let n=Un.CHARACTER_TOKEN;Ye(t)?n=Un.WHITESPACE_CHARACTER_TOKEN:t===k.NULL&&(n=Un.NULL_CHARACTER_TOKEN),this._appendCharToCurrentCharacterToken(n,mt(t))}_emitSeveralCodePoints(t){for(let n=0;n-1;){const o=Za[i],a=o<_K;a&&o&kb&&(n=o&Ib?[Za[++i],Za[++i]]:[Za[++i]],r=0);const l=this._consume();if(this.tempBuff.push(l),r++,l===k.EOF)break;a?i=o&xb?IT(i,l):-1:i=l===o?++i:-1}for(;r--;)this.tempBuff.pop(),this._unconsume();return n}_isCharacterReferenceInAttribute(){return this.returnState===Cf||this.returnState===Sf||this.returnState===_f}_isCharacterReferenceAttributeQuirk(t){if(!t&&this._isCharacterReferenceInAttribute()){const n=this._consume();return this._unconsume(),n===k.EQUALS_SIGN||Wm(n)}return!1}_flushCodePointsConsumedAsCharacterReference(){if(this._isCharacterReferenceInAttribute())for(let t=0;t")):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.state=Gr,this._emitChars(Be.REPLACEMENT_CHARACTER)):t===k.EOF?(this._err(L.eofInScriptHtmlCommentLikeText),this._emitEOFToken()):(this.state=Gr,this._emitCodePoint(t))}[Ef](t){t===k.SOLIDUS?(this.tempBuff=[],this.state=Z2):Uo(t)?(this.tempBuff=[],this._emitChars("<"),this._reconsumeInState(eT)):(this._emitChars("<"),this._reconsumeInState(Gr))}[Z2](t){Uo(t)?(this._createEndTagToken(),this._reconsumeInState(J2)):(this._emitChars("")):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.state=ji,this._emitChars(Be.REPLACEMENT_CHARACTER)):t===k.EOF?(this._err(L.eofInScriptHtmlCommentLikeText),this._emitEOFToken()):(this.state=ji,this._emitCodePoint(t))}[yf](t){t===k.SOLIDUS?(this.tempBuff=[],this.state=rT,this._emitChars("/")):this._reconsumeInState(ji)}[rT](t){Ye(t)||t===k.SOLIDUS||t===k.GREATER_THAN_SIGN?(this.state=this._isTempBufferEqualToScriptString()?Gr:ji,this._emitCodePoint(t)):$r(t)?(this.tempBuff.push(ld(t)),this._emitCodePoint(t)):Ka(t)?(this.tempBuff.push(t),this._emitCodePoint(t)):this._reconsumeInState(ji)}[yi](t){Ye(t)||(t===k.SOLIDUS||t===k.GREATER_THAN_SIGN||t===k.EOF?this._reconsumeInState(Om):t===k.EQUALS_SIGN?(this._err(L.unexpectedEqualsSignBeforeAttributeName),this._createAttr("="),this.state=Tf):(this._createAttr(""),this._reconsumeInState(Tf)))}[Tf](t){Ye(t)||t===k.SOLIDUS||t===k.GREATER_THAN_SIGN||t===k.EOF?(this._leaveAttrName(Om),this._unconsume()):t===k.EQUALS_SIGN?this._leaveAttrName(Lm):$r(t)?this.currentAttr.name+=Bo(t):t===k.QUOTATION_MARK||t===k.APOSTROPHE||t===k.LESS_THAN_SIGN?(this._err(L.unexpectedCharacterInAttributeName),this.currentAttr.name+=mt(t)):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentAttr.name+=Be.REPLACEMENT_CHARACTER):this.currentAttr.name+=mt(t)}[Om](t){Ye(t)||(t===k.SOLIDUS?this.state=Oo:t===k.EQUALS_SIGN?this.state=Lm:t===k.GREATER_THAN_SIGN?(this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInTag),this._emitEOFToken()):(this._createAttr(""),this._reconsumeInState(Tf)))}[Lm](t){Ye(t)||(t===k.QUOTATION_MARK?this.state=Cf:t===k.APOSTROPHE?this.state=Sf:t===k.GREATER_THAN_SIGN?(this._err(L.missingAttributeValue),this.state=Se,this._emitCurrentToken()):this._reconsumeInState(_f))}[Cf](t){t===k.QUOTATION_MARK?this.state=Bm:t===k.AMPERSAND?(this.returnState=Cf,this.state=zs):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentAttr.value+=Be.REPLACEMENT_CHARACTER):t===k.EOF?(this._err(L.eofInTag),this._emitEOFToken()):this.currentAttr.value+=mt(t)}[Sf](t){t===k.APOSTROPHE?this.state=Bm:t===k.AMPERSAND?(this.returnState=Sf,this.state=zs):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentAttr.value+=Be.REPLACEMENT_CHARACTER):t===k.EOF?(this._err(L.eofInTag),this._emitEOFToken()):this.currentAttr.value+=mt(t)}[_f](t){Ye(t)?this._leaveAttrValue(yi):t===k.AMPERSAND?(this.returnState=_f,this.state=zs):t===k.GREATER_THAN_SIGN?(this._leaveAttrValue(Se),this._emitCurrentToken()):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentAttr.value+=Be.REPLACEMENT_CHARACTER):t===k.QUOTATION_MARK||t===k.APOSTROPHE||t===k.LESS_THAN_SIGN||t===k.EQUALS_SIGN||t===k.GRAVE_ACCENT?(this._err(L.unexpectedCharacterInUnquotedAttributeValue),this.currentAttr.value+=mt(t)):t===k.EOF?(this._err(L.eofInTag),this._emitEOFToken()):this.currentAttr.value+=mt(t)}[Bm](t){Ye(t)?this._leaveAttrValue(yi):t===k.SOLIDUS?this._leaveAttrValue(Oo):t===k.GREATER_THAN_SIGN?(this._leaveAttrValue(Se),this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInTag),this._emitEOFToken()):(this._err(L.missingWhitespaceBetweenAttributes),this._reconsumeInState(yi))}[Oo](t){t===k.GREATER_THAN_SIGN?(this.currentToken.selfClosing=!0,this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInTag),this._emitEOFToken()):(this._err(L.unexpectedSolidusInTag),this._reconsumeInState(yi))}[bu](t){t===k.GREATER_THAN_SIGN?(this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._emitCurrentToken(),this._emitEOFToken()):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentToken.data+=Be.REPLACEMENT_CHARACTER):this.currentToken.data+=mt(t)}[iT](t){this._consumeSequenceIfMatch(Ba.DASH_DASH_STRING,t,!0)?(this._createCommentToken(),this.state=oT):this._consumeSequenceIfMatch(Ba.DOCTYPE_STRING,t,!1)?this.state=dT:this._consumeSequenceIfMatch(Ba.CDATA_START_STRING,t,!0)?this.allowCDATA?this.state=If:(this._err(L.cdataInHtmlContent),this._createCommentToken(),this.currentToken.data="[CDATA[",this.state=bu):this._ensureHibernation()||(this._err(L.incorrectlyOpenedComment),this._createCommentToken(),this._reconsumeInState(bu))}[oT](t){t===k.HYPHEN_MINUS?this.state=aT:t===k.GREATER_THAN_SIGN?(this._err(L.abruptClosingOfEmptyComment),this.state=Se,this._emitCurrentToken()):this._reconsumeInState(Lo)}[aT](t){t===k.HYPHEN_MINUS?this.state=Af:t===k.GREATER_THAN_SIGN?(this._err(L.abruptClosingOfEmptyComment),this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInComment),this._emitCurrentToken(),this._emitEOFToken()):(this.currentToken.data+="-",this._reconsumeInState(Lo))}[Lo](t){t===k.HYPHEN_MINUS?this.state=bf:t===k.LESS_THAN_SIGN?(this.currentToken.data+="<",this.state=sT):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentToken.data+=Be.REPLACEMENT_CHARACTER):t===k.EOF?(this._err(L.eofInComment),this._emitCurrentToken(),this._emitEOFToken()):this.currentToken.data+=mt(t)}[sT](t){t===k.EXCLAMATION_MARK?(this.currentToken.data+="!",this.state=lT):t===k.LESS_THAN_SIGN?this.currentToken.data+="!":this._reconsumeInState(Lo)}[lT](t){t===k.HYPHEN_MINUS?this.state=uT:this._reconsumeInState(Lo)}[uT](t){t===k.HYPHEN_MINUS?this.state=cT:this._reconsumeInState(bf)}[cT](t){t!==k.GREATER_THAN_SIGN&&t!==k.EOF&&this._err(L.nestedComment),this._reconsumeInState(Af)}[bf](t){t===k.HYPHEN_MINUS?this.state=Af:t===k.EOF?(this._err(L.eofInComment),this._emitCurrentToken(),this._emitEOFToken()):(this.currentToken.data+="-",this._reconsumeInState(Lo))}[Af](t){t===k.GREATER_THAN_SIGN?(this.state=Se,this._emitCurrentToken()):t===k.EXCLAMATION_MARK?this.state=fT:t===k.HYPHEN_MINUS?this.currentToken.data+="-":t===k.EOF?(this._err(L.eofInComment),this._emitCurrentToken(),this._emitEOFToken()):(this.currentToken.data+="--",this._reconsumeInState(Lo))}[fT](t){t===k.HYPHEN_MINUS?(this.currentToken.data+="--!",this.state=bf):t===k.GREATER_THAN_SIGN?(this._err(L.incorrectlyClosedComment),this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInComment),this._emitCurrentToken(),this._emitEOFToken()):(this.currentToken.data+="--!",this._reconsumeInState(Lo))}[dT](t){Ye(t)?this.state=wf:t===k.GREATER_THAN_SIGN?this._reconsumeInState(wf):t===k.EOF?(this._err(L.eofInDoctype),this._createDoctypeToken(null),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._err(L.missingWhitespaceBeforeDoctypeName),this._reconsumeInState(wf))}[wf](t){Ye(t)||($r(t)?(this._createDoctypeToken(Bo(t)),this.state=kf):t===k.NULL?(this._err(L.unexpectedNullCharacter),this._createDoctypeToken(Be.REPLACEMENT_CHARACTER),this.state=kf):t===k.GREATER_THAN_SIGN?(this._err(L.missingDoctypeName),this._createDoctypeToken(null),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this.state=Se):t===k.EOF?(this._err(L.eofInDoctype),this._createDoctypeToken(null),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._createDoctypeToken(mt(t)),this.state=kf))}[kf](t){Ye(t)?this.state=hT:t===k.GREATER_THAN_SIGN?(this.state=Se,this._emitCurrentToken()):$r(t)?this.currentToken.name+=Bo(t):t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentToken.name+=Be.REPLACEMENT_CHARACTER):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):this.currentToken.name+=mt(t)}[hT](t){Ye(t)||(t===k.GREATER_THAN_SIGN?(this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):this._consumeSequenceIfMatch(Ba.PUBLIC_STRING,t,!1)?this.state=pT:this._consumeSequenceIfMatch(Ba.SYSTEM_STRING,t,!1)?this.state=vT:this._ensureHibernation()||(this._err(L.invalidCharacterSequenceAfterDoctypeName),this.currentToken.forceQuirks=!0,this._reconsumeInState(Gi)))}[pT](t){Ye(t)?this.state=mT:t===k.QUOTATION_MARK?(this._err(L.missingWhitespaceAfterDoctypePublicKeyword),this.currentToken.publicId="",this.state=Hm):t===k.APOSTROPHE?(this._err(L.missingWhitespaceAfterDoctypePublicKeyword),this.currentToken.publicId="",this.state=zm):t===k.GREATER_THAN_SIGN?(this._err(L.missingDoctypePublicIdentifier),this.currentToken.forceQuirks=!0,this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._err(L.missingQuoteBeforeDoctypePublicIdentifier),this.currentToken.forceQuirks=!0,this._reconsumeInState(Gi))}[mT](t){Ye(t)||(t===k.QUOTATION_MARK?(this.currentToken.publicId="",this.state=Hm):t===k.APOSTROPHE?(this.currentToken.publicId="",this.state=zm):t===k.GREATER_THAN_SIGN?(this._err(L.missingDoctypePublicIdentifier),this.currentToken.forceQuirks=!0,this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._err(L.missingQuoteBeforeDoctypePublicIdentifier),this.currentToken.forceQuirks=!0,this._reconsumeInState(Gi)))}[Hm](t){t===k.QUOTATION_MARK?this.state=Um:t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentToken.publicId+=Be.REPLACEMENT_CHARACTER):t===k.GREATER_THAN_SIGN?(this._err(L.abruptDoctypePublicIdentifier),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this.state=Se):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):this.currentToken.publicId+=mt(t)}[zm](t){t===k.APOSTROPHE?this.state=Um:t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentToken.publicId+=Be.REPLACEMENT_CHARACTER):t===k.GREATER_THAN_SIGN?(this._err(L.abruptDoctypePublicIdentifier),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this.state=Se):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):this.currentToken.publicId+=mt(t)}[Um](t){Ye(t)?this.state=gT:t===k.GREATER_THAN_SIGN?(this.state=Se,this._emitCurrentToken()):t===k.QUOTATION_MARK?(this._err(L.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers),this.currentToken.systemId="",this.state=Au):t===k.APOSTROPHE?(this._err(L.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers),this.currentToken.systemId="",this.state=wu):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._err(L.missingQuoteBeforeDoctypeSystemIdentifier),this.currentToken.forceQuirks=!0,this._reconsumeInState(Gi))}[gT](t){Ye(t)||(t===k.GREATER_THAN_SIGN?(this._emitCurrentToken(),this.state=Se):t===k.QUOTATION_MARK?(this.currentToken.systemId="",this.state=Au):t===k.APOSTROPHE?(this.currentToken.systemId="",this.state=wu):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._err(L.missingQuoteBeforeDoctypeSystemIdentifier),this.currentToken.forceQuirks=!0,this._reconsumeInState(Gi)))}[vT](t){Ye(t)?this.state=ET:t===k.QUOTATION_MARK?(this._err(L.missingWhitespaceAfterDoctypeSystemKeyword),this.currentToken.systemId="",this.state=Au):t===k.APOSTROPHE?(this._err(L.missingWhitespaceAfterDoctypeSystemKeyword),this.currentToken.systemId="",this.state=wu):t===k.GREATER_THAN_SIGN?(this._err(L.missingDoctypeSystemIdentifier),this.currentToken.forceQuirks=!0,this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._err(L.missingQuoteBeforeDoctypeSystemIdentifier),this.currentToken.forceQuirks=!0,this._reconsumeInState(Gi))}[ET](t){Ye(t)||(t===k.QUOTATION_MARK?(this.currentToken.systemId="",this.state=Au):t===k.APOSTROPHE?(this.currentToken.systemId="",this.state=wu):t===k.GREATER_THAN_SIGN?(this._err(L.missingDoctypeSystemIdentifier),this.currentToken.forceQuirks=!0,this.state=Se,this._emitCurrentToken()):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._err(L.missingQuoteBeforeDoctypeSystemIdentifier),this.currentToken.forceQuirks=!0,this._reconsumeInState(Gi)))}[Au](t){t===k.QUOTATION_MARK?this.state=Km:t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentToken.systemId+=Be.REPLACEMENT_CHARACTER):t===k.GREATER_THAN_SIGN?(this._err(L.abruptDoctypeSystemIdentifier),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this.state=Se):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):this.currentToken.systemId+=mt(t)}[wu](t){t===k.APOSTROPHE?this.state=Km:t===k.NULL?(this._err(L.unexpectedNullCharacter),this.currentToken.systemId+=Be.REPLACEMENT_CHARACTER):t===k.GREATER_THAN_SIGN?(this._err(L.abruptDoctypeSystemIdentifier),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this.state=Se):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):this.currentToken.systemId+=mt(t)}[Km](t){Ye(t)||(t===k.GREATER_THAN_SIGN?(this._emitCurrentToken(),this.state=Se):t===k.EOF?(this._err(L.eofInDoctype),this.currentToken.forceQuirks=!0,this._emitCurrentToken(),this._emitEOFToken()):(this._err(L.unexpectedCharacterAfterDoctypeSystemIdentifier),this._reconsumeInState(Gi)))}[Gi](t){t===k.GREATER_THAN_SIGN?(this._emitCurrentToken(),this.state=Se):t===k.NULL?this._err(L.unexpectedNullCharacter):t===k.EOF&&(this._emitCurrentToken(),this._emitEOFToken())}[If](t){t===k.RIGHT_SQUARE_BRACKET?this.state=yT:t===k.EOF?(this._err(L.eofInCdata),this._emitEOFToken()):this._emitCodePoint(t)}[yT](t){t===k.RIGHT_SQUARE_BRACKET?this.state=TT:(this._emitChars("]"),this._reconsumeInState(If))}[TT](t){t===k.GREATER_THAN_SIGN?this.state=Se:t===k.RIGHT_SQUARE_BRACKET?this._emitChars("]"):(this._emitChars("]]"),this._reconsumeInState(If))}[zs](t){this.tempBuff=[k.AMPERSAND],t===k.NUMBER_SIGN?(this.tempBuff.push(t),this.state=_T):Wm(t)?this._reconsumeInState(CT):(this._flushCodePointsConsumedAsCharacterReference(),this._reconsumeInState(this.returnState))}[CT](t){const n=this._matchNamedCharacterReference(t);if(this._ensureHibernation())this.tempBuff=[k.AMPERSAND];else if(n){const r=this.tempBuff[this.tempBuff.length-1]===k.SEMICOLON;this._isCharacterReferenceAttributeQuirk(r)||(r||this._errOnNextCodePoint(L.missingSemicolonAfterCharacterReference),this.tempBuff=n),this._flushCodePointsConsumedAsCharacterReference(),this.state=this.returnState}else this._flushCodePointsConsumedAsCharacterReference(),this.state=ST}[ST](t){Wm(t)?this._isCharacterReferenceInAttribute()?this.currentAttr.value+=mt(t):this._emitCodePoint(t):(t===k.SEMICOLON&&this._err(L.unknownNamedCharacterReference),this._reconsumeInState(this.returnState))}[_T](t){this.charRefCode=0,t===k.LATIN_SMALL_X||t===k.LATIN_CAPITAL_X?(this.tempBuff.push(t),this.state=bT):this._reconsumeInState(AT)}[bT](t){bK(t)?this._reconsumeInState(wT):(this._err(L.absenceOfDigitsInNumericCharacterReference),this._flushCodePointsConsumedAsCharacterReference(),this._reconsumeInState(this.returnState))}[AT](t){r1(t)?this._reconsumeInState(kT):(this._err(L.absenceOfDigitsInNumericCharacterReference),this._flushCodePointsConsumedAsCharacterReference(),this._reconsumeInState(this.returnState))}[wT](t){Fb(t)?this.charRefCode=this.charRefCode*16+t-55:Rb(t)?this.charRefCode=this.charRefCode*16+t-87:r1(t)?this.charRefCode=this.charRefCode*16+t-48:t===k.SEMICOLON?this.state=ku:(this._err(L.missingSemicolonAfterCharacterReference),this._reconsumeInState(ku))}[kT](t){r1(t)?this.charRefCode=this.charRefCode*10+t-48:t===k.SEMICOLON?this.state=ku:(this._err(L.missingSemicolonAfterCharacterReference),this._reconsumeInState(ku))}[ku](){if(this.charRefCode===k.NULL)this._err(L.nullCharacterReference),this.charRefCode=k.REPLACEMENT_CHARACTER;else if(this.charRefCode>1114111)this._err(L.characterReferenceOutsideUnicodeRange),this.charRefCode=k.REPLACEMENT_CHARACTER;else if(Be.isSurrogate(this.charRefCode))this._err(L.surrogateCharacterReference),this.charRefCode=k.REPLACEMENT_CHARACTER;else if(Be.isUndefinedCodePoint(this.charRefCode))this._err(L.noncharacterCharacterReference);else if(Be.isControlCodePoint(this.charRefCode)||this.charRefCode===k.CARRIAGE_RETURN){this._err(L.controlCharacterReference);const t=SK[this.charRefCode];t&&(this.charRefCode=t)}this.tempBuff=[this.charRefCode],this._flushCodePointsConsumedAsCharacterReference(),this._reconsumeInState(this.returnState)}};Hr.CHARACTER_TOKEN="CHARACTER_TOKEN";Hr.NULL_CHARACTER_TOKEN="NULL_CHARACTER_TOKEN";Hr.WHITESPACE_CHARACTER_TOKEN="WHITESPACE_CHARACTER_TOKEN";Hr.START_TAG_TOKEN="START_TAG_TOKEN";Hr.END_TAG_TOKEN="END_TAG_TOKEN";Hr.COMMENT_TOKEN="COMMENT_TOKEN";Hr.DOCTYPE_TOKEN="DOCTYPE_TOKEN";Hr.EOF_TOKEN="EOF_TOKEN";Hr.HIBERNATION_TOKEN="HIBERNATION_TOKEN";Hr.MODE={DATA:Se,RCDATA:$s,RAWTEXT:Bu,SCRIPT_DATA:Xi,PLAINTEXT:Nb};Hr.getTokenAttr=function(e,t){for(let n=e.attrs.length-1;n>=0;n--)if(e.attrs[n].name===t)return e.attrs[n].value;return null};var Zh=Hr,fi={};const jm=fi.NAMESPACES={HTML:"http://www.w3.org/1999/xhtml",MATHML:"http://www.w3.org/1998/Math/MathML",SVG:"http://www.w3.org/2000/svg",XLINK:"http://www.w3.org/1999/xlink",XML:"http://www.w3.org/XML/1998/namespace",XMLNS:"http://www.w3.org/2000/xmlns/"};fi.ATTRS={TYPE:"type",ACTION:"action",ENCODING:"encoding",PROMPT:"prompt",NAME:"name",COLOR:"color",FACE:"face",SIZE:"size"};fi.DOCUMENT_MODE={NO_QUIRKS:"no-quirks",QUIRKS:"quirks",LIMITED_QUIRKS:"limited-quirks"};const j=fi.TAG_NAMES={A:"a",ADDRESS:"address",ANNOTATION_XML:"annotation-xml",APPLET:"applet",AREA:"area",ARTICLE:"article",ASIDE:"aside",B:"b",BASE:"base",BASEFONT:"basefont",BGSOUND:"bgsound",BIG:"big",BLOCKQUOTE:"blockquote",BODY:"body",BR:"br",BUTTON:"button",CAPTION:"caption",CENTER:"center",CODE:"code",COL:"col",COLGROUP:"colgroup",DD:"dd",DESC:"desc",DETAILS:"details",DIALOG:"dialog",DIR:"dir",DIV:"div",DL:"dl",DT:"dt",EM:"em",EMBED:"embed",FIELDSET:"fieldset",FIGCAPTION:"figcaption",FIGURE:"figure",FONT:"font",FOOTER:"footer",FOREIGN_OBJECT:"foreignObject",FORM:"form",FRAME:"frame",FRAMESET:"frameset",H1:"h1",H2:"h2",H3:"h3",H4:"h4",H5:"h5",H6:"h6",HEAD:"head",HEADER:"header",HGROUP:"hgroup",HR:"hr",HTML:"html",I:"i",IMG:"img",IMAGE:"image",INPUT:"input",IFRAME:"iframe",KEYGEN:"keygen",LABEL:"label",LI:"li",LINK:"link",LISTING:"listing",MAIN:"main",MALIGNMARK:"malignmark",MARQUEE:"marquee",MATH:"math",MENU:"menu",META:"meta",MGLYPH:"mglyph",MI:"mi",MO:"mo",MN:"mn",MS:"ms",MTEXT:"mtext",NAV:"nav",NOBR:"nobr",NOFRAMES:"noframes",NOEMBED:"noembed",NOSCRIPT:"noscript",OBJECT:"object",OL:"ol",OPTGROUP:"optgroup",OPTION:"option",P:"p",PARAM:"param",PLAINTEXT:"plaintext",PRE:"pre",RB:"rb",RP:"rp",RT:"rt",RTC:"rtc",RUBY:"ruby",S:"s",SCRIPT:"script",SECTION:"section",SELECT:"select",SOURCE:"source",SMALL:"small",SPAN:"span",STRIKE:"strike",STRONG:"strong",STYLE:"style",SUB:"sub",SUMMARY:"summary",SUP:"sup",TABLE:"table",TBODY:"tbody",TEMPLATE:"template",TEXTAREA:"textarea",TFOOT:"tfoot",TD:"td",TH:"th",THEAD:"thead",TITLE:"title",TR:"tr",TRACK:"track",TT:"tt",U:"u",UL:"ul",SVG:"svg",VAR:"var",WBR:"wbr",XMP:"xmp"};fi.SPECIAL_ELEMENTS={[jm.HTML]:{[j.ADDRESS]:!0,[j.APPLET]:!0,[j.AREA]:!0,[j.ARTICLE]:!0,[j.ASIDE]:!0,[j.BASE]:!0,[j.BASEFONT]:!0,[j.BGSOUND]:!0,[j.BLOCKQUOTE]:!0,[j.BODY]:!0,[j.BR]:!0,[j.BUTTON]:!0,[j.CAPTION]:!0,[j.CENTER]:!0,[j.COL]:!0,[j.COLGROUP]:!0,[j.DD]:!0,[j.DETAILS]:!0,[j.DIR]:!0,[j.DIV]:!0,[j.DL]:!0,[j.DT]:!0,[j.EMBED]:!0,[j.FIELDSET]:!0,[j.FIGCAPTION]:!0,[j.FIGURE]:!0,[j.FOOTER]:!0,[j.FORM]:!0,[j.FRAME]:!0,[j.FRAMESET]:!0,[j.H1]:!0,[j.H2]:!0,[j.H3]:!0,[j.H4]:!0,[j.H5]:!0,[j.H6]:!0,[j.HEAD]:!0,[j.HEADER]:!0,[j.HGROUP]:!0,[j.HR]:!0,[j.HTML]:!0,[j.IFRAME]:!0,[j.IMG]:!0,[j.INPUT]:!0,[j.LI]:!0,[j.LINK]:!0,[j.LISTING]:!0,[j.MAIN]:!0,[j.MARQUEE]:!0,[j.MENU]:!0,[j.META]:!0,[j.NAV]:!0,[j.NOEMBED]:!0,[j.NOFRAMES]:!0,[j.NOSCRIPT]:!0,[j.OBJECT]:!0,[j.OL]:!0,[j.P]:!0,[j.PARAM]:!0,[j.PLAINTEXT]:!0,[j.PRE]:!0,[j.SCRIPT]:!0,[j.SECTION]:!0,[j.SELECT]:!0,[j.SOURCE]:!0,[j.STYLE]:!0,[j.SUMMARY]:!0,[j.TABLE]:!0,[j.TBODY]:!0,[j.TD]:!0,[j.TEMPLATE]:!0,[j.TEXTAREA]:!0,[j.TFOOT]:!0,[j.TH]:!0,[j.THEAD]:!0,[j.TITLE]:!0,[j.TR]:!0,[j.TRACK]:!0,[j.UL]:!0,[j.WBR]:!0,[j.XMP]:!0},[jm.MATHML]:{[j.MI]:!0,[j.MO]:!0,[j.MN]:!0,[j.MS]:!0,[j.MTEXT]:!0,[j.ANNOTATION_XML]:!0},[jm.SVG]:{[j.TITLE]:!0,[j.FOREIGN_OBJECT]:!0,[j.DESC]:!0}};const Db=fi,$=Db.TAG_NAMES,He=Db.NAMESPACES;function xT(e){switch(e.length){case 1:return e===$.P;case 2:return e===$.RB||e===$.RP||e===$.RT||e===$.DD||e===$.DT||e===$.LI;case 3:return e===$.RTC;case 6:return e===$.OPTION;case 8:return e===$.OPTGROUP}return!1}function AK(e){switch(e.length){case 1:return e===$.P;case 2:return e===$.RB||e===$.RP||e===$.RT||e===$.DD||e===$.DT||e===$.LI||e===$.TD||e===$.TH||e===$.TR;case 3:return e===$.RTC;case 5:return e===$.TBODY||e===$.TFOOT||e===$.THEAD;case 6:return e===$.OPTION;case 7:return e===$.CAPTION;case 8:return e===$.OPTGROUP||e===$.COLGROUP}return!1}function xf(e,t){switch(e.length){case 2:if(e===$.TD||e===$.TH)return t===He.HTML;if(e===$.MI||e===$.MO||e===$.MN||e===$.MS)return t===He.MATHML;break;case 4:if(e===$.HTML)return t===He.HTML;if(e===$.DESC)return t===He.SVG;break;case 5:if(e===$.TABLE)return t===He.HTML;if(e===$.MTEXT)return t===He.MATHML;if(e===$.TITLE)return t===He.SVG;break;case 6:return(e===$.APPLET||e===$.OBJECT)&&t===He.HTML;case 7:return(e===$.CAPTION||e===$.MARQUEE)&&t===He.HTML;case 8:return e===$.TEMPLATE&&t===He.HTML;case 13:return e===$.FOREIGN_OBJECT&&t===He.SVG;case 14:return e===$.ANNOTATION_XML&&t===He.MATHML}return!1}let wK=class{constructor(t,n){this.stackTop=-1,this.items=[],this.current=t,this.currentTagName=null,this.currentTmplContent=null,this.tmplCount=0,this.treeAdapter=n}_indexOf(t){let n=-1;for(let r=this.stackTop;r>=0;r--)if(this.items[r]===t){n=r;break}return n}_isInTemplate(){return this.currentTagName===$.TEMPLATE&&this.treeAdapter.getNamespaceURI(this.current)===He.HTML}_updateCurrentElement(){this.current=this.items[this.stackTop],this.currentTagName=this.current&&this.treeAdapter.getTagName(this.current),this.currentTmplContent=this._isInTemplate()?this.treeAdapter.getTemplateContent(this.current):null}push(t){this.items[++this.stackTop]=t,this._updateCurrentElement(),this._isInTemplate()&&this.tmplCount++}pop(){this.stackTop--,this.tmplCount>0&&this._isInTemplate()&&this.tmplCount--,this._updateCurrentElement()}replace(t,n){const r=this._indexOf(t);this.items[r]=n,r===this.stackTop&&this._updateCurrentElement()}insertAfter(t,n){const r=this._indexOf(t)+1;this.items.splice(r,0,n),r===++this.stackTop&&this._updateCurrentElement()}popUntilTagNamePopped(t){for(;this.stackTop>-1;){const n=this.currentTagName,r=this.treeAdapter.getNamespaceURI(this.current);if(this.pop(),n===t&&r===He.HTML)break}}popUntilElementPopped(t){for(;this.stackTop>-1;){const n=this.current;if(this.pop(),n===t)break}}popUntilNumberedHeaderPopped(){for(;this.stackTop>-1;){const t=this.currentTagName,n=this.treeAdapter.getNamespaceURI(this.current);if(this.pop(),t===$.H1||t===$.H2||t===$.H3||t===$.H4||t===$.H5||t===$.H6&&n===He.HTML)break}}popUntilTableCellPopped(){for(;this.stackTop>-1;){const t=this.currentTagName,n=this.treeAdapter.getNamespaceURI(this.current);if(this.pop(),t===$.TD||t===$.TH&&n===He.HTML)break}}popAllUpToHtmlElement(){this.stackTop=0,this._updateCurrentElement()}clearBackToTableContext(){for(;this.currentTagName!==$.TABLE&&this.currentTagName!==$.TEMPLATE&&this.currentTagName!==$.HTML||this.treeAdapter.getNamespaceURI(this.current)!==He.HTML;)this.pop()}clearBackToTableBodyContext(){for(;this.currentTagName!==$.TBODY&&this.currentTagName!==$.TFOOT&&this.currentTagName!==$.THEAD&&this.currentTagName!==$.TEMPLATE&&this.currentTagName!==$.HTML||this.treeAdapter.getNamespaceURI(this.current)!==He.HTML;)this.pop()}clearBackToTableRowContext(){for(;this.currentTagName!==$.TR&&this.currentTagName!==$.TEMPLATE&&this.currentTagName!==$.HTML||this.treeAdapter.getNamespaceURI(this.current)!==He.HTML;)this.pop()}remove(t){for(let n=this.stackTop;n>=0;n--)if(this.items[n]===t){this.items.splice(n,1),this.stackTop--,this._updateCurrentElement();break}}tryPeekProperlyNestedBodyElement(){const t=this.items[1];return t&&this.treeAdapter.getTagName(t)===$.BODY?t:null}contains(t){return this._indexOf(t)>-1}getCommonAncestor(t){let n=this._indexOf(t);return--n>=0?this.items[n]:null}isRootHtmlElementCurrent(){return this.stackTop===0&&this.currentTagName===$.HTML}hasInScope(t){for(let n=this.stackTop;n>=0;n--){const r=this.treeAdapter.getTagName(this.items[n]),i=this.treeAdapter.getNamespaceURI(this.items[n]);if(r===t&&i===He.HTML)return!0;if(xf(r,i))return!1}return!0}hasNumberedHeaderInScope(){for(let t=this.stackTop;t>=0;t--){const n=this.treeAdapter.getTagName(this.items[t]),r=this.treeAdapter.getNamespaceURI(this.items[t]);if((n===$.H1||n===$.H2||n===$.H3||n===$.H4||n===$.H5||n===$.H6)&&r===He.HTML)return!0;if(xf(n,r))return!1}return!0}hasInListItemScope(t){for(let n=this.stackTop;n>=0;n--){const r=this.treeAdapter.getTagName(this.items[n]),i=this.treeAdapter.getNamespaceURI(this.items[n]);if(r===t&&i===He.HTML)return!0;if((r===$.UL||r===$.OL)&&i===He.HTML||xf(r,i))return!1}return!0}hasInButtonScope(t){for(let n=this.stackTop;n>=0;n--){const r=this.treeAdapter.getTagName(this.items[n]),i=this.treeAdapter.getNamespaceURI(this.items[n]);if(r===t&&i===He.HTML)return!0;if(r===$.BUTTON&&i===He.HTML||xf(r,i))return!1}return!0}hasInTableScope(t){for(let n=this.stackTop;n>=0;n--){const r=this.treeAdapter.getTagName(this.items[n]);if(this.treeAdapter.getNamespaceURI(this.items[n])===He.HTML){if(r===t)return!0;if(r===$.TABLE||r===$.TEMPLATE||r===$.HTML)return!1}}return!0}hasTableBodyContextInTableScope(){for(let t=this.stackTop;t>=0;t--){const n=this.treeAdapter.getTagName(this.items[t]);if(this.treeAdapter.getNamespaceURI(this.items[t])===He.HTML){if(n===$.TBODY||n===$.THEAD||n===$.TFOOT)return!0;if(n===$.TABLE||n===$.HTML)return!1}}return!0}hasInSelectScope(t){for(let n=this.stackTop;n>=0;n--){const r=this.treeAdapter.getTagName(this.items[n]);if(this.treeAdapter.getNamespaceURI(this.items[n])===He.HTML){if(r===t)return!0;if(r!==$.OPTION&&r!==$.OPTGROUP)return!1}}return!0}generateImpliedEndTags(){for(;xT(this.currentTagName);)this.pop()}generateImpliedEndTagsThoroughly(){for(;AK(this.currentTagName);)this.pop()}generateImpliedEndTagsWithExclusion(t){for(;xT(this.currentTagName)&&this.currentTagName!==t;)this.pop()}};var kK=wK;const Nf=3;let P9=class Ko{constructor(t){this.length=0,this.entries=[],this.treeAdapter=t,this.bookmark=null}_getNoahArkConditionCandidates(t){const n=[];if(this.length>=Nf){const r=this.treeAdapter.getAttrList(t).length,i=this.treeAdapter.getTagName(t),o=this.treeAdapter.getNamespaceURI(t);for(let a=this.length-1;a>=0;a--){const s=this.entries[a];if(s.type===Ko.MARKER_ENTRY)break;const l=s.element,u=this.treeAdapter.getAttrList(l);this.treeAdapter.getTagName(l)===i&&this.treeAdapter.getNamespaceURI(l)===o&&u.length===r&&n.push({idx:a,attrs:u})}}return n.length=Nf-1;s--)this.entries.splice(n[s].idx,1),this.length--}}insertMarker(){this.entries.push({type:Ko.MARKER_ENTRY}),this.length++}pushElement(t,n){this._ensureNoahArkCondition(t),this.entries.push({type:Ko.ELEMENT_ENTRY,element:t,token:n}),this.length++}insertElementAfterBookmark(t,n){let r=this.length-1;for(;r>=0&&this.entries[r]!==this.bookmark;r--);this.entries.splice(r+1,0,{type:Ko.ELEMENT_ENTRY,element:t,token:n}),this.length++}removeEntry(t){for(let n=this.length-1;n>=0;n--)if(this.entries[n]===t){this.entries.splice(n,1),this.length--;break}}clearToLastMarker(){for(;this.length;){const t=this.entries.pop();if(this.length--,t.type===Ko.MARKER_ENTRY)break}}getElementEntryInScopeWithTagName(t){for(let n=this.length-1;n>=0;n--){const r=this.entries[n];if(r.type===Ko.MARKER_ENTRY)return null;if(this.treeAdapter.getTagName(r.element)===t)return r}return null}getElementEntry(t){for(let n=this.length-1;n>=0;n--){const r=this.entries[n];if(r.type===Ko.ELEMENT_ENTRY&&r.element===t)return r}return null}};P9.MARKER_ENTRY="MARKER_ENTRY";P9.ELEMENT_ENTRY="ELEMENT_ENTRY";var IK=P9;let Mb=class{constructor(t){const n={},r=this._getOverriddenMethods(this,n);for(const i of Object.keys(r))typeof r[i]=="function"&&(n[i]=t[i],t[i]=r[i])}_getOverriddenMethods(){throw new Error("Not implemented")}};Mb.install=function(e,t,n){e.__mixins||(e.__mixins=[]);for(let i=0;i{const o=Gm.MODE[i];r[o]=function(a){t.ctLoc=t._getCurrentLocation(),n[o].call(this,a)}}),r}};var Ob=RK;const DK=So;let MK=class extends DK{constructor(t,n){super(t),this.onItemPop=n.onItemPop}_getOverriddenMethods(t,n){return{pop(){t.onItemPop(this.current),n.pop.call(this)},popAllUpToHtmlElement(){for(let r=this.stackTop;r>0;r--)t.onItemPop(this.items[r]);n.popAllUpToHtmlElement.call(this)},remove(r){t.onItemPop(this.current),n.remove.call(this,r)}}}};var PK=MK;const $m=So,FT=Zh,OK=Ob,LK=PK,BK=fi,Vm=BK.TAG_NAMES;let HK=class extends $m{constructor(t){super(t),this.parser=t,this.treeAdapter=this.parser.treeAdapter,this.posTracker=null,this.lastStartTagToken=null,this.lastFosterParentingLocation=null,this.currentToken=null}_setStartLocation(t){let n=null;this.lastStartTagToken&&(n=Object.assign({},this.lastStartTagToken.location),n.startTag=this.lastStartTagToken.location),this.treeAdapter.setNodeSourceCodeLocation(t,n)}_setEndLocation(t,n){if(this.treeAdapter.getNodeSourceCodeLocation(t)&&n.location){const i=n.location,o=this.treeAdapter.getTagName(t),a=n.type===FT.END_TAG_TOKEN&&o===n.tagName,s={};a?(s.endTag=Object.assign({},i),s.endLine=i.endLine,s.endCol=i.endCol,s.endOffset=i.endOffset):(s.endLine=i.startLine,s.endCol=i.startCol,s.endOffset=i.startOffset),this.treeAdapter.updateNodeSourceCodeLocation(t,s)}}_getOverriddenMethods(t,n){return{_bootstrap(r,i){n._bootstrap.call(this,r,i),t.lastStartTagToken=null,t.lastFosterParentingLocation=null,t.currentToken=null;const o=$m.install(this.tokenizer,OK);t.posTracker=o.posTracker,$m.install(this.openElements,LK,{onItemPop:function(a){t._setEndLocation(a,t.currentToken)}})},_runParsingLoop(r){n._runParsingLoop.call(this,r);for(let i=this.openElements.stackTop;i>=0;i--)t._setEndLocation(this.openElements.items[i],t.currentToken)},_processTokenInForeignContent(r){t.currentToken=r,n._processTokenInForeignContent.call(this,r)},_processToken(r){if(t.currentToken=r,n._processToken.call(this,r),r.type===FT.END_TAG_TOKEN&&(r.tagName===Vm.HTML||r.tagName===Vm.BODY&&this.openElements.hasInScope(Vm.BODY)))for(let o=this.openElements.stackTop;o>=0;o--){const a=this.openElements.items[o];if(this.treeAdapter.getTagName(a)===r.tagName){t._setEndLocation(a,r);break}}},_setDocumentType(r){n._setDocumentType.call(this,r);const i=this.treeAdapter.getChildNodes(this.document),o=i.length;for(let a=0;a(Object.keys(i).forEach(o=>{r[o]=i[o]}),r),Object.create(null))},Jh={};const{DOCUMENT_MODE:Us}=fi,Hb="html",sW="about:legacy-compat",lW="http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd",zb=["+//silmaril//dtd html pro v0r11 19970101//","-//as//dtd html 3.0 aswedit + extensions//","-//advasoft ltd//dtd html 3.0 aswedit + extensions//","-//ietf//dtd html 2.0 level 1//","-//ietf//dtd html 2.0 level 2//","-//ietf//dtd html 2.0 strict level 1//","-//ietf//dtd html 2.0 strict level 2//","-//ietf//dtd html 2.0 strict//","-//ietf//dtd html 2.0//","-//ietf//dtd html 2.1e//","-//ietf//dtd html 3.0//","-//ietf//dtd html 3.2 final//","-//ietf//dtd html 3.2//","-//ietf//dtd html 3//","-//ietf//dtd html level 0//","-//ietf//dtd html level 1//","-//ietf//dtd html level 2//","-//ietf//dtd html level 3//","-//ietf//dtd html strict level 0//","-//ietf//dtd html strict level 1//","-//ietf//dtd html strict level 2//","-//ietf//dtd html strict level 3//","-//ietf//dtd html strict//","-//ietf//dtd html//","-//metrius//dtd metrius presentational//","-//microsoft//dtd internet explorer 2.0 html strict//","-//microsoft//dtd internet explorer 2.0 html//","-//microsoft//dtd internet explorer 2.0 tables//","-//microsoft//dtd internet explorer 3.0 html strict//","-//microsoft//dtd internet explorer 3.0 html//","-//microsoft//dtd internet explorer 3.0 tables//","-//netscape comm. corp.//dtd html//","-//netscape comm. corp.//dtd strict html//","-//o'reilly and associates//dtd html 2.0//","-//o'reilly and associates//dtd html extended 1.0//","-//o'reilly and associates//dtd html extended relaxed 1.0//","-//sq//dtd html 2.0 hotmetal + extensions//","-//softquad software//dtd hotmetal pro 6.0::19990601::extensions to html 4.0//","-//softquad//dtd hotmetal pro 4.0::19971010::extensions to html 4.0//","-//spyglass//dtd html 2.0 extended//","-//sun microsystems corp.//dtd hotjava html//","-//sun microsystems corp.//dtd hotjava strict html//","-//w3c//dtd html 3 1995-03-24//","-//w3c//dtd html 3.2 draft//","-//w3c//dtd html 3.2 final//","-//w3c//dtd html 3.2//","-//w3c//dtd html 3.2s draft//","-//w3c//dtd html 4.0 frameset//","-//w3c//dtd html 4.0 transitional//","-//w3c//dtd html experimental 19960712//","-//w3c//dtd html experimental 970421//","-//w3c//dtd w3 html//","-//w3o//dtd w3 html 3.0//","-//webtechs//dtd mozilla html 2.0//","-//webtechs//dtd mozilla html//"],uW=zb.concat(["-//w3c//dtd html 4.01 frameset//","-//w3c//dtd html 4.01 transitional//"]),cW=["-//w3o//dtd w3 html strict 3.0//en//","-/w3c/dtd html 4.0 transitional/en","html"],Ub=["-//w3c//dtd xhtml 1.0 frameset//","-//w3c//dtd xhtml 1.0 transitional//"],fW=Ub.concat(["-//w3c//dtd html 4.01 frameset//","-//w3c//dtd html 4.01 transitional//"]);function DT(e){const t=e.indexOf('"')!==-1?"'":'"';return t+e+t}function MT(e,t){for(let n=0;n-1)return Us.QUIRKS;let r=t===null?uW:zb;if(MT(n,r))return Us.QUIRKS;if(r=t===null?Ub:fW,MT(n,r))return Us.LIMITED_QUIRKS}return Us.NO_QUIRKS};Jh.serializeContent=function(e,t,n){let r="!DOCTYPE ";return e&&(r+=e),t?r+=" PUBLIC "+DT(t):n&&(r+=" SYSTEM"),n!==null&&(r+=" "+DT(n)),r};var xa={};const qm=Zh,L9=fi,ue=L9.TAG_NAMES,Zt=L9.NAMESPACES,ud=L9.ATTRS,PT={TEXT_HTML:"text/html",APPLICATION_XML:"application/xhtml+xml"},dW="definitionurl",hW="definitionURL",pW={attributename:"attributeName",attributetype:"attributeType",basefrequency:"baseFrequency",baseprofile:"baseProfile",calcmode:"calcMode",clippathunits:"clipPathUnits",diffuseconstant:"diffuseConstant",edgemode:"edgeMode",filterunits:"filterUnits",glyphref:"glyphRef",gradienttransform:"gradientTransform",gradientunits:"gradientUnits",kernelmatrix:"kernelMatrix",kernelunitlength:"kernelUnitLength",keypoints:"keyPoints",keysplines:"keySplines",keytimes:"keyTimes",lengthadjust:"lengthAdjust",limitingconeangle:"limitingConeAngle",markerheight:"markerHeight",markerunits:"markerUnits",markerwidth:"markerWidth",maskcontentunits:"maskContentUnits",maskunits:"maskUnits",numoctaves:"numOctaves",pathlength:"pathLength",patterncontentunits:"patternContentUnits",patterntransform:"patternTransform",patternunits:"patternUnits",pointsatx:"pointsAtX",pointsaty:"pointsAtY",pointsatz:"pointsAtZ",preservealpha:"preserveAlpha",preserveaspectratio:"preserveAspectRatio",primitiveunits:"primitiveUnits",refx:"refX",refy:"refY",repeatcount:"repeatCount",repeatdur:"repeatDur",requiredextensions:"requiredExtensions",requiredfeatures:"requiredFeatures",specularconstant:"specularConstant",specularexponent:"specularExponent",spreadmethod:"spreadMethod",startoffset:"startOffset",stddeviation:"stdDeviation",stitchtiles:"stitchTiles",surfacescale:"surfaceScale",systemlanguage:"systemLanguage",tablevalues:"tableValues",targetx:"targetX",targety:"targetY",textlength:"textLength",viewbox:"viewBox",viewtarget:"viewTarget",xchannelselector:"xChannelSelector",ychannelselector:"yChannelSelector",zoomandpan:"zoomAndPan"},mW={"xlink:actuate":{prefix:"xlink",name:"actuate",namespace:Zt.XLINK},"xlink:arcrole":{prefix:"xlink",name:"arcrole",namespace:Zt.XLINK},"xlink:href":{prefix:"xlink",name:"href",namespace:Zt.XLINK},"xlink:role":{prefix:"xlink",name:"role",namespace:Zt.XLINK},"xlink:show":{prefix:"xlink",name:"show",namespace:Zt.XLINK},"xlink:title":{prefix:"xlink",name:"title",namespace:Zt.XLINK},"xlink:type":{prefix:"xlink",name:"type",namespace:Zt.XLINK},"xml:base":{prefix:"xml",name:"base",namespace:Zt.XML},"xml:lang":{prefix:"xml",name:"lang",namespace:Zt.XML},"xml:space":{prefix:"xml",name:"space",namespace:Zt.XML},xmlns:{prefix:"",name:"xmlns",namespace:Zt.XMLNS},"xmlns:xlink":{prefix:"xmlns",name:"xlink",namespace:Zt.XMLNS}},gW=xa.SVG_TAG_NAMES_ADJUSTMENT_MAP={altglyph:"altGlyph",altglyphdef:"altGlyphDef",altglyphitem:"altGlyphItem",animatecolor:"animateColor",animatemotion:"animateMotion",animatetransform:"animateTransform",clippath:"clipPath",feblend:"feBlend",fecolormatrix:"feColorMatrix",fecomponenttransfer:"feComponentTransfer",fecomposite:"feComposite",feconvolvematrix:"feConvolveMatrix",fediffuselighting:"feDiffuseLighting",fedisplacementmap:"feDisplacementMap",fedistantlight:"feDistantLight",feflood:"feFlood",fefunca:"feFuncA",fefuncb:"feFuncB",fefuncg:"feFuncG",fefuncr:"feFuncR",fegaussianblur:"feGaussianBlur",feimage:"feImage",femerge:"feMerge",femergenode:"feMergeNode",femorphology:"feMorphology",feoffset:"feOffset",fepointlight:"fePointLight",fespecularlighting:"feSpecularLighting",fespotlight:"feSpotLight",fetile:"feTile",feturbulence:"feTurbulence",foreignobject:"foreignObject",glyphref:"glyphRef",lineargradient:"linearGradient",radialgradient:"radialGradient",textpath:"textPath"},vW={[ue.B]:!0,[ue.BIG]:!0,[ue.BLOCKQUOTE]:!0,[ue.BODY]:!0,[ue.BR]:!0,[ue.CENTER]:!0,[ue.CODE]:!0,[ue.DD]:!0,[ue.DIV]:!0,[ue.DL]:!0,[ue.DT]:!0,[ue.EM]:!0,[ue.EMBED]:!0,[ue.H1]:!0,[ue.H2]:!0,[ue.H3]:!0,[ue.H4]:!0,[ue.H5]:!0,[ue.H6]:!0,[ue.HEAD]:!0,[ue.HR]:!0,[ue.I]:!0,[ue.IMG]:!0,[ue.LI]:!0,[ue.LISTING]:!0,[ue.MENU]:!0,[ue.META]:!0,[ue.NOBR]:!0,[ue.OL]:!0,[ue.P]:!0,[ue.PRE]:!0,[ue.RUBY]:!0,[ue.S]:!0,[ue.SMALL]:!0,[ue.SPAN]:!0,[ue.STRONG]:!0,[ue.STRIKE]:!0,[ue.SUB]:!0,[ue.SUP]:!0,[ue.TABLE]:!0,[ue.TT]:!0,[ue.U]:!0,[ue.UL]:!0,[ue.VAR]:!0};xa.causesExit=function(e){const t=e.tagName;return t===ue.FONT&&(qm.getTokenAttr(e,ud.COLOR)!==null||qm.getTokenAttr(e,ud.SIZE)!==null||qm.getTokenAttr(e,ud.FACE)!==null)?!0:vW[t]};xa.adjustTokenMathMLAttrs=function(e){for(let t=0;t0);for(let i=n;i=0;t--){let r=this.openElements.items[t];t===0&&(n=!0,this.fragmentContext&&(r=this.fragmentContext));const i=this.treeAdapter.getTagName(r),o=xW[i];if(o){this.insertionMode=o;break}else if(!n&&(i===p.TD||i===p.TH)){this.insertionMode=rp;break}else if(!n&&i===p.HEAD){this.insertionMode=eu;break}else if(i===p.SELECT){this._resetInsertionModeForSelect(t);break}else if(i===p.TEMPLATE){this.insertionMode=this.currentTmplInsertionMode;break}else if(i===p.HTML){this.insertionMode=this.headElement?tp:ep;break}else if(n){this.insertionMode=zi;break}}}_resetInsertionModeForSelect(t){if(t>0)for(let n=t-1;n>0;n--){const r=this.openElements.items[n],i=this.treeAdapter.getTagName(r);if(i===p.TEMPLATE)break;if(i===p.TABLE){this.insertionMode=z9;return}}this.insertionMode=H9}_pushTmplInsertionMode(t){this.tmplInsertionModeStack.push(t),this.tmplInsertionModeStackTop++,this.currentTmplInsertionMode=t}_popTmplInsertionMode(){this.tmplInsertionModeStack.pop(),this.tmplInsertionModeStackTop--,this.currentTmplInsertionMode=this.tmplInsertionModeStack[this.tmplInsertionModeStackTop]}_isElementCausesFosterParenting(t){const n=this.treeAdapter.getTagName(t);return n===p.TABLE||n===p.TBODY||n===p.TFOOT||n===p.THEAD||n===p.TR}_shouldFosterParentOnInsertion(){return this.fosterParentingEnabled&&this._isElementCausesFosterParenting(this.openElements.current)}_findFosterParentingLocation(){const t={parent:null,beforeElement:null};for(let n=this.openElements.stackTop;n>=0;n--){const r=this.openElements.items[n],i=this.treeAdapter.getTagName(r),o=this.treeAdapter.getNamespaceURI(r);if(i===p.TEMPLATE&&o===ie.HTML){t.parent=this.treeAdapter.getTemplateContent(r);break}else if(i===p.TABLE){t.parent=this.treeAdapter.getParentNode(r),t.parent?t.beforeElement=r:t.parent=this.openElements.items[n-1];break}}return t.parent||(t.parent=this.openElements.items[0]),t}_fosterParentElement(t){const n=this._findFosterParentingLocation();n.beforeElement?this.treeAdapter.insertBefore(n.parent,t,n.beforeElement):this.treeAdapter.appendChild(n.parent,t)}_fosterParentText(t){const n=this._findFosterParentingLocation();n.beforeElement?this.treeAdapter.insertTextBefore(n.parent,t,n.beforeElement):this.treeAdapter.insertText(n.parent,t)}_isSpecialElement(t){const n=this.treeAdapter.getTagName(t),r=this.treeAdapter.getNamespaceURI(t);return bs.SPECIAL_ELEMENTS[r][n]}}var RW=FW;function DW(e,t){let n=e.activeFormattingElements.getElementEntryInScopeWithTagName(t.tagName);return n?e.openElements.contains(n.element)?e.openElements.hasInScope(t.tagName)||(n=null):(e.activeFormattingElements.removeEntry(n),n=null):Vr(e,t),n}function MW(e,t){let n=null;for(let r=e.openElements.stackTop;r>=0;r--){const i=e.openElements.items[r];if(i===t.element)break;e._isSpecialElement(i)&&(n=i)}return n||(e.openElements.popUntilElementPopped(t.element),e.activeFormattingElements.removeEntry(t)),n}function PW(e,t,n){let r=t,i=e.openElements.getCommonAncestor(t);for(let o=0,a=i;a!==n;o++,a=i){i=e.openElements.getCommonAncestor(a);const s=e.activeFormattingElements.getElementEntry(a),l=s&&o>=IW;!s||l?(l&&e.activeFormattingElements.removeEntry(s),e.openElements.remove(a)):(a=OW(e,s),r===t&&(e.activeFormattingElements.bookmark=s),e.treeAdapter.detachNode(r),e.treeAdapter.appendChild(a,r),r=a)}return r}function OW(e,t){const n=e.treeAdapter.getNamespaceURI(t.element),r=e.treeAdapter.createElement(t.token.tagName,n,t.token.attrs);return e.openElements.replace(t.element,r),t.element=r,r}function LW(e,t,n){if(e._isElementCausesFosterParenting(t))e._fosterParentElement(n);else{const r=e.treeAdapter.getTagName(t),i=e.treeAdapter.getNamespaceURI(t);r===p.TEMPLATE&&i===ie.HTML&&(t=e.treeAdapter.getTemplateContent(t)),e.treeAdapter.appendChild(t,n)}}function BW(e,t,n){const r=e.treeAdapter.getNamespaceURI(n.element),i=n.token,o=e.treeAdapter.createElement(i.tagName,r,i.attrs);e._adoptNodes(t,o),e.treeAdapter.appendChild(t,o),e.activeFormattingElements.insertElementAfterBookmark(o,n.token),e.activeFormattingElements.removeEntry(n),e.openElements.remove(n.element),e.openElements.insertAfter(t,o)}function qo(e,t){let n;for(let r=0;r0?(e.openElements.generateImpliedEndTagsThoroughly(),e.openElements.currentTagName!==p.TEMPLATE&&e._err(en.closingOfElementWithOpenChildElements),e.openElements.popUntilTagNamePopped(p.TEMPLATE),e.activeFormattingElements.clearToLastMarker(),e._popTmplInsertionMode(),e._resetInsertionMode()):e._err(en.endTagWithoutMatchingOpenElement)}function a1(e,t){e.openElements.pop(),e.insertionMode=tp,e._processToken(t)}function GW(e,t){const n=t.tagName;n===p.HTML?Jn(e,t):n===p.BASEFONT||n===p.BGSOUND||n===p.HEAD||n===p.LINK||n===p.META||n===p.NOFRAMES||n===p.STYLE?Gt(e,t):n===p.NOSCRIPT?e._err(en.nestedNoscriptInHead):s1(e,t)}function $W(e,t){const n=t.tagName;n===p.NOSCRIPT?(e.openElements.pop(),e.insertionMode=eu):n===p.BR?s1(e,t):e._err(en.endTagWithoutMatchingOpenElement)}function s1(e,t){const n=t.type===N.EOF_TOKEN?en.openElementsLeftAfterEof:en.disallowedContentInNoscriptInHead;e._err(n),e.openElements.pop(),e.insertionMode=eu,e._processToken(t)}function VW(e,t){const n=t.tagName;n===p.HTML?Jn(e,t):n===p.BODY?(e._insertElement(t,ie.HTML),e.framesetOk=!1,e.insertionMode=zi):n===p.FRAMESET?(e._insertElement(t,ie.HTML),e.insertionMode=ip):n===p.BASE||n===p.BASEFONT||n===p.BGSOUND||n===p.LINK||n===p.META||n===p.NOFRAMES||n===p.SCRIPT||n===p.STYLE||n===p.TEMPLATE||n===p.TITLE?(e._err(en.abandonedHeadElementChild),e.openElements.push(e.headElement),Gt(e,t),e.openElements.remove(e.headElement)):n===p.HEAD?e._err(en.misplacedStartTagForHeadElement):l1(e,t)}function qW(e,t){const n=t.tagName;n===p.BODY||n===p.HTML||n===p.BR?l1(e,t):n===p.TEMPLATE?As(e,t):e._err(en.endTagWithoutMatchingOpenElement)}function l1(e,t){e._insertFakeElement(p.BODY),e.insertionMode=zi,e._processToken(t)}function Ha(e,t){e._reconstructActiveFormattingElements(),e._insertCharacters(t)}function Rf(e,t){e._reconstructActiveFormattingElements(),e._insertCharacters(t),e.framesetOk=!1}function YW(e,t){e.openElements.tmplCount===0&&e.treeAdapter.adoptAttributes(e.openElements.items[0],t.attrs)}function QW(e,t){const n=e.openElements.tryPeekProperlyNestedBodyElement();n&&e.openElements.tmplCount===0&&(e.framesetOk=!1,e.treeAdapter.adoptAttributes(n,t.attrs))}function XW(e,t){const n=e.openElements.tryPeekProperlyNestedBodyElement();e.framesetOk&&n&&(e.treeAdapter.detachNode(n),e.openElements.popAllUpToHtmlElement(),e._insertElement(t,ie.HTML),e.insertionMode=ip)}function $i(e,t){e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._insertElement(t,ie.HTML)}function ZW(e,t){e.openElements.hasInButtonScope(p.P)&&e._closePElement();const n=e.openElements.currentTagName;(n===p.H1||n===p.H2||n===p.H3||n===p.H4||n===p.H5||n===p.H6)&&e.openElements.pop(),e._insertElement(t,ie.HTML)}function UT(e,t){e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._insertElement(t,ie.HTML),e.skipNextNewLine=!0,e.framesetOk=!1}function JW(e,t){const n=e.openElements.tmplCount>0;(!e.formElement||n)&&(e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._insertElement(t,ie.HTML),n||(e.formElement=e.openElements.current))}function ej(e,t){e.framesetOk=!1;const n=t.tagName;for(let r=e.openElements.stackTop;r>=0;r--){const i=e.openElements.items[r],o=e.treeAdapter.getTagName(i);let a=null;if(n===p.LI&&o===p.LI?a=p.LI:(n===p.DD||n===p.DT)&&(o===p.DD||o===p.DT)&&(a=o),a){e.openElements.generateImpliedEndTagsWithExclusion(a),e.openElements.popUntilTagNamePopped(a);break}if(o!==p.ADDRESS&&o!==p.DIV&&o!==p.P&&e._isSpecialElement(i))break}e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._insertElement(t,ie.HTML)}function tj(e,t){e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._insertElement(t,ie.HTML),e.tokenizer.state=N.MODE.PLAINTEXT}function nj(e,t){e.openElements.hasInScope(p.BUTTON)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(p.BUTTON)),e._reconstructActiveFormattingElements(),e._insertElement(t,ie.HTML),e.framesetOk=!1}function rj(e,t){const n=e.activeFormattingElements.getElementEntryInScopeWithTagName(p.A);n&&(qo(e,t),e.openElements.remove(n.element),e.activeFormattingElements.removeEntry(n)),e._reconstructActiveFormattingElements(),e._insertElement(t,ie.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}function Ks(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,ie.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}function ij(e,t){e._reconstructActiveFormattingElements(),e.openElements.hasInScope(p.NOBR)&&(qo(e,t),e._reconstructActiveFormattingElements()),e._insertElement(t,ie.HTML),e.activeFormattingElements.pushElement(e.openElements.current,t)}function KT(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,ie.HTML),e.activeFormattingElements.insertMarker(),e.framesetOk=!1}function oj(e,t){e.treeAdapter.getDocumentMode(e.document)!==bs.DOCUMENT_MODE.QUIRKS&&e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._insertElement(t,ie.HTML),e.framesetOk=!1,e.insertionMode=dn}function Vs(e,t){e._reconstructActiveFormattingElements(),e._appendElement(t,ie.HTML),e.framesetOk=!1,t.ackSelfClosing=!0}function aj(e,t){e._reconstructActiveFormattingElements(),e._appendElement(t,ie.HTML);const n=N.getTokenAttr(t,Kb.TYPE);(!n||n.toLowerCase()!==Wb)&&(e.framesetOk=!1),t.ackSelfClosing=!0}function WT(e,t){e._appendElement(t,ie.HTML),t.ackSelfClosing=!0}function sj(e,t){e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._appendElement(t,ie.HTML),e.framesetOk=!1,t.ackSelfClosing=!0}function lj(e,t){t.tagName=p.IMG,Vs(e,t)}function uj(e,t){e._insertElement(t,ie.HTML),e.skipNextNewLine=!0,e.tokenizer.state=N.MODE.RCDATA,e.originalInsertionMode=e.insertionMode,e.framesetOk=!1,e.insertionMode=rh}function cj(e,t){e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._reconstructActiveFormattingElements(),e.framesetOk=!1,e._switchToTextParsing(t,N.MODE.RAWTEXT)}function fj(e,t){e.framesetOk=!1,e._switchToTextParsing(t,N.MODE.RAWTEXT)}function jT(e,t){e._switchToTextParsing(t,N.MODE.RAWTEXT)}function dj(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,ie.HTML),e.framesetOk=!1,e.insertionMode===dn||e.insertionMode===np||e.insertionMode===Rr||e.insertionMode===go||e.insertionMode===rp?e.insertionMode=z9:e.insertionMode=H9}function GT(e,t){e.openElements.currentTagName===p.OPTION&&e.openElements.pop(),e._reconstructActiveFormattingElements(),e._insertElement(t,ie.HTML)}function $T(e,t){e.openElements.hasInScope(p.RUBY)&&e.openElements.generateImpliedEndTags(),e._insertElement(t,ie.HTML)}function hj(e,t){e.openElements.hasInScope(p.RUBY)&&e.openElements.generateImpliedEndTagsWithExclusion(p.RTC),e._insertElement(t,ie.HTML)}function pj(e,t){e.openElements.hasInButtonScope(p.P)&&e._closePElement(),e._insertElement(t,ie.HTML)}function mj(e,t){e._reconstructActiveFormattingElements(),Fi.adjustTokenMathMLAttrs(t),Fi.adjustTokenXMLAttrs(t),t.selfClosing?e._appendElement(t,ie.MATHML):e._insertElement(t,ie.MATHML),t.ackSelfClosing=!0}function gj(e,t){e._reconstructActiveFormattingElements(),Fi.adjustTokenSVGAttrs(t),Fi.adjustTokenXMLAttrs(t),t.selfClosing?e._appendElement(t,ie.SVG):e._insertElement(t,ie.SVG),t.ackSelfClosing=!0}function Sr(e,t){e._reconstructActiveFormattingElements(),e._insertElement(t,ie.HTML)}function Jn(e,t){const n=t.tagName;switch(n.length){case 1:n===p.I||n===p.S||n===p.B||n===p.U?Ks(e,t):n===p.P?$i(e,t):n===p.A?rj(e,t):Sr(e,t);break;case 2:n===p.DL||n===p.OL||n===p.UL?$i(e,t):n===p.H1||n===p.H2||n===p.H3||n===p.H4||n===p.H5||n===p.H6?ZW(e,t):n===p.LI||n===p.DD||n===p.DT?ej(e,t):n===p.EM||n===p.TT?Ks(e,t):n===p.BR?Vs(e,t):n===p.HR?sj(e,t):n===p.RB?$T(e,t):n===p.RT||n===p.RP?hj(e,t):n!==p.TH&&n!==p.TD&&n!==p.TR&&Sr(e,t);break;case 3:n===p.DIV||n===p.DIR||n===p.NAV?$i(e,t):n===p.PRE?UT(e,t):n===p.BIG?Ks(e,t):n===p.IMG||n===p.WBR?Vs(e,t):n===p.XMP?cj(e,t):n===p.SVG?gj(e,t):n===p.RTC?$T(e,t):n!==p.COL&&Sr(e,t);break;case 4:n===p.HTML?YW(e,t):n===p.BASE||n===p.LINK||n===p.META?Gt(e,t):n===p.BODY?QW(e,t):n===p.MAIN||n===p.MENU?$i(e,t):n===p.FORM?JW(e,t):n===p.CODE||n===p.FONT?Ks(e,t):n===p.NOBR?ij(e,t):n===p.AREA?Vs(e,t):n===p.MATH?mj(e,t):n===p.MENU?pj(e,t):n!==p.HEAD&&Sr(e,t);break;case 5:n===p.STYLE||n===p.TITLE?Gt(e,t):n===p.ASIDE?$i(e,t):n===p.SMALL?Ks(e,t):n===p.TABLE?oj(e,t):n===p.EMBED?Vs(e,t):n===p.INPUT?aj(e,t):n===p.PARAM||n===p.TRACK?WT(e,t):n===p.IMAGE?lj(e,t):n!==p.FRAME&&n!==p.TBODY&&n!==p.TFOOT&&n!==p.THEAD&&Sr(e,t);break;case 6:n===p.SCRIPT?Gt(e,t):n===p.CENTER||n===p.FIGURE||n===p.FOOTER||n===p.HEADER||n===p.HGROUP||n===p.DIALOG?$i(e,t):n===p.BUTTON?nj(e,t):n===p.STRIKE||n===p.STRONG?Ks(e,t):n===p.APPLET||n===p.OBJECT?KT(e,t):n===p.KEYGEN?Vs(e,t):n===p.SOURCE?WT(e,t):n===p.IFRAME?fj(e,t):n===p.SELECT?dj(e,t):n===p.OPTION?GT(e,t):Sr(e,t);break;case 7:n===p.BGSOUND?Gt(e,t):n===p.DETAILS||n===p.ADDRESS||n===p.ARTICLE||n===p.SECTION||n===p.SUMMARY?$i(e,t):n===p.LISTING?UT(e,t):n===p.MARQUEE?KT(e,t):n===p.NOEMBED?jT(e,t):n!==p.CAPTION&&Sr(e,t);break;case 8:n===p.BASEFONT?Gt(e,t):n===p.FRAMESET?XW(e,t):n===p.FIELDSET?$i(e,t):n===p.TEXTAREA?uj(e,t):n===p.TEMPLATE?Gt(e,t):n===p.NOSCRIPT?e.options.scriptingEnabled?jT(e,t):Sr(e,t):n===p.OPTGROUP?GT(e,t):n!==p.COLGROUP&&Sr(e,t);break;case 9:n===p.PLAINTEXT?tj(e,t):Sr(e,t);break;case 10:n===p.BLOCKQUOTE||n===p.FIGCAPTION?$i(e,t):Sr(e,t);break;default:Sr(e,t)}}function vj(e){e.openElements.hasInScope(p.BODY)&&(e.insertionMode=U9)}function Ej(e,t){e.openElements.hasInScope(p.BODY)&&(e.insertionMode=U9,e._processToken(t))}function Ho(e,t){const n=t.tagName;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(n))}function yj(e){const t=e.openElements.tmplCount>0,n=e.formElement;t||(e.formElement=null),(n||t)&&e.openElements.hasInScope(p.FORM)&&(e.openElements.generateImpliedEndTags(),t?e.openElements.popUntilTagNamePopped(p.FORM):e.openElements.remove(n))}function Tj(e){e.openElements.hasInButtonScope(p.P)||e._insertFakeElement(p.P),e._closePElement()}function Cj(e){e.openElements.hasInListItemScope(p.LI)&&(e.openElements.generateImpliedEndTagsWithExclusion(p.LI),e.openElements.popUntilTagNamePopped(p.LI))}function Sj(e,t){const n=t.tagName;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTagsWithExclusion(n),e.openElements.popUntilTagNamePopped(n))}function _j(e){e.openElements.hasNumberedHeaderInScope()&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilNumberedHeaderPopped())}function VT(e,t){const n=t.tagName;e.openElements.hasInScope(n)&&(e.openElements.generateImpliedEndTags(),e.openElements.popUntilTagNamePopped(n),e.activeFormattingElements.clearToLastMarker())}function bj(e){e._reconstructActiveFormattingElements(),e._insertFakeElement(p.BR),e.openElements.pop(),e.framesetOk=!1}function Vr(e,t){const n=t.tagName;for(let r=e.openElements.stackTop;r>0;r--){const i=e.openElements.items[r];if(e.treeAdapter.getTagName(i)===n){e.openElements.generateImpliedEndTagsWithExclusion(n),e.openElements.popUntilElementPopped(i);break}if(e._isSpecialElement(i))break}}function K9(e,t){const n=t.tagName;switch(n.length){case 1:n===p.A||n===p.B||n===p.I||n===p.S||n===p.U?qo(e,t):n===p.P?Tj(e):Vr(e,t);break;case 2:n===p.DL||n===p.UL||n===p.OL?Ho(e,t):n===p.LI?Cj(e):n===p.DD||n===p.DT?Sj(e,t):n===p.H1||n===p.H2||n===p.H3||n===p.H4||n===p.H5||n===p.H6?_j(e):n===p.BR?bj(e):n===p.EM||n===p.TT?qo(e,t):Vr(e,t);break;case 3:n===p.BIG?qo(e,t):n===p.DIR||n===p.DIV||n===p.NAV||n===p.PRE?Ho(e,t):Vr(e,t);break;case 4:n===p.BODY?vj(e):n===p.HTML?Ej(e,t):n===p.FORM?yj(e):n===p.CODE||n===p.FONT||n===p.NOBR?qo(e,t):n===p.MAIN||n===p.MENU?Ho(e,t):Vr(e,t);break;case 5:n===p.ASIDE?Ho(e,t):n===p.SMALL?qo(e,t):Vr(e,t);break;case 6:n===p.CENTER||n===p.FIGURE||n===p.FOOTER||n===p.HEADER||n===p.HGROUP||n===p.DIALOG?Ho(e,t):n===p.APPLET||n===p.OBJECT?VT(e,t):n===p.STRIKE||n===p.STRONG?qo(e,t):Vr(e,t);break;case 7:n===p.ADDRESS||n===p.ARTICLE||n===p.DETAILS||n===p.SECTION||n===p.SUMMARY||n===p.LISTING?Ho(e,t):n===p.MARQUEE?VT(e,t):Vr(e,t);break;case 8:n===p.FIELDSET?Ho(e,t):n===p.TEMPLATE?As(e,t):Vr(e,t);break;case 10:n===p.BLOCKQUOTE||n===p.FIGCAPTION?Ho(e,t):Vr(e,t);break;default:Vr(e,t)}}function Vi(e,t){e.tmplInsertionModeStackTop>-1?Zb(e,t):e.stopped=!0}function Aj(e,t){t.tagName===p.SCRIPT&&(e.pendingScript=e.openElements.current),e.openElements.pop(),e.insertionMode=e.originalInsertionMode}function wj(e,t){e._err(en.eofInElementThatCanContainOnlyText),e.openElements.pop(),e.insertionMode=e.originalInsertionMode,e._processToken(t)}function qi(e,t){const n=e.openElements.currentTagName;n===p.TABLE||n===p.TBODY||n===p.TFOOT||n===p.THEAD||n===p.TR?(e.pendingCharacterTokens=[],e.hasNonWhitespacePendingCharacterToken=!1,e.originalInsertionMode=e.insertionMode,e.insertionMode=$b,e._processToken(t)):br(e,t)}function kj(e,t){e.openElements.clearBackToTableContext(),e.activeFormattingElements.insertMarker(),e._insertElement(t,ie.HTML),e.insertionMode=np}function Ij(e,t){e.openElements.clearBackToTableContext(),e._insertElement(t,ie.HTML),e.insertionMode=kc}function xj(e,t){e.openElements.clearBackToTableContext(),e._insertFakeElement(p.COLGROUP),e.insertionMode=kc,e._processToken(t)}function Nj(e,t){e.openElements.clearBackToTableContext(),e._insertElement(t,ie.HTML),e.insertionMode=Rr}function Fj(e,t){e.openElements.clearBackToTableContext(),e._insertFakeElement(p.TBODY),e.insertionMode=Rr,e._processToken(t)}function Rj(e,t){e.openElements.hasInTableScope(p.TABLE)&&(e.openElements.popUntilTagNamePopped(p.TABLE),e._resetInsertionMode(),e._processToken(t))}function Dj(e,t){const n=N.getTokenAttr(t,Kb.TYPE);n&&n.toLowerCase()===Wb?e._appendElement(t,ie.HTML):br(e,t),t.ackSelfClosing=!0}function Mj(e,t){!e.formElement&&e.openElements.tmplCount===0&&(e._insertElement(t,ie.HTML),e.formElement=e.openElements.current,e.openElements.pop())}function W9(e,t){const n=t.tagName;switch(n.length){case 2:n===p.TD||n===p.TH||n===p.TR?Fj(e,t):br(e,t);break;case 3:n===p.COL?xj(e,t):br(e,t);break;case 4:n===p.FORM?Mj(e,t):br(e,t);break;case 5:n===p.TABLE?Rj(e,t):n===p.STYLE?Gt(e,t):n===p.TBODY||n===p.TFOOT||n===p.THEAD?Nj(e,t):n===p.INPUT?Dj(e,t):br(e,t);break;case 6:n===p.SCRIPT?Gt(e,t):br(e,t);break;case 7:n===p.CAPTION?kj(e,t):br(e,t);break;case 8:n===p.COLGROUP?Ij(e,t):n===p.TEMPLATE?Gt(e,t):br(e,t);break;default:br(e,t)}}function j9(e,t){const n=t.tagName;n===p.TABLE?e.openElements.hasInTableScope(p.TABLE)&&(e.openElements.popUntilTagNamePopped(p.TABLE),e._resetInsertionMode()):n===p.TEMPLATE?As(e,t):n!==p.BODY&&n!==p.CAPTION&&n!==p.COL&&n!==p.COLGROUP&&n!==p.HTML&&n!==p.TBODY&&n!==p.TD&&n!==p.TFOOT&&n!==p.TH&&n!==p.THEAD&&n!==p.TR&&br(e,t)}function br(e,t){const n=e.fosterParentingEnabled;e.fosterParentingEnabled=!0,e._processTokenInBodyMode(t),e.fosterParentingEnabled=n}function Pj(e,t){e.pendingCharacterTokens.push(t)}function Oj(e,t){e.pendingCharacterTokens.push(t),e.hasNonWhitespacePendingCharacterToken=!0}function Nu(e,t){let n=0;if(e.hasNonWhitespacePendingCharacterToken)for(;n0?(e.openElements.popUntilTagNamePopped(p.TEMPLATE),e.activeFormattingElements.clearToLastMarker(),e._popTmplInsertionMode(),e._resetInsertionMode(),e._processToken(t)):e.stopped=!0}function Xj(e,t){t.tagName===p.HTML?Jn(e,t):ah(e,t)}function Zj(e,t){t.tagName===p.HTML?e.fragmentContext||(e.insertionMode=qb):ah(e,t)}function ah(e,t){e.insertionMode=zi,e._processToken(t)}function Jj(e,t){const n=t.tagName;n===p.HTML?Jn(e,t):n===p.FRAMESET?e._insertElement(t,ie.HTML):n===p.FRAME?(e._appendElement(t,ie.HTML),t.ackSelfClosing=!0):n===p.NOFRAMES&&Gt(e,t)}function eG(e,t){t.tagName===p.FRAMESET&&!e.openElements.isRootHtmlElementCurrent()&&(e.openElements.pop(),!e.fragmentContext&&e.openElements.currentTagName!==p.FRAMESET&&(e.insertionMode=Vb))}function tG(e,t){const n=t.tagName;n===p.HTML?Jn(e,t):n===p.NOFRAMES&&Gt(e,t)}function nG(e,t){t.tagName===p.HTML&&(e.insertionMode=Yb)}function rG(e,t){t.tagName===p.HTML?Jn(e,t):cd(e,t)}function cd(e,t){e.insertionMode=zi,e._processToken(t)}function iG(e,t){const n=t.tagName;n===p.HTML?Jn(e,t):n===p.NOFRAMES&&Gt(e,t)}function oG(e,t){t.chars=AW.REPLACEMENT_CHARACTER,e._insertCharacters(t)}function aG(e,t){e._insertCharacters(t),e.framesetOk=!1}function sG(e,t){if(Fi.causesExit(t)&&!e.fragmentContext){for(;e.treeAdapter.getNamespaceURI(e.openElements.current)!==ie.HTML&&!e._isIntegrationPoint(e.openElements.current);)e.openElements.pop();e._processToken(t)}else{const n=e._getAdjustedCurrentElement(),r=e.treeAdapter.getNamespaceURI(n);r===ie.MATHML?Fi.adjustTokenMathMLAttrs(t):r===ie.SVG&&(Fi.adjustTokenSVGTagName(t),Fi.adjustTokenSVGAttrs(t)),Fi.adjustTokenXMLAttrs(t),t.selfClosing?e._appendElement(t,r):e._insertElement(t,r),t.ackSelfClosing=!0}}function lG(e,t){for(let n=e.openElements.stackTop;n>0;n--){const r=e.openElements.items[n];if(e.treeAdapter.getNamespaceURI(r)===ie.HTML){e._processToken(t);break}if(e.treeAdapter.getTagName(r).toLowerCase()===t.tagName){e.openElements.popUntilElementPopped(r);break}}}const uG=Ea(RW),qT=/[#.]/g;function cG(e,t){const n=e||"",r={};let i=0,o,a;for(;i-1&&aa)return{line:s+1,column:a-(s>0?n[s-1]:0)+1,offset:a}}return{line:void 0,column:void 0,offset:void 0}}function o(a){const s=a&&a.line,l=a&&a.column;if(typeof s=="number"&&typeof l=="number"&&!Number.isNaN(s)&&!Number.isNaN(l)&&s-1 in n){const u=(n[s-2]||0)+l-1||0;if(u>-1&&u{const I=A;if(I.value.stitch&&D!==null&&F!==null)return D.children[F]=I.value.stitch,F}),e.type!=="root"&&f.type==="root"&&f.children.length===1)return f.children[0];return f;function d(){const A={nodeName:"template",tagName:"template",attrs:[],namespaceURI:G1.html,childNodes:[]},F={nodeName:"documentmock",tagName:"documentmock",attrs:[],namespaceURI:G1.html,childNodes:[]},D={nodeName:"#document-fragment",childNodes:[]};if(i._bootstrap(F,A),i._pushTmplInsertionMode(PG),i._initTokenizerForFragmentParsing(),i._insertFakeRootElement(),i._resetInsertionMode(),i._findFormInFragmentContext(),s=i.tokenizer,!s)throw new Error("Expected `tokenizer`");return l=s.preprocessor,c=s.__mixins[0],u=c.posTracker,o(e),b(),i._adoptNodes(F.childNodes[0],D),D}function h(){const A=i.treeAdapter.createDocument();if(i._bootstrap(A,void 0),s=i.tokenizer,!s)throw new Error("Expected `tokenizer`");return l=s.preprocessor,c=s.__mixins[0],u=c.posTracker,o(e),b(),A}function v(A){let F=-1;if(A)for(;++FiA(t,n,e)}const QG="_container_1a2j4_1",XG="_chatRoot_1a2j4_15",ZG="_chatContainer_1a2j4_35",JG="_chatEmptyState_1a2j4_59",e$="_chatEmptyStateTitle_1a2j4_75",t$="_chatEmptyStateSubtitle_1a2j4_101",n$="_chatIcon_1a2j4_129",r$="_chatMessageStream_1a2j4_139",i$="_chatMessageUser_1a2j4_163",o$="_chatMessageUserMessage_1a2j4_175",a$="_chatMessageGpt_1a2j4_213",s$="_chatInput_1a2j4_225",l$="_clearChatBroom_1a2j4_251",u$="_stopGeneratingContainer_1a2j4_287",c$="_stopGeneratingIcon_1a2j4_321",f$="_stopGeneratingText_1a2j4_333",d$="_citationPanel_1a2j4_365",h$="_citationPanelHeaderContainer_1a2j4_401",p$="_citationPanelHeader_1a2j4_401",m$="_citationPanelDismiss_1a2j4_433",g$="_citationPanelTitle_1a2j4_455",v$="_citationPanelContent_1a2j4_477",Qe={container:QG,chatRoot:XG,chatContainer:ZG,chatEmptyState:JG,chatEmptyStateTitle:e$,chatEmptyStateSubtitle:t$,chatIcon:n$,chatMessageStream:r$,chatMessageUser:i$,chatMessageUserMessage:o$,chatMessageGpt:a$,chatInput:s$,clearChatBroom:l$,stopGeneratingContainer:u$,stopGeneratingIcon:c$,stopGeneratingText:f$,citationPanel:d$,citationPanelHeaderContainer:h$,citationPanelHeader:p$,citationPanelDismiss:m$,citationPanelTitle:g$,citationPanelContent:v$};async function E$(e,t){return await fetch("/api/conversation/custom",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({messages:e.messages,conversation_id:e.id}),signal:t})}const y$="_answerContainer_19h6v_1",T$="_answerText_19h6v_23",C$="_answerFooter_19h6v_59",S$="_answerDisclaimerContainer_19h6v_77",_$="_answerDisclaimer_19h6v_77",b$="_citationContainer_19h6v_119",A$="_citation_19h6v_119",w$="_accordionIcon_19h6v_223",k$="_accordionTitle_19h6v_253",I$="_clickableSup_19h6v_287",Yi={answerContainer:y$,answerText:T$,answerFooter:C$,answerDisclaimerContainer:S$,answerDisclaimer:_$,citationContainer:b$,citation:A$,accordionIcon:w$,accordionTitle:k$,clickableSup:I$};var x$=typeof global=="object"&&global&&global.Object===Object&&global;const oA=x$;var N$=typeof self=="object"&&self&&self.Object===Object&&self,F$=oA||N$||Function("return this")();const Ki=F$;var R$=Ki.Symbol;const Ul=R$;var aA=Object.prototype,D$=aA.hasOwnProperty,M$=aA.toString,Fu=Ul?Ul.toStringTag:void 0;function P$(e){var t=D$.call(e,Fu),n=e[Fu];try{e[Fu]=void 0;var r=!0}catch{}var i=M$.call(e);return r&&(t?e[Fu]=n:delete e[Fu]),i}var O$=Object.prototype,L$=O$.toString;function B$(e){return L$.call(e)}var H$="[object Null]",z$="[object Undefined]",ZT=Ul?Ul.toStringTag:void 0;function Ic(e){return e==null?e===void 0?z$:H$:ZT&&ZT in Object(e)?P$(e):B$(e)}function xc(e){return e!=null&&typeof e=="object"}var U$=Array.isArray;const V9=U$;function Nc(e){var t=typeof e;return e!=null&&(t=="object"||t=="function")}var K$="[object AsyncFunction]",W$="[object Function]",j$="[object GeneratorFunction]",G$="[object Proxy]";function sA(e){if(!Nc(e))return!1;var t=Ic(e);return t==W$||t==j$||t==K$||t==G$}var $$=Ki["__core-js_shared__"];const Ym=$$;var JT=function(){var e=/[^.]+$/.exec(Ym&&Ym.keys&&Ym.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""}();function V$(e){return!!JT&&JT in e}var q$=Function.prototype,Y$=q$.toString;function ws(e){if(e!=null){try{return Y$.call(e)}catch{}try{return e+""}catch{}}return""}var Q$=/[\\^$.*+?()[\]{}|]/g,X$=/^\[object .+?Constructor\]$/,Z$=Function.prototype,J$=Object.prototype,eV=Z$.toString,tV=J$.hasOwnProperty,nV=RegExp("^"+eV.call(tV).replace(Q$,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function rV(e){if(!Nc(e)||V$(e))return!1;var t=sA(e)?nV:X$;return t.test(ws(e))}function iV(e,t){return e==null?void 0:e[t]}function ks(e,t){var n=iV(e,t);return rV(n)?n:void 0}var oV=ks(Ki,"WeakMap");const Dg=oV;var e4=Object.create,aV=function(){function e(){}return function(t){if(!Nc(t))return{};if(e4)return e4(t);e.prototype=t;var n=new e;return e.prototype=void 0,n}}();const sV=aV;function lV(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n-1&&e%1==0&&e-1&&e%1==0&&e<=gV}function dA(e){return e!=null&&fA(e.length)&&!sA(e)}var vV=Object.prototype;function q9(e){var t=e&&e.constructor,n=typeof t=="function"&&t.prototype||vV;return e===n}function EV(e,t){for(var n=-1,r=Array(e);++n-1}function Rq(e,t){var n=this.__data__,r=ap(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this}function _o(e){var t=-1,n=e==null?0:e.length;for(this.clear();++t{let s=a.slice(r,a.length-1),l=IQ(e.citations[Number(s)-1]);i.find(u=>u.id===s)||(t=t.replaceAll(a,` ^${++o}^ `),l.id=s,l.reindex_id=o.toString(),i.push(l))}),{citations:i,markdownFormatText:t}}function NQ(){return e=>{zl(e,["text"],(t,n,r)=>{if(t.type!=="text")return;const{value:i}=t,o=i.split(/\^/);if(o.length===1||o.length%2===0)return;const a=o.map((s,l)=>l%2===0?{type:"text",value:s}:{type:"superscript",data:{hName:"sup"},children:[{type:"text",value:s}]});r.children.splice(n,1,...a)}),zl(e,["text"],(t,n,r)=>{if(t.type!=="text")return;const{value:i}=t,o=i.split(/\~/);if(o.length===1||o.length%2===0)return;const a=o.map((s,l)=>l%2===0?{type:"text",value:s}:{type:"subscript",data:{hName:"sub"},children:[{type:"text",value:s}]});r.children.splice(n,1,...a)})}}const T4=({answer:e,onCitationClicked:t})=>{const[n,{toggle:r}]=u_(!1),i=50,o=E.useMemo(()=>xQ(e),[e]),[a,s]=E.useState(n),l=()=>{s(!a),r()};E.useEffect(()=>{s(n)},[n]);const u=(c,f,d=!1)=>{let h="";if(c.filepath&&c.chunk_id!=null)if(d&&c.filepath.length>i){const v=c.filepath.length;h=`${c.filepath.substring(0,20)}...${c.filepath.substring(v-20)} - Part ${parseInt(c.chunk_id)+1}`}else h=`${c.filepath} - Part ${parseInt(c.chunk_id)+1}`;else h=`Citation ${f}`;return h};return E.useEffect(()=>{const c=()=>{alert("Please consider where you paste this content.")};return document.addEventListener("copy",c),()=>{document.removeEventListener("copy",c)}},[]),re(r0,{children:gt(ln,{className:Yi.answerContainer,children:[re(ln.Item,{grow:!0,children:re(Qh,{remarkPlugins:[wb,NQ],children:o.markdownFormatText,className:Yi.answerText})}),gt(ln,{horizontal:!0,className:Yi.answerFooter,children:[!!o.citations.length&&re(ln.Item,{"aria-label":"References",children:re(ln,{style:{width:"100%"},children:gt(ln,{horizontal:!0,horizontalAlign:"start",verticalAlign:"center",children:[re(GM,{className:Yi.accordionTitle,onClick:r,children:re("span",{children:o.citations.length>1?o.citations.length+" references":"1 reference"})}),re(Yd,{className:Yi.accordionIcon,onClick:l,iconName:a?"ChevronDown":"ChevronRight"})]})})}),re(ln.Item,{className:Yi.answerDisclaimerContainer,children:re("span",{className:Yi.answerDisclaimer,children:"AI-generated content may be incorrect"})})]}),a&&re("div",{style:{marginTop:8,display:"flex",flexFlow:"wrap column",maxHeight:"150px",gap:"4px"},children:o.citations.map((c,f)=>gt("span",{title:u(c,++f),onClick:()=>t(c),className:Yi.citationContainer,children:[re("div",{className:Yi.citation,children:f}),u(c,f,!0)]},f))})]})})},FQ="/assets/Send-a9ab2367.svg",RQ="/assets/mic-outline-75e9b1db.svg",DQ="_questionInputContainer_thazn_1",MQ="_questionInputTextArea_thazn_31",PQ="_questionInputSendButtonContainer_thazn_49",OQ="_questionInputSendButton_thazn_49",LQ="_questionInputSendButtonDisabled_thazn_75",BQ="_questionInputBottomBorder_thazn_91",HQ="_questionInputOptionsButton_thazn_113",zQ="_questionInputMicrophone_thazn_125",UQ="_microphoneAndSendContainer_thazn_137",Qi={questionInputContainer:DQ,questionInputTextArea:MQ,questionInputSendButtonContainer:PQ,questionInputSendButton:OQ,questionInputSendButtonDisabled:LQ,questionInputBottomBorder:BQ,questionInputOptionsButton:HQ,questionInputMicrophone:zQ,microphoneAndSendContainer:UQ};function C4(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function te(e){for(var t=1;te.length)&&(t=e.length);for(var n=0,r=new Array(t);n-1;i--){var o=n[i],a=(o.tagName||"").toUpperCase();["STYLE","LINK"].indexOf(a)>-1&&(r=o)}return rt.head.insertBefore(t,r),e}}var dX="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";function Z1(){for(var e=12,t="";e-- >0;)t+=dX[Math.random()*62|0];return t}function iu(e){for(var t=[],n=(e||[]).length>>>0;n--;)t[n]=e[n];return t}function aE(e){return e.classList?iu(e.classList):(e.getAttribute("class")||"").split(" ").filter(function(t){return t})}function HA(e){return"".concat(e).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function hX(e){return Object.keys(e||{}).reduce(function(t,n){return t+"".concat(n,'="').concat(HA(e[n]),'" ')},"").trim()}function lp(e){return Object.keys(e||{}).reduce(function(t,n){return t+"".concat(n,": ").concat(e[n].trim(),";")},"")}function sE(e){return e.size!==Mi.size||e.x!==Mi.x||e.y!==Mi.y||e.rotate!==Mi.rotate||e.flipX||e.flipY}function pX(e){var t=e.transform,n=e.containerWidth,r=e.iconWidth,i={transform:"translate(".concat(n/2," 256)")},o="translate(".concat(t.x*32,", ").concat(t.y*32,") "),a="scale(".concat(t.size/16*(t.flipX?-1:1),", ").concat(t.size/16*(t.flipY?-1:1),") "),s="rotate(".concat(t.rotate," 0 0)"),l={transform:"".concat(o," ").concat(a," ").concat(s)},u={transform:"translate(".concat(r/2*-1," -256)")};return{outer:i,inner:l,path:u}}function mX(e){var t=e.transform,n=e.width,r=n===void 0?Bg:n,i=e.height,o=i===void 0?Bg:i,a=e.startCentered,s=a===void 0?!1:a,l="";return s&&RA?l+="translate(".concat(t.x/zo-r/2,"em, ").concat(t.y/zo-o/2,"em) "):s?l+="translate(calc(-50% + ".concat(t.x/zo,"em), calc(-50% + ").concat(t.y/zo,"em)) "):l+="translate(".concat(t.x/zo,"em, ").concat(t.y/zo,"em) "),l+="scale(".concat(t.size/zo*(t.flipX?-1:1),", ").concat(t.size/zo*(t.flipY?-1:1),") "),l+="rotate(".concat(t.rotate,"deg) "),l}var gX=`:root, :host { + --fa-font-solid: normal 900 1em/1 "Font Awesome 6 Solid"; + --fa-font-regular: normal 400 1em/1 "Font Awesome 6 Regular"; + --fa-font-light: normal 300 1em/1 "Font Awesome 6 Light"; + --fa-font-thin: normal 100 1em/1 "Font Awesome 6 Thin"; + --fa-font-duotone: normal 900 1em/1 "Font Awesome 6 Duotone"; + --fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 6 Sharp"; + --fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 6 Sharp"; + --fa-font-sharp-light: normal 300 1em/1 "Font Awesome 6 Sharp"; + --fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 6 Sharp"; + --fa-font-brands: normal 400 1em/1 "Font Awesome 6 Brands"; +} + +svg:not(:root).svg-inline--fa, svg:not(:host).svg-inline--fa { + overflow: visible; + box-sizing: content-box; +} + +.svg-inline--fa { + display: var(--fa-display, inline-block); + height: 1em; + overflow: visible; + vertical-align: -0.125em; +} +.svg-inline--fa.fa-2xs { + vertical-align: 0.1em; +} +.svg-inline--fa.fa-xs { + vertical-align: 0em; +} +.svg-inline--fa.fa-sm { + vertical-align: -0.0714285705em; +} +.svg-inline--fa.fa-lg { + vertical-align: -0.2em; +} +.svg-inline--fa.fa-xl { + vertical-align: -0.25em; +} +.svg-inline--fa.fa-2xl { + vertical-align: -0.3125em; +} +.svg-inline--fa.fa-pull-left { + margin-right: var(--fa-pull-margin, 0.3em); + width: auto; +} +.svg-inline--fa.fa-pull-right { + margin-left: var(--fa-pull-margin, 0.3em); + width: auto; +} +.svg-inline--fa.fa-li { + width: var(--fa-li-width, 2em); + top: 0.25em; +} +.svg-inline--fa.fa-fw { + width: var(--fa-fw-width, 1.25em); +} + +.fa-layers svg.svg-inline--fa { + bottom: 0; + left: 0; + margin: auto; + position: absolute; + right: 0; + top: 0; +} + +.fa-layers-counter, .fa-layers-text { + display: inline-block; + position: absolute; + text-align: center; +} + +.fa-layers { + display: inline-block; + height: 1em; + position: relative; + text-align: center; + vertical-align: -0.125em; + width: 1em; +} +.fa-layers svg.svg-inline--fa { + -webkit-transform-origin: center center; + transform-origin: center center; +} + +.fa-layers-text { + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + -webkit-transform-origin: center center; + transform-origin: center center; +} + +.fa-layers-counter { + background-color: var(--fa-counter-background-color, #ff253a); + border-radius: var(--fa-counter-border-radius, 1em); + box-sizing: border-box; + color: var(--fa-inverse, #fff); + line-height: var(--fa-counter-line-height, 1); + max-width: var(--fa-counter-max-width, 5em); + min-width: var(--fa-counter-min-width, 1.5em); + overflow: hidden; + padding: var(--fa-counter-padding, 0.25em 0.5em); + right: var(--fa-right, 0); + text-overflow: ellipsis; + top: var(--fa-top, 0); + -webkit-transform: scale(var(--fa-counter-scale, 0.25)); + transform: scale(var(--fa-counter-scale, 0.25)); + -webkit-transform-origin: top right; + transform-origin: top right; +} + +.fa-layers-bottom-right { + bottom: var(--fa-bottom, 0); + right: var(--fa-right, 0); + top: auto; + -webkit-transform: scale(var(--fa-layers-scale, 0.25)); + transform: scale(var(--fa-layers-scale, 0.25)); + -webkit-transform-origin: bottom right; + transform-origin: bottom right; +} + +.fa-layers-bottom-left { + bottom: var(--fa-bottom, 0); + left: var(--fa-left, 0); + right: auto; + top: auto; + -webkit-transform: scale(var(--fa-layers-scale, 0.25)); + transform: scale(var(--fa-layers-scale, 0.25)); + -webkit-transform-origin: bottom left; + transform-origin: bottom left; +} + +.fa-layers-top-right { + top: var(--fa-top, 0); + right: var(--fa-right, 0); + -webkit-transform: scale(var(--fa-layers-scale, 0.25)); + transform: scale(var(--fa-layers-scale, 0.25)); + -webkit-transform-origin: top right; + transform-origin: top right; +} + +.fa-layers-top-left { + left: var(--fa-left, 0); + right: auto; + top: var(--fa-top, 0); + -webkit-transform: scale(var(--fa-layers-scale, 0.25)); + transform: scale(var(--fa-layers-scale, 0.25)); + -webkit-transform-origin: top left; + transform-origin: top left; +} + +.fa-1x { + font-size: 1em; +} + +.fa-2x { + font-size: 2em; +} + +.fa-3x { + font-size: 3em; +} + +.fa-4x { + font-size: 4em; +} + +.fa-5x { + font-size: 5em; +} + +.fa-6x { + font-size: 6em; +} + +.fa-7x { + font-size: 7em; +} + +.fa-8x { + font-size: 8em; +} + +.fa-9x { + font-size: 9em; +} + +.fa-10x { + font-size: 10em; +} + +.fa-2xs { + font-size: 0.625em; + line-height: 0.1em; + vertical-align: 0.225em; +} + +.fa-xs { + font-size: 0.75em; + line-height: 0.0833333337em; + vertical-align: 0.125em; +} + +.fa-sm { + font-size: 0.875em; + line-height: 0.0714285718em; + vertical-align: 0.0535714295em; +} + +.fa-lg { + font-size: 1.25em; + line-height: 0.05em; + vertical-align: -0.075em; +} + +.fa-xl { + font-size: 1.5em; + line-height: 0.0416666682em; + vertical-align: -0.125em; +} + +.fa-2xl { + font-size: 2em; + line-height: 0.03125em; + vertical-align: -0.1875em; +} + +.fa-fw { + text-align: center; + width: 1.25em; +} + +.fa-ul { + list-style-type: none; + margin-left: var(--fa-li-margin, 2.5em); + padding-left: 0; +} +.fa-ul > li { + position: relative; +} + +.fa-li { + left: calc(var(--fa-li-width, 2em) * -1); + position: absolute; + text-align: center; + width: var(--fa-li-width, 2em); + line-height: inherit; +} + +.fa-border { + border-color: var(--fa-border-color, #eee); + border-radius: var(--fa-border-radius, 0.1em); + border-style: var(--fa-border-style, solid); + border-width: var(--fa-border-width, 0.08em); + padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); +} + +.fa-pull-left { + float: left; + margin-right: var(--fa-pull-margin, 0.3em); +} + +.fa-pull-right { + float: right; + margin-left: var(--fa-pull-margin, 0.3em); +} + +.fa-beat { + -webkit-animation-name: fa-beat; + animation-name: fa-beat; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); + animation-timing-function: var(--fa-animation-timing, ease-in-out); +} + +.fa-bounce { + -webkit-animation-name: fa-bounce; + animation-name: fa-bounce; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); +} + +.fa-fade { + -webkit-animation-name: fa-fade; + animation-name: fa-fade; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); +} + +.fa-beat-fade { + -webkit-animation-name: fa-beat-fade; + animation-name: fa-beat-fade; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); +} + +.fa-flip { + -webkit-animation-name: fa-flip; + animation-name: fa-flip; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, ease-in-out); + animation-timing-function: var(--fa-animation-timing, ease-in-out); +} + +.fa-shake { + -webkit-animation-name: fa-shake; + animation-name: fa-shake; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, linear); + animation-timing-function: var(--fa-animation-timing, linear); +} + +.fa-spin { + -webkit-animation-name: fa-spin; + animation-name: fa-spin; + -webkit-animation-delay: var(--fa-animation-delay, 0s); + animation-delay: var(--fa-animation-delay, 0s); + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 2s); + animation-duration: var(--fa-animation-duration, 2s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, linear); + animation-timing-function: var(--fa-animation-timing, linear); +} + +.fa-spin-reverse { + --fa-animation-direction: reverse; +} + +.fa-pulse, +.fa-spin-pulse { + -webkit-animation-name: fa-spin; + animation-name: fa-spin; + -webkit-animation-direction: var(--fa-animation-direction, normal); + animation-direction: var(--fa-animation-direction, normal); + -webkit-animation-duration: var(--fa-animation-duration, 1s); + animation-duration: var(--fa-animation-duration, 1s); + -webkit-animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + -webkit-animation-timing-function: var(--fa-animation-timing, steps(8)); + animation-timing-function: var(--fa-animation-timing, steps(8)); +} + +@media (prefers-reduced-motion: reduce) { + .fa-beat, +.fa-bounce, +.fa-fade, +.fa-beat-fade, +.fa-flip, +.fa-pulse, +.fa-shake, +.fa-spin, +.fa-spin-pulse { + -webkit-animation-delay: -1ms; + animation-delay: -1ms; + -webkit-animation-duration: 1ms; + animation-duration: 1ms; + -webkit-animation-iteration-count: 1; + animation-iteration-count: 1; + -webkit-transition-delay: 0s; + transition-delay: 0s; + -webkit-transition-duration: 0s; + transition-duration: 0s; + } +} +@-webkit-keyframes fa-beat { + 0%, 90% { + -webkit-transform: scale(1); + transform: scale(1); + } + 45% { + -webkit-transform: scale(var(--fa-beat-scale, 1.25)); + transform: scale(var(--fa-beat-scale, 1.25)); + } +} +@keyframes fa-beat { + 0%, 90% { + -webkit-transform: scale(1); + transform: scale(1); + } + 45% { + -webkit-transform: scale(var(--fa-beat-scale, 1.25)); + transform: scale(var(--fa-beat-scale, 1.25)); + } +} +@-webkit-keyframes fa-bounce { + 0% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } + 10% { + -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + } + 30% { + -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + } + 50% { + -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + } + 57% { + -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + } + 64% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } + 100% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } +} +@keyframes fa-bounce { + 0% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } + 10% { + -webkit-transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + } + 30% { + -webkit-transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + } + 50% { + -webkit-transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + } + 57% { + -webkit-transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + } + 64% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } + 100% { + -webkit-transform: scale(1, 1) translateY(0); + transform: scale(1, 1) translateY(0); + } +} +@-webkit-keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); + } +} +@keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); + } +} +@-webkit-keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + -webkit-transform: scale(1); + transform: scale(1); + } + 50% { + opacity: 1; + -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); + transform: scale(var(--fa-beat-fade-scale, 1.125)); + } +} +@keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + -webkit-transform: scale(1); + transform: scale(1); + } + 50% { + opacity: 1; + -webkit-transform: scale(var(--fa-beat-fade-scale, 1.125)); + transform: scale(var(--fa-beat-fade-scale, 1.125)); + } +} +@-webkit-keyframes fa-flip { + 50% { + -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + } +} +@keyframes fa-flip { + 50% { + -webkit-transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + } +} +@-webkit-keyframes fa-shake { + 0% { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); + } + 4% { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); + } + 8%, 24% { + -webkit-transform: rotate(-18deg); + transform: rotate(-18deg); + } + 12%, 28% { + -webkit-transform: rotate(18deg); + transform: rotate(18deg); + } + 16% { + -webkit-transform: rotate(-22deg); + transform: rotate(-22deg); + } + 20% { + -webkit-transform: rotate(22deg); + transform: rotate(22deg); + } + 32% { + -webkit-transform: rotate(-12deg); + transform: rotate(-12deg); + } + 36% { + -webkit-transform: rotate(12deg); + transform: rotate(12deg); + } + 40%, 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } +} +@keyframes fa-shake { + 0% { + -webkit-transform: rotate(-15deg); + transform: rotate(-15deg); + } + 4% { + -webkit-transform: rotate(15deg); + transform: rotate(15deg); + } + 8%, 24% { + -webkit-transform: rotate(-18deg); + transform: rotate(-18deg); + } + 12%, 28% { + -webkit-transform: rotate(18deg); + transform: rotate(18deg); + } + 16% { + -webkit-transform: rotate(-22deg); + transform: rotate(-22deg); + } + 20% { + -webkit-transform: rotate(22deg); + transform: rotate(22deg); + } + 32% { + -webkit-transform: rotate(-12deg); + transform: rotate(-12deg); + } + 36% { + -webkit-transform: rotate(12deg); + transform: rotate(12deg); + } + 40%, 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } +} +@-webkit-keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes fa-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +.fa-rotate-90 { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); +} + +.fa-rotate-180 { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); +} + +.fa-rotate-270 { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); +} + +.fa-flip-horizontal { + -webkit-transform: scale(-1, 1); + transform: scale(-1, 1); +} + +.fa-flip-vertical { + -webkit-transform: scale(1, -1); + transform: scale(1, -1); +} + +.fa-flip-both, +.fa-flip-horizontal.fa-flip-vertical { + -webkit-transform: scale(-1, -1); + transform: scale(-1, -1); +} + +.fa-rotate-by { + -webkit-transform: rotate(var(--fa-rotate-angle, none)); + transform: rotate(var(--fa-rotate-angle, none)); +} + +.fa-stack { + display: inline-block; + vertical-align: middle; + height: 2em; + position: relative; + width: 2.5em; +} + +.fa-stack-1x, +.fa-stack-2x { + bottom: 0; + left: 0; + margin: auto; + position: absolute; + right: 0; + top: 0; + z-index: var(--fa-stack-z-index, auto); +} + +.svg-inline--fa.fa-stack-1x { + height: 1em; + width: 1.25em; +} +.svg-inline--fa.fa-stack-2x { + height: 2em; + width: 2.5em; +} + +.fa-inverse { + color: var(--fa-inverse, #fff); +} + +.sr-only, +.fa-sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.sr-only-focusable:not(:focus), +.fa-sr-only-focusable:not(:focus) { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.svg-inline--fa .fa-primary { + fill: var(--fa-primary-color, currentColor); + opacity: var(--fa-primary-opacity, 1); +} + +.svg-inline--fa .fa-secondary { + fill: var(--fa-secondary-color, currentColor); + opacity: var(--fa-secondary-opacity, 0.4); +} + +.svg-inline--fa.fa-swap-opacity .fa-primary { + opacity: var(--fa-secondary-opacity, 0.4); +} + +.svg-inline--fa.fa-swap-opacity .fa-secondary { + opacity: var(--fa-primary-opacity, 1); +} + +.svg-inline--fa mask .fa-primary, +.svg-inline--fa mask .fa-secondary { + fill: black; +} + +.fad.fa-inverse, +.fa-duotone.fa-inverse { + color: var(--fa-inverse, #fff); +}`;function zA(){var e=DA,t=MA,n=ae.cssPrefix,r=ae.replacementClass,i=gX;if(n!==e||r!==t){var o=new RegExp("\\.".concat(e,"\\-"),"g"),a=new RegExp("\\--".concat(e,"\\-"),"g"),s=new RegExp("\\.".concat(t),"g");i=i.replace(o,".".concat(n,"-")).replace(a,"--".concat(n,"-")).replace(s,".".concat(r))}return i}var I4=!1;function Xm(){ae.autoAddCss&&!I4&&(fX(zA()),I4=!0)}var vX={mixout:function(){return{dom:{css:zA,insertCss:Xm}}},hooks:function(){return{beforeDOMElementCreation:function(){Xm()},beforeI2svg:function(){Xm()}}}},Eo=ma||{};Eo[vo]||(Eo[vo]={});Eo[vo].styles||(Eo[vo].styles={});Eo[vo].hooks||(Eo[vo].hooks={});Eo[vo].shims||(Eo[vo].shims=[]);var ti=Eo[vo],UA=[],EX=function e(){rt.removeEventListener("DOMContentLoaded",e),lh=1,UA.map(function(t){return t()})},lh=!1;bo&&(lh=(rt.documentElement.doScroll?/^loaded|^c/:/^loaded|^i|^c/).test(rt.readyState),lh||rt.addEventListener("DOMContentLoaded",EX));function yX(e){bo&&(lh?setTimeout(e,0):UA.push(e))}function Dc(e){var t=e.tag,n=e.attributes,r=n===void 0?{}:n,i=e.children,o=i===void 0?[]:i;return typeof e=="string"?HA(e):"<".concat(t," ").concat(hX(r),">").concat(o.map(Dc).join(""),"")}function x4(e,t,n){if(e&&e[t]&&e[t][n])return{prefix:t,iconName:n,icon:e[t][n]}}var TX=function(t,n){return function(r,i,o,a){return t.call(n,r,i,o,a)}},Zm=function(t,n,r,i){var o=Object.keys(t),a=o.length,s=i!==void 0?TX(n,i):n,l,u,c;for(r===void 0?(l=1,c=t[o[0]]):(l=0,c=r);l=55296&&i<=56319&&n=55296&&r<=56319&&n>t+1&&(i=e.charCodeAt(t+1),i>=56320&&i<=57343)?(r-55296)*1024+i-56320+65536:r}function N4(e){return Object.keys(e).reduce(function(t,n){var r=e[n],i=!!r.icon;return i?t[r.iconName]=r.icon:t[n]=r,t},{})}function Ug(e,t){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{},r=n.skipHooks,i=r===void 0?!1:r,o=N4(t);typeof ti.hooks.addPack=="function"&&!i?ti.hooks.addPack(e,N4(t)):ti.styles[e]=te(te({},ti.styles[e]||{}),o),e==="fas"&&Ug("fa",t)}var Hf,zf,Uf,dl=ti.styles,_X=ti.shims,bX=(Hf={},Ot(Hf,tt,Object.values(Q1[tt])),Ot(Hf,Et,Object.values(Q1[Et])),Hf),lE=null,KA={},WA={},jA={},GA={},$A={},AX=(zf={},Ot(zf,tt,Object.keys(q1[tt])),Ot(zf,Et,Object.keys(q1[Et])),zf);function wX(e){return~aX.indexOf(e)}function kX(e,t){var n=t.split("-"),r=n[0],i=n.slice(1).join("-");return r===e&&i!==""&&!wX(i)?i:null}var VA=function(){var t=function(o){return Zm(dl,function(a,s,l){return a[l]=Zm(s,o,{}),a},{})};KA=t(function(i,o,a){if(o[3]&&(i[o[3]]=a),o[2]){var s=o[2].filter(function(l){return typeof l=="number"});s.forEach(function(l){i[l.toString(16)]=a})}return i}),WA=t(function(i,o,a){if(i[a]=a,o[2]){var s=o[2].filter(function(l){return typeof l=="string"});s.forEach(function(l){i[l]=a})}return i}),$A=t(function(i,o,a){var s=o[2];return i[a]=a,s.forEach(function(l){i[l]=a}),i});var n="far"in dl||ae.autoFetchSvg,r=Zm(_X,function(i,o){var a=o[0],s=o[1],l=o[2];return s==="far"&&!n&&(s="fas"),typeof a=="string"&&(i.names[a]={prefix:s,iconName:l}),typeof a=="number"&&(i.unicodes[a.toString(16)]={prefix:s,iconName:l}),i},{names:{},unicodes:{}});jA=r.names,GA=r.unicodes,lE=up(ae.styleDefault,{family:ae.familyDefault})};cX(function(e){lE=up(e.styleDefault,{family:ae.familyDefault})});VA();function uE(e,t){return(KA[e]||{})[t]}function IX(e,t){return(WA[e]||{})[t]}function es(e,t){return($A[e]||{})[t]}function qA(e){return jA[e]||{prefix:null,iconName:null}}function xX(e){var t=GA[e],n=uE("fas",e);return t||(n?{prefix:"fas",iconName:n}:null)||{prefix:null,iconName:null}}function ga(){return lE}var cE=function(){return{prefix:null,iconName:null,rest:[]}};function up(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},n=t.family,r=n===void 0?tt:n,i=q1[r][e],o=Y1[r][e]||Y1[r][i],a=e in ti.styles?e:null;return o||a||null}var F4=(Uf={},Ot(Uf,tt,Object.keys(Q1[tt])),Ot(Uf,Et,Object.keys(Q1[Et])),Uf);function cp(e){var t,n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=n.skipLookups,i=r===void 0?!1:r,o=(t={},Ot(t,tt,"".concat(ae.cssPrefix,"-").concat(tt)),Ot(t,Et,"".concat(ae.cssPrefix,"-").concat(Et)),t),a=null,s=tt;(e.includes(o[tt])||e.some(function(u){return F4[tt].includes(u)}))&&(s=tt),(e.includes(o[Et])||e.some(function(u){return F4[Et].includes(u)}))&&(s=Et);var l=e.reduce(function(u,c){var f=kX(ae.cssPrefix,c);if(dl[c]?(c=bX[s].includes(c)?eX[s][c]:c,a=c,u.prefix=c):AX[s].indexOf(c)>-1?(a=c,u.prefix=up(c,{family:s})):f?u.iconName=f:c!==ae.replacementClass&&c!==o[tt]&&c!==o[Et]&&u.rest.push(c),!i&&u.prefix&&u.iconName){var d=a==="fa"?qA(u.iconName):{},h=es(u.prefix,u.iconName);d.prefix&&(a=null),u.iconName=d.iconName||h||u.iconName,u.prefix=d.prefix||u.prefix,u.prefix==="far"&&!dl.far&&dl.fas&&!ae.autoFetchSvg&&(u.prefix="fas")}return u},cE());return(e.includes("fa-brands")||e.includes("fab"))&&(l.prefix="fab"),(e.includes("fa-duotone")||e.includes("fad"))&&(l.prefix="fad"),!l.prefix&&s===Et&&(dl.fass||ae.autoFetchSvg)&&(l.prefix="fass",l.iconName=es(l.prefix,l.iconName)||l.iconName),(l.prefix==="fa"||a==="fa")&&(l.prefix=ga()||"fas"),l}var NX=function(){function e(){KQ(this,e),this.definitions={}}return WQ(e,[{key:"add",value:function(){for(var n=this,r=arguments.length,i=new Array(r),o=0;o0&&c.forEach(function(f){typeof f=="string"&&(n[s][f]=u)}),n[s][l]=u}),n}}]),e}(),R4=[],hl={},Al={},FX=Object.keys(Al);function RX(e,t){var n=t.mixoutsTo;return R4=e,hl={},Object.keys(Al).forEach(function(r){FX.indexOf(r)===-1&&delete Al[r]}),R4.forEach(function(r){var i=r.mixout?r.mixout():{};if(Object.keys(i).forEach(function(a){typeof i[a]=="function"&&(n[a]=i[a]),sh(i[a])==="object"&&Object.keys(i[a]).forEach(function(s){n[a]||(n[a]={}),n[a][s]=i[a][s]})}),r.hooks){var o=r.hooks();Object.keys(o).forEach(function(a){hl[a]||(hl[a]=[]),hl[a].push(o[a])})}r.provides&&r.provides(Al)}),n}function Kg(e,t){for(var n=arguments.length,r=new Array(n>2?n-2:0),i=2;i1?t-1:0),r=1;r0&&arguments[0]!==void 0?arguments[0]:{};return bo?(vs("beforeI2svg",t),yo("pseudoElements2svg",t),yo("i2svg",t)):Promise.reject("Operation requires a DOM of some kind.")},watch:function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},n=t.autoReplaceSvgRoot;ae.autoReplaceSvg===!1&&(ae.autoReplaceSvg=!0),ae.observeMutations=!0,yX(function(){OX({autoReplaceSvgRoot:n}),vs("watch",t)})}},PX={icon:function(t){if(t===null)return null;if(sh(t)==="object"&&t.prefix&&t.iconName)return{prefix:t.prefix,iconName:es(t.prefix,t.iconName)||t.iconName};if(Array.isArray(t)&&t.length===2){var n=t[1].indexOf("fa-")===0?t[1].slice(3):t[1],r=up(t[0]);return{prefix:r,iconName:es(r,n)||n}}if(typeof t=="string"&&(t.indexOf("".concat(ae.cssPrefix,"-"))>-1||t.match(tX))){var i=cp(t.split(" "),{skipLookups:!0});return{prefix:i.prefix||ga(),iconName:es(i.prefix,i.iconName)||i.iconName}}if(typeof t=="string"){var o=ga();return{prefix:o,iconName:es(o,t)||t}}}},Tr={noAuto:DX,config:ae,dom:MX,parse:PX,library:YA,findIconDefinition:Wg,toHtml:Dc},OX=function(){var t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},n=t.autoReplaceSvgRoot,r=n===void 0?rt:n;(Object.keys(ti.styles).length>0||ae.autoFetchSvg)&&bo&&ae.autoReplaceSvg&&Tr.dom.i2svg({node:r})};function fp(e,t){return Object.defineProperty(e,"abstract",{get:t}),Object.defineProperty(e,"html",{get:function(){return e.abstract.map(function(r){return Dc(r)})}}),Object.defineProperty(e,"node",{get:function(){if(bo){var r=rt.createElement("div");return r.innerHTML=e.html,r.children}}}),e}function LX(e){var t=e.children,n=e.main,r=e.mask,i=e.attributes,o=e.styles,a=e.transform;if(sE(a)&&n.found&&!r.found){var s=n.width,l=n.height,u={x:s/l/2,y:.5};i.style=lp(te(te({},o),{},{"transform-origin":"".concat(u.x+a.x/16,"em ").concat(u.y+a.y/16,"em")}))}return[{tag:"svg",attributes:i,children:t}]}function BX(e){var t=e.prefix,n=e.iconName,r=e.children,i=e.attributes,o=e.symbol,a=o===!0?"".concat(t,"-").concat(ae.cssPrefix,"-").concat(n):o;return[{tag:"svg",attributes:{style:"display: none;"},children:[{tag:"symbol",attributes:te(te({},i),{},{id:a}),children:r}]}]}function fE(e){var t=e.icons,n=t.main,r=t.mask,i=e.prefix,o=e.iconName,a=e.transform,s=e.symbol,l=e.title,u=e.maskId,c=e.titleId,f=e.extra,d=e.watchable,h=d===void 0?!1:d,v=r.found?r:n,g=v.width,C=v.height,m=i==="fak",y=[ae.replacementClass,o?"".concat(ae.cssPrefix,"-").concat(o):""].filter(function(D){return f.classes.indexOf(D)===-1}).filter(function(D){return D!==""||!!D}).concat(f.classes).join(" "),T={children:[],attributes:te(te({},f.attributes),{},{"data-prefix":i,"data-icon":o,class:y,role:f.attributes.role||"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 ".concat(g," ").concat(C)})},S=m&&!~f.classes.indexOf("fa-fw")?{width:"".concat(g/C*16*.0625,"em")}:{};h&&(T.attributes[gs]=""),l&&(T.children.push({tag:"title",attributes:{id:T.attributes["aria-labelledby"]||"title-".concat(c||Z1())},children:[l]}),delete T.attributes.title);var w=te(te({},T),{},{prefix:i,iconName:o,main:n,mask:r,maskId:u,transform:a,symbol:s,styles:te(te({},S),f.styles)}),b=r.found&&n.found?yo("generateAbstractMask",w)||{children:[],attributes:{}}:yo("generateAbstractIcon",w)||{children:[],attributes:{}},A=b.children,F=b.attributes;return w.children=A,w.attributes=F,s?BX(w):LX(w)}function D4(e){var t=e.content,n=e.width,r=e.height,i=e.transform,o=e.title,a=e.extra,s=e.watchable,l=s===void 0?!1:s,u=te(te(te({},a.attributes),o?{title:o}:{}),{},{class:a.classes.join(" ")});l&&(u[gs]="");var c=te({},a.styles);sE(i)&&(c.transform=mX({transform:i,startCentered:!0,width:n,height:r}),c["-webkit-transform"]=c.transform);var f=lp(c);f.length>0&&(u.style=f);var d=[];return d.push({tag:"span",attributes:u,children:[t]}),o&&d.push({tag:"span",attributes:{class:"sr-only"},children:[o]}),d}function HX(e){var t=e.content,n=e.title,r=e.extra,i=te(te(te({},r.attributes),n?{title:n}:{}),{},{class:r.classes.join(" ")}),o=lp(r.styles);o.length>0&&(i.style=o);var a=[];return a.push({tag:"span",attributes:i,children:[t]}),n&&a.push({tag:"span",attributes:{class:"sr-only"},children:[n]}),a}var Jm=ti.styles;function jg(e){var t=e[0],n=e[1],r=e.slice(4),i=tE(r,1),o=i[0],a=null;return Array.isArray(o)?a={tag:"g",attributes:{class:"".concat(ae.cssPrefix,"-").concat(Ja.GROUP)},children:[{tag:"path",attributes:{class:"".concat(ae.cssPrefix,"-").concat(Ja.SECONDARY),fill:"currentColor",d:o[0]}},{tag:"path",attributes:{class:"".concat(ae.cssPrefix,"-").concat(Ja.PRIMARY),fill:"currentColor",d:o[1]}}]}:a={tag:"path",attributes:{fill:"currentColor",d:o}},{found:!0,width:t,height:n,icon:a}}var zX={found:!1,width:512,height:512};function UX(e,t){!PA&&!ae.showMissingIcons&&e&&console.error('Icon with name "'.concat(e,'" and prefix "').concat(t,'" is missing.'))}function Gg(e,t){var n=t;return t==="fa"&&ae.styleDefault!==null&&(t=ga()),new Promise(function(r,i){if(yo("missingIconAbstract"),n==="fa"){var o=qA(e)||{};e=o.iconName||e,t=o.prefix||t}if(e&&t&&Jm[t]&&Jm[t][e]){var a=Jm[t][e];return r(jg(a))}UX(e,t),r(te(te({},zX),{},{icon:ae.showMissingIcons&&e?yo("missingIconAbstract")||{}:{}}))})}var M4=function(){},$g=ae.measurePerformance&&Df&&Df.mark&&Df.measure?Df:{mark:M4,measure:M4},Hu='FA "6.5.1"',KX=function(t){return $g.mark("".concat(Hu," ").concat(t," begins")),function(){return QA(t)}},QA=function(t){$g.mark("".concat(Hu," ").concat(t," ends")),$g.measure("".concat(Hu," ").concat(t),"".concat(Hu," ").concat(t," begins"),"".concat(Hu," ").concat(t," ends"))},dE={begin:KX,end:QA},hd=function(){};function P4(e){var t=e.getAttribute?e.getAttribute(gs):null;return typeof t=="string"}function WX(e){var t=e.getAttribute?e.getAttribute(rE):null,n=e.getAttribute?e.getAttribute(iE):null;return t&&n}function jX(e){return e&&e.classList&&e.classList.contains&&e.classList.contains(ae.replacementClass)}function GX(){if(ae.autoReplaceSvg===!0)return pd.replace;var e=pd[ae.autoReplaceSvg];return e||pd.replace}function $X(e){return rt.createElementNS("http://www.w3.org/2000/svg",e)}function VX(e){return rt.createElement(e)}function XA(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},n=t.ceFn,r=n===void 0?e.tag==="svg"?$X:VX:n;if(typeof e=="string")return rt.createTextNode(e);var i=r(e.tag);Object.keys(e.attributes||[]).forEach(function(a){i.setAttribute(a,e.attributes[a])});var o=e.children||[];return o.forEach(function(a){i.appendChild(XA(a,{ceFn:r}))}),i}function qX(e){var t=" ".concat(e.outerHTML," ");return t="".concat(t,"Font Awesome fontawesome.com "),t}var pd={replace:function(t){var n=t[0];if(n.parentNode)if(t[1].forEach(function(i){n.parentNode.insertBefore(XA(i),n)}),n.getAttribute(gs)===null&&ae.keepOriginalSource){var r=rt.createComment(qX(n));n.parentNode.replaceChild(r,n)}else n.remove()},nest:function(t){var n=t[0],r=t[1];if(~aE(n).indexOf(ae.replacementClass))return pd.replace(t);var i=new RegExp("".concat(ae.cssPrefix,"-.*"));if(delete r[0].attributes.id,r[0].attributes.class){var o=r[0].attributes.class.split(" ").reduce(function(s,l){return l===ae.replacementClass||l.match(i)?s.toSvg.push(l):s.toNode.push(l),s},{toNode:[],toSvg:[]});r[0].attributes.class=o.toSvg.join(" "),o.toNode.length===0?n.removeAttribute("class"):n.setAttribute("class",o.toNode.join(" "))}var a=r.map(function(s){return Dc(s)}).join(` +`);n.setAttribute(gs,""),n.innerHTML=a}};function O4(e){e()}function ZA(e,t){var n=typeof t=="function"?t:hd;if(e.length===0)n();else{var r=O4;ae.mutateApproach===ZQ&&(r=ma.requestAnimationFrame||O4),r(function(){var i=GX(),o=dE.begin("mutate");e.map(i),o(),n()})}}var hE=!1;function JA(){hE=!0}function Vg(){hE=!1}var uh=null;function L4(e){if(w4&&ae.observeMutations){var t=e.treeCallback,n=t===void 0?hd:t,r=e.nodeCallback,i=r===void 0?hd:r,o=e.pseudoElementsCallback,a=o===void 0?hd:o,s=e.observeMutationsRoot,l=s===void 0?rt:s;uh=new w4(function(u){if(!hE){var c=ga();iu(u).forEach(function(f){if(f.type==="childList"&&f.addedNodes.length>0&&!P4(f.addedNodes[0])&&(ae.searchPseudoElements&&a(f.target),n(f.target)),f.type==="attributes"&&f.target.parentNode&&ae.searchPseudoElements&&a(f.target.parentNode),f.type==="attributes"&&P4(f.target)&&~oX.indexOf(f.attributeName))if(f.attributeName==="class"&&WX(f.target)){var d=cp(aE(f.target)),h=d.prefix,v=d.iconName;f.target.setAttribute(rE,h||c),v&&f.target.setAttribute(iE,v)}else jX(f.target)&&i(f.target)})}}),bo&&uh.observe(l,{childList:!0,attributes:!0,characterData:!0,subtree:!0})}}function YX(){uh&&uh.disconnect()}function QX(e){var t=e.getAttribute("style"),n=[];return t&&(n=t.split(";").reduce(function(r,i){var o=i.split(":"),a=o[0],s=o.slice(1);return a&&s.length>0&&(r[a]=s.join(":").trim()),r},{})),n}function XX(e){var t=e.getAttribute("data-prefix"),n=e.getAttribute("data-icon"),r=e.innerText!==void 0?e.innerText.trim():"",i=cp(aE(e));return i.prefix||(i.prefix=ga()),t&&n&&(i.prefix=t,i.iconName=n),i.iconName&&i.prefix||(i.prefix&&r.length>0&&(i.iconName=IX(i.prefix,e.innerText)||uE(i.prefix,zg(e.innerText))),!i.iconName&&ae.autoFetchSvg&&e.firstChild&&e.firstChild.nodeType===Node.TEXT_NODE&&(i.iconName=e.firstChild.data)),i}function ZX(e){var t=iu(e.attributes).reduce(function(i,o){return i.name!=="class"&&i.name!=="style"&&(i[o.name]=o.value),i},{}),n=e.getAttribute("title"),r=e.getAttribute("data-fa-title-id");return ae.autoA11y&&(n?t["aria-labelledby"]="".concat(ae.replacementClass,"-title-").concat(r||Z1()):(t["aria-hidden"]="true",t.focusable="false")),t}function JX(){return{iconName:null,title:null,titleId:null,prefix:null,transform:Mi,symbol:!1,mask:{iconName:null,prefix:null,rest:[]},maskId:null,extra:{classes:[],styles:{},attributes:{}}}}function B4(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{styleParser:!0},n=XX(e),r=n.iconName,i=n.prefix,o=n.rest,a=ZX(e),s=Kg("parseNodeAttributes",{},e),l=t.styleParser?QX(e):[];return te({iconName:r,title:e.getAttribute("title"),titleId:e.getAttribute("data-fa-title-id"),prefix:i,transform:Mi,mask:{iconName:null,prefix:null,rest:[]},maskId:null,symbol:!1,extra:{classes:o,styles:l,attributes:a}},s)}var eZ=ti.styles;function e3(e){var t=ae.autoReplaceSvg==="nest"?B4(e,{styleParser:!1}):B4(e);return~t.extra.classes.indexOf(OA)?yo("generateLayersText",e,t):yo("generateSvgReplacementMutation",e,t)}var va=new Set;oE.map(function(e){va.add("fa-".concat(e))});Object.keys(q1[tt]).map(va.add.bind(va));Object.keys(q1[Et]).map(va.add.bind(va));va=Fc(va);function H4(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:null;if(!bo)return Promise.resolve();var n=rt.documentElement.classList,r=function(f){return n.add("".concat(k4,"-").concat(f))},i=function(f){return n.remove("".concat(k4,"-").concat(f))},o=ae.autoFetchSvg?va:oE.map(function(c){return"fa-".concat(c)}).concat(Object.keys(eZ));o.includes("fa")||o.push("fa");var a=[".".concat(OA,":not([").concat(gs,"])")].concat(o.map(function(c){return".".concat(c,":not([").concat(gs,"])")})).join(", ");if(a.length===0)return Promise.resolve();var s=[];try{s=iu(e.querySelectorAll(a))}catch{}if(s.length>0)r("pending"),i("complete");else return Promise.resolve();var l=dE.begin("onTree"),u=s.reduce(function(c,f){try{var d=e3(f);d&&c.push(d)}catch(h){PA||h.name==="MissingIcon"&&console.error(h)}return c},[]);return new Promise(function(c,f){Promise.all(u).then(function(d){ZA(d,function(){r("active"),r("complete"),i("pending"),typeof t=="function"&&t(),l(),c()})}).catch(function(d){l(),f(d)})})}function tZ(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:null;e3(e).then(function(n){n&&ZA([n],t)})}function nZ(e){return function(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=(t||{}).icon?t:Wg(t||{}),i=n.mask;return i&&(i=(i||{}).icon?i:Wg(i||{})),e(r,te(te({},n),{},{mask:i}))}}var rZ=function(t){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},r=n.transform,i=r===void 0?Mi:r,o=n.symbol,a=o===void 0?!1:o,s=n.mask,l=s===void 0?null:s,u=n.maskId,c=u===void 0?null:u,f=n.title,d=f===void 0?null:f,h=n.titleId,v=h===void 0?null:h,g=n.classes,C=g===void 0?[]:g,m=n.attributes,y=m===void 0?{}:m,T=n.styles,S=T===void 0?{}:T;if(t){var w=t.prefix,b=t.iconName,A=t.icon;return fp(te({type:"icon"},t),function(){return vs("beforeDOMElementCreation",{iconDefinition:t,params:n}),ae.autoA11y&&(d?y["aria-labelledby"]="".concat(ae.replacementClass,"-title-").concat(v||Z1()):(y["aria-hidden"]="true",y.focusable="false")),fE({icons:{main:jg(A),mask:l?jg(l.icon):{found:!1,width:null,height:null,icon:{}}},prefix:w,iconName:b,transform:te(te({},Mi),i),symbol:a,title:d,maskId:c,titleId:v,extra:{attributes:y,styles:S,classes:C}})})}},iZ={mixout:function(){return{icon:nZ(rZ)}},hooks:function(){return{mutationObserverCallbacks:function(n){return n.treeCallback=H4,n.nodeCallback=tZ,n}}},provides:function(t){t.i2svg=function(n){var r=n.node,i=r===void 0?rt:r,o=n.callback,a=o===void 0?function(){}:o;return H4(i,a)},t.generateSvgReplacementMutation=function(n,r){var i=r.iconName,o=r.title,a=r.titleId,s=r.prefix,l=r.transform,u=r.symbol,c=r.mask,f=r.maskId,d=r.extra;return new Promise(function(h,v){Promise.all([Gg(i,s),c.iconName?Gg(c.iconName,c.prefix):Promise.resolve({found:!1,width:512,height:512,icon:{}})]).then(function(g){var C=tE(g,2),m=C[0],y=C[1];h([n,fE({icons:{main:m,mask:y},prefix:s,iconName:i,transform:l,symbol:u,maskId:f,title:o,titleId:a,extra:d,watchable:!0})])}).catch(v)})},t.generateAbstractIcon=function(n){var r=n.children,i=n.attributes,o=n.main,a=n.transform,s=n.styles,l=lp(s);l.length>0&&(i.style=l);var u;return sE(a)&&(u=yo("generateAbstractTransformGrouping",{main:o,transform:a,containerWidth:o.width,iconWidth:o.width})),r.push(u||o.icon),{children:r,attributes:i}}}},oZ={mixout:function(){return{layer:function(n){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=r.classes,o=i===void 0?[]:i;return fp({type:"layer"},function(){vs("beforeDOMElementCreation",{assembler:n,params:r});var a=[];return n(function(s){Array.isArray(s)?s.map(function(l){a=a.concat(l.abstract)}):a=a.concat(s.abstract)}),[{tag:"span",attributes:{class:["".concat(ae.cssPrefix,"-layers")].concat(Fc(o)).join(" ")},children:a}]})}}}},aZ={mixout:function(){return{counter:function(n){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=r.title,o=i===void 0?null:i,a=r.classes,s=a===void 0?[]:a,l=r.attributes,u=l===void 0?{}:l,c=r.styles,f=c===void 0?{}:c;return fp({type:"counter",content:n},function(){return vs("beforeDOMElementCreation",{content:n,params:r}),HX({content:n.toString(),title:o,extra:{attributes:u,styles:f,classes:["".concat(ae.cssPrefix,"-layers-counter")].concat(Fc(s))}})})}}}},sZ={mixout:function(){return{text:function(n){var r=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},i=r.transform,o=i===void 0?Mi:i,a=r.title,s=a===void 0?null:a,l=r.classes,u=l===void 0?[]:l,c=r.attributes,f=c===void 0?{}:c,d=r.styles,h=d===void 0?{}:d;return fp({type:"text",content:n},function(){return vs("beforeDOMElementCreation",{content:n,params:r}),D4({content:n,transform:te(te({},Mi),o),title:s,extra:{attributes:f,styles:h,classes:["".concat(ae.cssPrefix,"-layers-text")].concat(Fc(u))}})})}}},provides:function(t){t.generateLayersText=function(n,r){var i=r.title,o=r.transform,a=r.extra,s=null,l=null;if(RA){var u=parseInt(getComputedStyle(n).fontSize,10),c=n.getBoundingClientRect();s=c.width/u,l=c.height/u}return ae.autoA11y&&!i&&(a.attributes["aria-hidden"]="true"),Promise.resolve([n,D4({content:n.innerHTML,width:s,height:l,transform:o,title:i,extra:a,watchable:!0})])}}},lZ=new RegExp('"',"ug"),z4=[1105920,1112319];function uZ(e){var t=e.replace(lZ,""),n=SX(t,0),r=n>=z4[0]&&n<=z4[1],i=t.length===2?t[0]===t[1]:!1;return{value:zg(i?t[0]:t),isSecondary:r||i}}function U4(e,t){var n="".concat(XQ).concat(t.replace(":","-"));return new Promise(function(r,i){if(e.getAttribute(n)!==null)return r();var o=iu(e.children),a=o.filter(function(A){return A.getAttribute(Hg)===t})[0],s=ma.getComputedStyle(e,t),l=s.getPropertyValue("font-family").match(nX),u=s.getPropertyValue("font-weight"),c=s.getPropertyValue("content");if(a&&!l)return e.removeChild(a),r();if(l&&c!=="none"&&c!==""){var f=s.getPropertyValue("content"),d=~["Sharp"].indexOf(l[2])?Et:tt,h=~["Solid","Regular","Light","Thin","Duotone","Brands","Kit"].indexOf(l[2])?Y1[d][l[2].toLowerCase()]:rX[d][u],v=uZ(f),g=v.value,C=v.isSecondary,m=l[0].startsWith("FontAwesome"),y=uE(h,g),T=y;if(m){var S=xX(g);S.iconName&&S.prefix&&(y=S.iconName,h=S.prefix)}if(y&&!C&&(!a||a.getAttribute(rE)!==h||a.getAttribute(iE)!==T)){e.setAttribute(n,T),a&&e.removeChild(a);var w=JX(),b=w.extra;b.attributes[Hg]=t,Gg(y,h).then(function(A){var F=fE(te(te({},w),{},{icons:{main:A,mask:cE()},prefix:h,iconName:T,extra:b,watchable:!0})),D=rt.createElementNS("http://www.w3.org/2000/svg","svg");t==="::before"?e.insertBefore(D,e.firstChild):e.appendChild(D),D.outerHTML=F.map(function(I){return Dc(I)}).join(` +`),e.removeAttribute(n),r()}).catch(i)}else r()}else r()})}function cZ(e){return Promise.all([U4(e,"::before"),U4(e,"::after")])}function fZ(e){return e.parentNode!==document.head&&!~JQ.indexOf(e.tagName.toUpperCase())&&!e.getAttribute(Hg)&&(!e.parentNode||e.parentNode.tagName!=="svg")}function K4(e){if(bo)return new Promise(function(t,n){var r=iu(e.querySelectorAll("*")).filter(fZ).map(cZ),i=dE.begin("searchPseudoElements");JA(),Promise.all(r).then(function(){i(),Vg(),t()}).catch(function(){i(),Vg(),n()})})}var dZ={hooks:function(){return{mutationObserverCallbacks:function(n){return n.pseudoElementsCallback=K4,n}}},provides:function(t){t.pseudoElements2svg=function(n){var r=n.node,i=r===void 0?rt:r;ae.searchPseudoElements&&K4(i)}}},W4=!1,hZ={mixout:function(){return{dom:{unwatch:function(){JA(),W4=!0}}}},hooks:function(){return{bootstrap:function(){L4(Kg("mutationObserverCallbacks",{}))},noAuto:function(){YX()},watch:function(n){var r=n.observeMutationsRoot;W4?Vg():L4(Kg("mutationObserverCallbacks",{observeMutationsRoot:r}))}}}},j4=function(t){var n={size:16,x:0,y:0,flipX:!1,flipY:!1,rotate:0};return t.toLowerCase().split(" ").reduce(function(r,i){var o=i.toLowerCase().split("-"),a=o[0],s=o.slice(1).join("-");if(a&&s==="h")return r.flipX=!0,r;if(a&&s==="v")return r.flipY=!0,r;if(s=parseFloat(s),isNaN(s))return r;switch(a){case"grow":r.size=r.size+s;break;case"shrink":r.size=r.size-s;break;case"left":r.x=r.x-s;break;case"right":r.x=r.x+s;break;case"up":r.y=r.y-s;break;case"down":r.y=r.y+s;break;case"rotate":r.rotate=r.rotate+s;break}return r},n)},pZ={mixout:function(){return{parse:{transform:function(n){return j4(n)}}}},hooks:function(){return{parseNodeAttributes:function(n,r){var i=r.getAttribute("data-fa-transform");return i&&(n.transform=j4(i)),n}}},provides:function(t){t.generateAbstractTransformGrouping=function(n){var r=n.main,i=n.transform,o=n.containerWidth,a=n.iconWidth,s={transform:"translate(".concat(o/2," 256)")},l="translate(".concat(i.x*32,", ").concat(i.y*32,") "),u="scale(".concat(i.size/16*(i.flipX?-1:1),", ").concat(i.size/16*(i.flipY?-1:1),") "),c="rotate(".concat(i.rotate," 0 0)"),f={transform:"".concat(l," ").concat(u," ").concat(c)},d={transform:"translate(".concat(a/2*-1," -256)")},h={outer:s,inner:f,path:d};return{tag:"g",attributes:te({},h.outer),children:[{tag:"g",attributes:te({},h.inner),children:[{tag:r.icon.tag,children:r.icon.children,attributes:te(te({},r.icon.attributes),h.path)}]}]}}}},e0={x:0,y:0,width:"100%",height:"100%"};function G4(e){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:!0;return e.attributes&&(e.attributes.fill||t)&&(e.attributes.fill="black"),e}function mZ(e){return e.tag==="g"?e.children:[e]}var gZ={hooks:function(){return{parseNodeAttributes:function(n,r){var i=r.getAttribute("data-fa-mask"),o=i?cp(i.split(" ").map(function(a){return a.trim()})):cE();return o.prefix||(o.prefix=ga()),n.mask=o,n.maskId=r.getAttribute("data-fa-mask-id"),n}}},provides:function(t){t.generateAbstractMask=function(n){var r=n.children,i=n.attributes,o=n.main,a=n.mask,s=n.maskId,l=n.transform,u=o.width,c=o.icon,f=a.width,d=a.icon,h=pX({transform:l,containerWidth:f,iconWidth:u}),v={tag:"rect",attributes:te(te({},e0),{},{fill:"white"})},g=c.children?{children:c.children.map(G4)}:{},C={tag:"g",attributes:te({},h.inner),children:[G4(te({tag:c.tag,attributes:te(te({},c.attributes),h.path)},g))]},m={tag:"g",attributes:te({},h.outer),children:[C]},y="mask-".concat(s||Z1()),T="clip-".concat(s||Z1()),S={tag:"mask",attributes:te(te({},e0),{},{id:y,maskUnits:"userSpaceOnUse",maskContentUnits:"userSpaceOnUse"}),children:[v,m]},w={tag:"defs",children:[{tag:"clipPath",attributes:{id:T},children:mZ(d)},S]};return r.push(w,{tag:"rect",attributes:te({fill:"currentColor","clip-path":"url(#".concat(T,")"),mask:"url(#".concat(y,")")},e0)}),{children:r,attributes:i}}}},vZ={provides:function(t){var n=!1;ma.matchMedia&&(n=ma.matchMedia("(prefers-reduced-motion: reduce)").matches),t.missingIconAbstract=function(){var r=[],i={fill:"currentColor"},o={attributeType:"XML",repeatCount:"indefinite",dur:"2s"};r.push({tag:"path",attributes:te(te({},i),{},{d:"M156.5,447.7l-12.6,29.5c-18.7-9.5-35.9-21.2-51.5-34.9l22.7-22.7C127.6,430.5,141.5,440,156.5,447.7z M40.6,272H8.5 c1.4,21.2,5.4,41.7,11.7,61.1L50,321.2C45.1,305.5,41.8,289,40.6,272z M40.6,240c1.4-18.8,5.2-37,11.1-54.1l-29.5-12.6 C14.7,194.3,10,216.7,8.5,240H40.6z M64.3,156.5c7.8-14.9,17.2-28.8,28.1-41.5L69.7,92.3c-13.7,15.6-25.5,32.8-34.9,51.5 L64.3,156.5z M397,419.6c-13.9,12-29.4,22.3-46.1,30.4l11.9,29.8c20.7-9.9,39.8-22.6,56.9-37.6L397,419.6z M115,92.4 c13.9-12,29.4-22.3,46.1-30.4l-11.9-29.8c-20.7,9.9-39.8,22.6-56.8,37.6L115,92.4z M447.7,355.5c-7.8,14.9-17.2,28.8-28.1,41.5 l22.7,22.7c13.7-15.6,25.5-32.9,34.9-51.5L447.7,355.5z M471.4,272c-1.4,18.8-5.2,37-11.1,54.1l29.5,12.6 c7.5-21.1,12.2-43.5,13.6-66.8H471.4z M321.2,462c-15.7,5-32.2,8.2-49.2,9.4v32.1c21.2-1.4,41.7-5.4,61.1-11.7L321.2,462z M240,471.4c-18.8-1.4-37-5.2-54.1-11.1l-12.6,29.5c21.1,7.5,43.5,12.2,66.8,13.6V471.4z M462,190.8c5,15.7,8.2,32.2,9.4,49.2h32.1 c-1.4-21.2-5.4-41.7-11.7-61.1L462,190.8z M92.4,397c-12-13.9-22.3-29.4-30.4-46.1l-29.8,11.9c9.9,20.7,22.6,39.8,37.6,56.9 L92.4,397z M272,40.6c18.8,1.4,36.9,5.2,54.1,11.1l12.6-29.5C317.7,14.7,295.3,10,272,8.5V40.6z M190.8,50 c15.7-5,32.2-8.2,49.2-9.4V8.5c-21.2,1.4-41.7,5.4-61.1,11.7L190.8,50z M442.3,92.3L419.6,115c12,13.9,22.3,29.4,30.5,46.1 l29.8-11.9C470,128.5,457.3,109.4,442.3,92.3z M397,92.4l22.7-22.7c-15.6-13.7-32.8-25.5-51.5-34.9l-12.6,29.5 C370.4,72.1,384.4,81.5,397,92.4z"})});var a=te(te({},o),{},{attributeName:"opacity"}),s={tag:"circle",attributes:te(te({},i),{},{cx:"256",cy:"364",r:"28"}),children:[]};return n||s.children.push({tag:"animate",attributes:te(te({},o),{},{attributeName:"r",values:"28;14;28;28;14;28;"})},{tag:"animate",attributes:te(te({},a),{},{values:"1;0;1;1;0;1;"})}),r.push(s),r.push({tag:"path",attributes:te(te({},i),{},{opacity:"1",d:"M263.7,312h-16c-6.6,0-12-5.4-12-12c0-71,77.4-63.9,77.4-107.8c0-20-17.8-40.2-57.4-40.2c-29.1,0-44.3,9.6-59.2,28.7 c-3.9,5-11.1,6-16.2,2.4l-13.1-9.2c-5.6-3.9-6.9-11.8-2.6-17.2c21.2-27.2,46.4-44.7,91.2-44.7c52.3,0,97.4,29.8,97.4,80.2 c0,67.6-77.4,63.5-77.4,107.8C275.7,306.6,270.3,312,263.7,312z"}),children:n?[]:[{tag:"animate",attributes:te(te({},a),{},{values:"1;0;0;0;0;1;"})}]}),n||r.push({tag:"path",attributes:te(te({},i),{},{opacity:"0",d:"M232.5,134.5l7,168c0.3,6.4,5.6,11.5,12,11.5h9c6.4,0,11.7-5.1,12-11.5l7-168c0.3-6.8-5.2-12.5-12-12.5h-23 C237.7,122,232.2,127.7,232.5,134.5z"}),children:[{tag:"animate",attributes:te(te({},a),{},{values:"0;0;1;1;0;0;"})}]}),{tag:"g",attributes:{class:"missing"},children:r}}}},EZ={hooks:function(){return{parseNodeAttributes:function(n,r){var i=r.getAttribute("data-fa-symbol"),o=i===null?!1:i===""?!0:i;return n.symbol=o,n}}}},yZ=[vX,iZ,oZ,aZ,sZ,dZ,hZ,pZ,gZ,vZ,EZ];RX(yZ,{mixoutsTo:Tr});Tr.noAuto;Tr.config;Tr.library;Tr.dom;var qg=Tr.parse;Tr.findIconDefinition;Tr.toHtml;var TZ=Tr.icon;Tr.layer;Tr.text;Tr.counter;function $4(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter(function(i){return Object.getOwnPropertyDescriptor(e,i).enumerable})),n.push.apply(n,r)}return n}function ta(e){for(var t=1;t=0)&&(n[i]=e[i]);return n}function SZ(e,t){if(e==null)return{};var n=CZ(e,t),r,i;if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(i=0;i=0)&&Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}function Yg(e){return _Z(e)||bZ(e)||AZ(e)||wZ()}function _Z(e){if(Array.isArray(e))return Qg(e)}function bZ(e){if(typeof Symbol<"u"&&e[Symbol.iterator]!=null||e["@@iterator"]!=null)return Array.from(e)}function AZ(e,t){if(e){if(typeof e=="string")return Qg(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);if(n==="Object"&&e.constructor&&(n=e.constructor.name),n==="Map"||n==="Set")return Array.from(e);if(n==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return Qg(e,t)}}function Qg(e,t){(t==null||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&arguments[2]!==void 0?arguments[2]:{};if(typeof t=="string")return t;var r=(t.children||[]).map(function(l){return n3(e,l)}),i=Object.keys(t.attributes||{}).reduce(function(l,u){var c=t.attributes[u];switch(u){case"class":l.attrs.className=c,delete t.attributes.class;break;case"style":l.attrs.style=FZ(c);break;default:u.indexOf("aria-")===0||u.indexOf("data-")===0?l.attrs[u.toLowerCase()]=c:l.attrs[t3(u)]=c}return l},{attrs:{}}),o=n.style,a=o===void 0?{}:o,s=SZ(n,xZ);return i.attrs.style=ta(ta({},i.attrs.style),a),e.apply(void 0,[t.tag,ta(ta({},i.attrs),s)].concat(Yg(r)))}var r3=!1;try{r3=!0}catch{}function RZ(){if(!r3&&console&&typeof console.error=="function"){var e;(e=console).error.apply(e,arguments)}}function V4(e){if(e&&ch(e)==="object"&&e.prefix&&e.iconName&&e.icon)return e;if(qg.icon)return qg.icon(e);if(e===null)return null;if(e&&ch(e)==="object"&&e.prefix&&e.iconName)return e;if(Array.isArray(e)&&e.length===2)return{prefix:e[0],iconName:e[1]};if(typeof e=="string")return{prefix:"fas",iconName:e}}function t0(e,t){return Array.isArray(t)&&t.length>0||!Array.isArray(t)&&t?pl({},e,t):{}}var Mc=Pi.forwardRef(function(e,t){var n=e.icon,r=e.mask,i=e.symbol,o=e.className,a=e.title,s=e.titleId,l=e.maskId,u=V4(n),c=t0("classes",[].concat(Yg(kZ(e)),Yg(o.split(" ")))),f=t0("transform",typeof e.transform=="string"?qg.transform(e.transform):e.transform),d=t0("mask",V4(r)),h=TZ(u,ta(ta(ta(ta({},c),f),d),{},{symbol:i,title:a,titleId:s,maskId:l}));if(!h)return RZ("Could not find icon",u),null;var v=h.abstract,g={ref:t};return Object.keys(e).forEach(function(C){Mc.defaultProps.hasOwnProperty(C)||(g[C]=e[C])}),DZ(v[0],g)});Mc.displayName="FontAwesomeIcon";Mc.propTypes={beat:J.bool,border:J.bool,beatFade:J.bool,bounce:J.bool,className:J.string,fade:J.bool,flash:J.bool,mask:J.oneOfType([J.object,J.array,J.string]),maskId:J.string,fixedWidth:J.bool,inverse:J.bool,flip:J.oneOf([!0,!1,"horizontal","vertical","both"]),icon:J.oneOfType([J.object,J.array,J.string]),listItem:J.bool,pull:J.oneOf(["right","left"]),pulse:J.bool,rotation:J.oneOf([0,90,180,270]),shake:J.bool,size:J.oneOf(["2xs","xs","sm","lg","xl","2xl","1x","2x","3x","4x","5x","6x","7x","8x","9x","10x"]),spin:J.bool,spinPulse:J.bool,spinReverse:J.bool,symbol:J.oneOfType([J.bool,J.string]),title:J.string,titleId:J.string,transform:J.oneOfType([J.string,J.object]),swapOpacity:J.bool};Mc.defaultProps={border:!1,className:"",mask:null,maskId:null,fixedWidth:!1,inverse:!1,flip:!1,icon:null,listItem:!1,pull:null,pulse:!1,rotation:null,size:null,spin:!1,spinPulse:!1,spinReverse:!1,beat:!1,fade:!1,beatFade:!1,bounce:!1,shake:!1,symbol:!1,title:"",titleId:null,transform:null,swapOpacity:!1};var DZ=n3.bind(null,Pi.createElement),MZ={prefix:"fas",iconName:"microphone",icon:[384,512,[],"f130","M192 0C139 0 96 43 96 96V256c0 53 43 96 96 96s96-43 96-96V96c0-53-43-96-96-96zM64 216c0-13.3-10.7-24-24-24s-24 10.7-24 24v40c0 89.1 66.2 162.7 152 174.4V464H120c-13.3 0-24 10.7-24 24s10.7 24 24 24h72 72c13.3 0 24-10.7 24-24s-10.7-24-24-24H216V430.4c85.8-11.7 152-85.3 152-174.4V216c0-13.3-10.7-24-24-24s-24 10.7-24 24v40c0 70.7-57.3 128-128 128s-128-57.3-128-128V216z"]};const PZ=({onSend:e,onMicrophoneClick:t,onStopClick:n,disabled:r,placeholder:i,clearOnSend:o,recognizedText:a,isListening:s,isRecognizing:l,setRecognizedText:u})=>{const[c,f]=E.useState(""),[d,h]=E.useState(""),[v,g]=E.useState(!1);E.useEffect(()=>{l?(h(a),g(!0)):g(!1)},[a,l]);const C=()=>{if(r||!c.trim()&&!d.trim())return;e(c||d),o&&(f(""),h(""),u(""))},m=T=>{T.key==="Enter"&&!T.shiftKey&&(T.preventDefault(),C())},y=(T,S)=>{f(S||""),h(S||"")};return r||c.trim(),gt(ln,{horizontal:!0,className:Qi.questionInputContainer,children:[re(H_,{className:Qi.questionInputTextArea,placeholder:i,multiline:!0,resizable:!1,borderless:!0,value:c||d,onChange:(T,S)=>{S!==void 0&&(y(T,S),u(S))},onKeyDown:m}),gt("div",{className:Qi.microphoneAndSendContainer,children:[re("div",{className:Qi.questionInputMicrophone,onClick:s?n:t,onKeyDown:T=>T.key==="Enter"||T.key===" "?s?n():t():null,role:"button",tabIndex:0,"aria-label":"Microphone button",children:v?re(Mc,{icon:MZ,className:Qi.microphoneIconActive,style:{color:"blue"}}):re("img",{src:RQ,className:Qi.microphoneIcon,alt:"Microphone"})}),re("div",{role:"button",tabIndex:0,"aria-label":"Ask question button",onClick:C,onKeyDown:T=>T.key==="Enter"||T.key===" "?C():null,className:Qi.questionInputSendButtonContainer,children:r?re(FP,{className:Qi.questionInputSendButtonDisabled}):re("img",{src:FQ,className:Qi.questionInputSendButton,alt:"Send"})})]})]})},OZ=()=>{const e=E.useRef(""),t=E.useRef(null),[n,r]=E.useState(!1),[i,o]=E.useState(!1),[a,s]=E.useState(),[l,u]=E.useState(!1),[c,f]=E.useState([]),d=E.useRef([]),[h,v]=E.useState(mg()),[g,C]=E.useState(""),[m,y]=E.useState(""),[T,S]=E.useState(!1),[w,b]=E.useState(!1),A=E.useRef(null),[F,D]=E.useState(""),[I,B]=E.useState(""),M=async _=>{e.current=_,r(!0),o(!0);const ee=new AbortController;d.current.unshift(ee);const X={role:"user",content:m||_},x={id:h,messages:[...c,X]};let ge={};try{const pe=await E$(x,ee.signal);if(pe!=null&&pe.body){const it=pe.body.getReader();let Nt="";for(;;){const{done:Bt,value:Ct}=await it.read();if(Bt)break;var xt=new TextDecoder("utf-8").decode(Ct);xt.split(` +`).forEach(er=>{try{Nt+=er,ge=JSON.parse(Nt),o(!1),ge.error?f([...c,X,{role:"error",content:ge.error}]):f([...c,X,...ge.choices[0].messages]),Nt=""}catch{}})}f([...c,X,...ge.choices[0].messages])}}catch{ee.signal.aborted||(console.error(ge),alert("An error occurred. Please try again. If the problem persists, please contact the site administrator.")),f([...c,X])}finally{r(!1),o(!1),d.current=d.current.filter(pe=>pe!==ee)}return ee.abort()};E.useEffect(()=>{async function _(){try{const ee=await fetch("/api/config");if(!ee.ok)throw new Error("Network response was not ok");const X=await ee.json(),x=X.azureSpeechKey,ge=X.azureSpeechRegion;D(x),B(ge)}catch(ee){console.error("Error fetching server configuration:",ee)}}_()},[]);const oe=(_,ee)=>{if(!T)if(S(!0),!_||!ee)console.error("Azure Speech subscription key or region is not defined.");else{const X=u6.fromSubscription(_,ee),x=Xl.fromDefaultMicrophoneInput(),ge=new S9(X,x);A.current=ge,A.current.recognized=(xt,pe)=>{if(pe.result.reason===ei.RecognizedSpeech){const it=pe.result.text;C(it),y(it)}},A.current.startContinuousRecognitionAsync(()=>{S(!0),b(!0)})}},V=()=>{T&&(A.current&&A.current.stopContinuousRecognitionAsync(()=>{var _;(_=A.current)==null||_.close()}),S(!1),y(""),b(!1))},P=()=>{T?(V(),y(g)):oe(F,I)},z=()=>{e.current="",s(void 0),f([]),v(mg())},Z=()=>{d.current.forEach(_=>_.abort()),o(!1),r(!1)};E.useEffect(()=>{var _;return(_=t.current)==null?void 0:_.scrollIntoView({behavior:"smooth"})},[i]);const H=_=>{s([_.content,_.id,_.title??"",_.filepath??"","",""]),u(!0)},U=_=>{if(_.role==="tool")try{return JSON.parse(_.content).citations}catch{return[]}return[]};return re("div",{className:Qe.container,children:gt(ln,{horizontal:!0,className:Qe.chatRoot,children:[gt("div",{className:Qe.chatContainer,children:[e.current?gt("div",{className:Qe.chatMessageStream,style:{marginBottom:n?"40px":"0px"},children:[c.map((_,ee)=>re(r0,{children:_.role==="user"?re("div",{className:Qe.chatMessageUser,children:re("div",{className:Qe.chatMessageUserMessage,children:_.content})}):_.role==="assistant"||_.role==="error"?re("div",{className:Qe.chatMessageGpt,children:re(T4,{answer:{answer:_.role==="assistant"?_.content:"Sorry, an error occurred. Try refreshing the conversation or waiting a few minutes. If the issue persists, contact your system administrator. Error: "+_.content,citations:_.role==="assistant"?U(c[ee-1]):[]},onCitationClicked:X=>H(X)})}):null})),i&>(r0,{children:[re("div",{className:Qe.chatMessageUser,children:re("div",{className:Qe.chatMessageUserMessage,children:e.current})}),re("div",{className:Qe.chatMessageGpt,children:re(T4,{answer:{answer:"Generating answer...",citations:[]},onCitationClicked:()=>null})})]}),re("div",{ref:t})]}):gt(ln,{className:Qe.chatEmptyState,children:[re("img",{src:Y_,className:Qe.chatIcon,"aria-hidden":"true"}),re("h1",{className:Qe.chatEmptyStateTitle,children:"Start chatting"}),re("h2",{className:Qe.chatEmptyStateSubtitle,children:"This chatbot is configured to answer your questions"})]}),gt("div",{children:[T&&!w&&re("p",{children:"Please wait..."})," ",w&&re("p",{children:"Listening..."})," "]}),gt(ln,{horizontal:!0,className:Qe.chatInput,children:[n&>(ln,{horizontal:!0,className:Qe.stopGeneratingContainer,role:"button","aria-label":"Stop generating",tabIndex:0,onClick:Z,onKeyDown:_=>_.key==="Enter"||_.key===" "?Z():null,children:[re(DP,{className:Qe.stopGeneratingIcon,"aria-hidden":"true"}),re("span",{className:Qe.stopGeneratingText,"aria-hidden":"true",children:"Stop generating"})]}),re(IP,{className:Qe.clearChatBroom,style:{background:n||c.length===0?"#BDBDBD":"radial-gradient(109.81% 107.82% at 100.1% 90.19%, #0F6CBD 33.63%, #2D87C3 70.31%, #8DDDD8 100%)",cursor:n||c.length===0?"":"pointer"},onClick:z,onKeyDown:_=>_.key==="Enter"||_.key===" "?z():null,"aria-label":"Clear session",role:"button",tabIndex:0}),re(PZ,{clearOnSend:!0,placeholder:"Type a new question...",disabled:n,onSend:_=>M(_),recognizedText:m,onMicrophoneClick:P,onStopClick:V,isListening:w,isRecognizing:T,setRecognizedText:y})]})]}),c.length>0&&l&&a&>(ln.Item,{className:Qe.citationPanel,children:[gt(ln,{horizontal:!0,className:Qe.citationPanelHeaderContainer,horizontalAlign:"space-between",verticalAlign:"center",children:[re("span",{className:Qe.citationPanelHeader,children:"Citations"}),re(NP,{className:Qe.citationPanelDismiss,onClick:()=>u(!1)})]}),re("h5",{className:Qe.citationPanelTitle,children:a[2]}),re(Qh,{className:Qe.citationPanelContent,children:a[0],remarkPlugins:[wb],rehypePlugins:[YG]})]})]})})};IM();function LZ(){return re(Nk,{children:re(Ak,{children:gt(ed,{path:"/",element:re(MP,{}),children:[re(ed,{index:!0,element:re(OZ,{})}),re(ed,{path:"*",element:re(PP,{})})]})})})}i0.createRoot(document.getElementById("root")).render(re(Pi.StrictMode,{children:re(LZ,{})}))});export default BZ(); +//# sourceMappingURL=index-5e1f6cf7.js.map diff --git a/code/static/assets/index-5e1f6cf7.js.map b/code/static/assets/index-5e1f6cf7.js.map new file mode 100644 index 000000000..c1a5d2658 --- /dev/null +++ b/code/static/assets/index-5e1f6cf7.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index-5e1f6cf7.js","sources":["../../frontend/node_modules/react/cjs/react.production.min.js","../../frontend/node_modules/react/index.js","../../frontend/node_modules/react/cjs/react-jsx-runtime.production.min.js","../../frontend/node_modules/react/jsx-runtime.js","../../frontend/node_modules/scheduler/cjs/scheduler.production.min.js","../../frontend/node_modules/scheduler/index.js","../../frontend/node_modules/react-dom/cjs/react-dom.production.min.js","../../frontend/node_modules/react-dom/index.js","../../frontend/node_modules/react-dom/client.js","../../frontend/node_modules/@remix-run/router/dist/router.js","../../frontend/node_modules/react-router/dist/index.js","../../frontend/node_modules/react-router-dom/dist/index.js","../../frontend/node_modules/@fluentui/set-version/lib/setVersion.js","../../frontend/node_modules/@fluentui/set-version/lib/index.js","../../frontend/node_modules/tslib/tslib.es6.mjs","../../frontend/node_modules/@fluentui/merge-styles/lib/Stylesheet.js","../../frontend/node_modules/@fluentui/merge-styles/lib/extractStyleParts.js","../../frontend/node_modules/@fluentui/merge-styles/lib/StyleOptionsState.js","../../frontend/node_modules/@fluentui/merge-styles/lib/transforms/kebabRules.js","../../frontend/node_modules/@fluentui/merge-styles/lib/getVendorSettings.js","../../frontend/node_modules/@fluentui/merge-styles/lib/transforms/prefixRules.js","../../frontend/node_modules/@fluentui/merge-styles/lib/transforms/provideUnits.js","../../frontend/node_modules/@fluentui/merge-styles/lib/transforms/rtlifyRules.js","../../frontend/node_modules/@fluentui/merge-styles/lib/tokenizeWithParentheses.js","../../frontend/node_modules/@fluentui/merge-styles/lib/styleToClassName.js","../../frontend/node_modules/@fluentui/merge-styles/lib/mergeStyles.js","../../frontend/node_modules/@fluentui/merge-styles/lib/concatStyleSets.js","../../frontend/node_modules/@fluentui/merge-styles/lib/mergeStyleSets.js","../../frontend/node_modules/@fluentui/merge-styles/lib/concatStyleSetsWithProps.js","../../frontend/node_modules/@fluentui/merge-styles/lib/fontFace.js","../../frontend/node_modules/@fluentui/merge-styles/lib/keyframes.js","../../frontend/node_modules/@fluentui/style-utilities/lib/utilities/buildClassMap.js","../../frontend/node_modules/@fluentui/utilities/lib/dom/canUseDOM.js","../../frontend/node_modules/@fluentui/utilities/lib/dom/getWindow.js","../../frontend/node_modules/@fluentui/utilities/lib/Async.js","../../frontend/node_modules/@fluentui/utilities/lib/object.js","../../frontend/node_modules/@fluentui/utilities/lib/EventGroup.js","../../frontend/node_modules/@fluentui/utilities/lib/dom/getDocument.js","../../frontend/node_modules/@fluentui/utilities/lib/scroll.js","../../frontend/node_modules/@fluentui/utilities/lib/warn/warn.js","../../frontend/node_modules/@fluentui/utilities/lib/warn/warnConditionallyRequiredProps.js","../../frontend/node_modules/@fluentui/utilities/lib/BaseComponent.js","../../frontend/node_modules/@fluentui/utilities/lib/DelayedRender.js","../../frontend/node_modules/@fluentui/utilities/lib/GlobalSettings.js","../../frontend/node_modules/@fluentui/utilities/lib/KeyCodes.js","../../frontend/node_modules/@fluentui/utilities/lib/Rectangle.js","../../frontend/node_modules/@fluentui/utilities/lib/appendFunction.js","../../frontend/node_modules/@fluentui/utilities/lib/aria.js","../../frontend/node_modules/@fluentui/utilities/lib/array.js","../../frontend/node_modules/@fluentui/utilities/lib/sessionStorage.js","../../frontend/node_modules/@fluentui/utilities/lib/rtl.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/isVirtualElement.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/getVirtualParent.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/getParent.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/elementContains.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/findElementRecursive.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/elementContainsAttribute.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/setPortalAttribute.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/portalContainsElement.js","../../frontend/node_modules/@fluentui/dom-utilities/lib/setVirtualParent.js","../../frontend/node_modules/@fluentui/utilities/lib/focus.js","../../frontend/node_modules/@fluentui/utilities/lib/dom/on.js","../../frontend/node_modules/@fluentui/utilities/lib/classNamesFunction.js","../../frontend/node_modules/@fluentui/utilities/lib/memoize.js","../../frontend/node_modules/@fluentui/utilities/lib/componentAs/composeComponentAs.js","../../frontend/node_modules/@fluentui/utilities/lib/controlled.js","../../frontend/node_modules/@fluentui/utilities/lib/css.js","../../frontend/node_modules/@fluentui/utilities/lib/customizations/Customizations.js","../../frontend/node_modules/@fluentui/utilities/lib/customizations/CustomizerContext.js","../../frontend/node_modules/@fluentui/utilities/lib/customizations/mergeSettings.js","../../frontend/node_modules/@fluentui/utilities/lib/customizations/mergeCustomizations.js","../../frontend/node_modules/@fluentui/utilities/lib/customizations/Customizer.js","../../frontend/node_modules/@fluentui/utilities/lib/hoistStatics.js","../../frontend/node_modules/@fluentui/utilities/lib/customizations/customizable.js","../../frontend/node_modules/@fluentui/utilities/lib/customizations/useCustomizationSettings.js","../../frontend/node_modules/@fluentui/utilities/lib/extendComponent.js","../../frontend/node_modules/@fluentui/utilities/lib/getId.js","../../frontend/node_modules/@fluentui/utilities/lib/properties.js","../../frontend/node_modules/@fluentui/utilities/lib/hoist.js","../../frontend/node_modules/@fluentui/utilities/lib/initializeComponentRef.js","../../frontend/node_modules/@fluentui/utilities/lib/keyboard.js","../../frontend/node_modules/@fluentui/utilities/lib/setFocusVisibility.js","../../frontend/node_modules/@fluentui/utilities/lib/useFocusRects.js","../../frontend/node_modules/@fluentui/utilities/lib/FocusRectsProvider.js","../../frontend/node_modules/@fluentui/utilities/lib/localStorage.js","../../frontend/node_modules/@fluentui/utilities/lib/language.js","../../frontend/node_modules/@fluentui/utilities/lib/merge.js","../../frontend/node_modules/@fluentui/utilities/lib/mobileDetector.js","../../frontend/node_modules/@fluentui/utilities/lib/modalize.js","../../frontend/node_modules/@fluentui/utilities/lib/osDetector.js","../../frontend/node_modules/@fluentui/utilities/lib/renderFunction/composeRenderFunction.js","../../frontend/node_modules/@fluentui/utilities/lib/styled.js","../../frontend/node_modules/@fluentui/utilities/lib/ie11Detector.js","../../frontend/node_modules/@fluentui/utilities/lib/getPropsWithDefaults.js","../../frontend/node_modules/@fluentui/utilities/lib/createMergedRef.js","../../frontend/node_modules/@fluentui/utilities/lib/useIsomorphicLayoutEffect.js","../../frontend/node_modules/@fluentui/style-utilities/lib/utilities/icons.js","../../frontend/node_modules/@fluentui/theme/lib/utilities/makeSemanticColors.js","../../frontend/node_modules/@fluentui/theme/lib/mergeThemes.js","../../frontend/node_modules/@fluentui/theme/lib/colors/DefaultPalette.js","../../frontend/node_modules/@fluentui/theme/lib/effects/FluentDepths.js","../../frontend/node_modules/@fluentui/theme/lib/effects/DefaultEffects.js","../../frontend/node_modules/@fluentui/theme/lib/spacing/DefaultSpacing.js","../../frontend/node_modules/@fluentui/theme/lib/motion/AnimationStyles.js","../../frontend/node_modules/@fluentui/theme/lib/fonts/FluentFonts.js","../../frontend/node_modules/@fluentui/theme/lib/fonts/createFontStyles.js","../../frontend/node_modules/@fluentui/theme/lib/fonts/DefaultFontStyles.js","../../frontend/node_modules/@fluentui/theme/lib/createTheme.js","../../frontend/node_modules/@fluentui/style-utilities/lib/styles/CommonStyles.js","../../frontend/node_modules/@fluentui/style-utilities/lib/styles/zIndexes.js","../../frontend/node_modules/@fluentui/style-utilities/lib/styles/getFocusStyle.js","../../frontend/node_modules/@fluentui/style-utilities/lib/styles/hiddenContentStyle.js","../../frontend/node_modules/@fluentui/style-utilities/lib/styles/getGlobalClassNames.js","../../frontend/node_modules/@microsoft/load-themed-styles/lib-es6/index.js","../../frontend/node_modules/@fluentui/style-utilities/lib/styles/theme.js","../../frontend/node_modules/@fluentui/style-utilities/lib/styles/GeneralStyles.js","../../frontend/node_modules/@fluentui/style-utilities/lib/styles/getPlaceholderStyles.js","../../frontend/node_modules/@fluentui/style-utilities/lib/classNames/AnimationClassNames.js","../../frontend/node_modules/@fluentui/style-utilities/lib/cdn.js","../../frontend/node_modules/@fluentui/style-utilities/lib/version.js","../../frontend/node_modules/@fluentui/style-utilities/lib/index.js","../../frontend/node_modules/@fluentui/react/lib/common/DirectionalHint.js","../../frontend/node_modules/@fluentui/react/lib/utilities/positioning/positioning.types.js","../../frontend/node_modules/@fluentui/react/lib/utilities/positioning/positioning.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useAsync.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useConst.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useBoolean.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useEventCallback.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useId.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useMergedRefs.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useOnEvent.js","../../frontend/node_modules/@fluentui/react-hooks/lib/usePrevious.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useSetTimeout.js","../../frontend/node_modules/@fluentui/react-window-provider/lib/WindowProvider.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useTarget.js","../../frontend/node_modules/@fluentui/react-hooks/lib/useUnmount.js","../../frontend/node_modules/@fluentui/react/lib/components/Popup/Popup.js","../../frontend/node_modules/@fluentui/react/lib/components/Callout/CalloutContent.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Callout/CalloutContent.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Callout/CalloutContent.js","../../frontend/node_modules/@fluentui/react-portal-compat-context/lib/PortalCompatContext.js","../../frontend/node_modules/@fluentui/react/lib/components/Fabric/Fabric.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Fabric/Fabric.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Fabric/Fabric.js","../../frontend/node_modules/@fluentui/react/lib/components/Layer/Layer.notification.js","../../frontend/node_modules/@fluentui/react/lib/components/Layer/Layer.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Layer/Layer.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Layer/Layer.js","../../frontend/node_modules/@fluentui/react/lib/components/Callout/Callout.js","../../frontend/node_modules/@fluentui/react/lib/components/FocusTrapZone/FocusTrapZone.js","../../frontend/node_modules/@fluentui/react/lib/components/Icon/Icon.types.js","../../frontend/node_modules/@fluentui/react/lib/components/Image/Image.types.js","../../frontend/node_modules/@fluentui/react/lib/components/Image/Image.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Image/Image.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Image/Image.js","../../frontend/node_modules/@fluentui/react/lib/components/Icon/Icon.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Icon/FontIcon.js","../../frontend/node_modules/@fluentui/react/lib/components/Icon/Icon.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Icon/Icon.js","../../frontend/node_modules/@fluentui/react/lib/components/Icon/ImageIcon.js","../../frontend/node_modules/@fluentui/react-focus/lib/components/FocusZone/FocusZone.types.js","../../frontend/node_modules/@fluentui/react-focus/lib/components/FocusZone/FocusZone.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenu.types.js","../../frontend/node_modules/@fluentui/react/lib/utilities/contextualMenu/contextualMenuUtility.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenuItem.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Divider/VerticalDivider.classNames.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenu.cnstyles.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenu.classNames.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenuItem.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuItemWrapper.js","../../frontend/node_modules/@fluentui/react/lib/utilities/keytips/KeytipConstants.js","../../frontend/node_modules/@fluentui/react/lib/utilities/keytips/KeytipManager.js","../../frontend/node_modules/@fluentui/react/lib/utilities/keytips/KeytipUtils.js","../../frontend/node_modules/@fluentui/react/lib/components/KeytipData/useKeytipData.js","../../frontend/node_modules/@fluentui/react/lib/components/KeytipData/KeytipData.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuAnchor.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuButton.js","../../frontend/node_modules/@fluentui/react/lib/components/Divider/VerticalDivider.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Divider/VerticalDivider.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Divider/VerticalDivider.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenuItemWrapper/ContextualMenuSplitButton.js","../../frontend/node_modules/@fluentui/react/lib/utilities/decorators/BaseDecorator.js","../../frontend/node_modules/@fluentui/react/lib/utilities/decorators/withResponsiveMode.js","../../frontend/node_modules/@fluentui/react/lib/utilities/hooks/useResponsiveMode.js","../../frontend/node_modules/@fluentui/react/lib/utilities/MenuContext/MenuContext.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenu.base.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenu.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/ContextualMenu/ContextualMenu.js","../../frontend/node_modules/@fluentui/react/lib/components/Button/BaseButton.classNames.js","../../frontend/node_modules/@fluentui/react/lib/components/Button/SplitButton/SplitButton.classNames.js","../../frontend/node_modules/@fluentui/react/lib/components/Button/BaseButton.js","../../frontend/node_modules/@fluentui/react/lib/components/Button/BaseButton.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Button/SplitButton/SplitButton.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Button/IconButton/IconButton.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Button/IconButton/IconButton.js","../../frontend/node_modules/@fluentui/react/lib/components/Label/Label.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Label/Label.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Label/Label.js","../../frontend/node_modules/@fluentui/react/lib/components/TextField/TextField.base.js","../../frontend/node_modules/@fluentui/react/lib/components/TextField/TextField.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/TextField/TextField.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/DialogContent.types.js","../../frontend/node_modules/@fluentui/react/lib/components/Modal/Modal.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Overlay/Overlay.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Overlay/Overlay.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Overlay/Overlay.js","../../frontend/node_modules/@fluentui/react/lib/utilities/DraggableZone/DraggableZone.styles.js","../../frontend/node_modules/@fluentui/react/lib/utilities/DraggableZone/DraggableZone.js","../../frontend/node_modules/@fluentui/react/lib/components/Modal/Modal.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Modal/Modal.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/DialogFooter.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/DialogFooter.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/DialogFooter.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/DialogContent.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/DialogContent.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/DialogContent.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/Dialog.base.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/Dialog.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Dialog/Dialog.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-0.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-1.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-2.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-3.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-4.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-5.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-6.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-7.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-8.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-9.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-10.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-11.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-12.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-13.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-14.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-15.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-16.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/fabric-icons-17.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/iconAliases.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/version.js","../../frontend/node_modules/@fluentui/font-icons-mdl2/lib/index.js","../../frontend/node_modules/@fluentui/foundation-legacy/lib/utilities.js","../../frontend/node_modules/@fluentui/foundation-legacy/lib/slots.js","../../frontend/node_modules/@fluentui/foundation-legacy/lib/createComponent.js","../../frontend/node_modules/@fluentui/react/lib/components/Stack/StackItem/StackItem.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Stack/StackItem/StackItem.js","../../frontend/node_modules/@fluentui/react/lib/components/Stack/StackUtils.js","../../frontend/node_modules/@fluentui/react/lib/components/Stack/Stack.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Stack/Stack.js","../../frontend/node_modules/@fluentui/react/lib/components/Text/Text.view.js","../../frontend/node_modules/@fluentui/react/lib/components/Text/Text.styles.js","../../frontend/node_modules/@fluentui/react/lib/components/Text/Text.js","../../frontend/src/assets/Azure.svg","../../frontend/node_modules/@griffel/core/constants.esm.js","../../frontend/node_modules/@emotion/hash/dist/emotion-hash.esm.js","../../frontend/node_modules/@griffel/core/runtime/utils/hashSequence.esm.js","../../frontend/node_modules/@griffel/core/runtime/reduceToClassNameForSlots.esm.js","../../frontend/node_modules/@griffel/core/mergeClasses.esm.js","../../frontend/node_modules/@griffel/core/runtime/utils/normalizeCSSBucketEntry.esm.js","../../frontend/node_modules/@griffel/core/renderer/createIsomorphicStyleSheet.esm.js","../../frontend/node_modules/@griffel/core/renderer/getStyleSheetForBucket.esm.js","../../frontend/node_modules/@griffel/core/renderer/createDOMRenderer.esm.js","../../frontend/node_modules/@griffel/core/__styles.esm.js","../../frontend/node_modules/@griffel/react/RendererContext.esm.js","../../frontend/node_modules/@griffel/react/TextDirectionContext.esm.js","../../frontend/node_modules/@griffel/react/__styles.esm.js","../../frontend/node_modules/@fluentui/react-icons/lib/utils/useIconState.js","../../frontend/node_modules/@fluentui/react-icons/lib/utils/createFluentIcon.js","../../frontend/node_modules/@fluentui/react-icons/lib/icons/chunk-0.js","../../frontend/node_modules/@fluentui/react-icons/lib/icons/chunk-1.js","../../frontend/node_modules/@fluentui/react-icons/lib/icons/chunk-3.js","../../frontend/src/pages/layout/Layout.tsx","../../frontend/src/pages/NoPage.tsx","../../frontend/node_modules/uuid/dist/esm-browser/rng.js","../../frontend/node_modules/uuid/dist/esm-browser/stringify.js","../../frontend/node_modules/uuid/dist/esm-browser/native.js","../../frontend/node_modules/uuid/dist/esm-browser/v4.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Guid.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/PlatformEvent.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/AudioSourceEvents.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/ConnectionEvents.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Error.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/ConnectionMessage.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/ConnectionOpenResponse.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/EventSource.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Events.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/IConnection.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/List.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Promise.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Queue.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/RawWebsocketMessage.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/RiffPcmEncoder.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Stream.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/ChunkedArrayBufferStream.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Timeout.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/BackgroundError.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/HeaderNames.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/IAuthentication.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/CognitiveSubscriptionKeyAuthentication.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/CognitiveTokenAuthentication.js","../../frontend/__vite-browser-external","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Contracts.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioStreamFormat.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/MicAudioSource.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/FileAudioSource.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/PCMRecorder.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/WebsocketMessageAdapter.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/WebsocketConnection.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/ReplayableAudioNode.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioFileWriter.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioInputStream.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/SpeechSynthesisOutputFormat.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioOutputFormat.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioOutputStream.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioConfig.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/CancellationReason.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/PullAudioInputStreamCallback.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/PushAudioOutputStreamCallback.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/SessionEventArgs.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/RecognitionEventArgs.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/OutputFormat.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/RecognitionResult.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/SpeechRecognitionResult.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/SpeechRecognitionEventArgs.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/CancellationEventArgsBase.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/SpeechRecognitionCanceledEventArgs.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/ResultReason.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/SpeechConfig.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/PropertyCollection.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/PropertyId.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Recognizer.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/SpeechRecognizer.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/CancellationErrorCodes.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/QueryParameterNames.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/ConnectionFactoryBase.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/ProfanityOption.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/SpeakerAudioDestination.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.browser/ProxyInfo.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/RecognitionEvents.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/SpeechConnectionMessage.Internal.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/ServiceRecognizerBase.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/RecognizerConfig.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/WebsocketMessageFormatter.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/SpeechConnectionFactory.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/EnumTranslation.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/ServiceMessages/Enums.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/ServiceMessages/SpeechDetected.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/ServiceMessages/SpeechHypothesis.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/SpeechServiceRecognizer.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/ServiceMessages/DetailedSpeechPhrase.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/ServiceMessages/SimpleSpeechPhrase.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/ServiceTelemetryListener.Internal.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/RequestSession.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/SpeechContext.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/DynamicGrammarBuilder.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/AgentConfig.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/SpeechServiceConfig.js","../../frontend/node_modules/microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/Exports.js","../../frontend/node_modules/react-markdown/lib/uri-transformer.js","../../frontend/node_modules/is-buffer/index.js","../../frontend/node_modules/unist-util-stringify-position/lib/index.js","../../frontend/node_modules/vfile-message/lib/index.js","../../frontend/node_modules/vfile/lib/minpath.browser.js","../../frontend/node_modules/vfile/lib/minproc.browser.js","../../frontend/node_modules/vfile/lib/minurl.shared.js","../../frontend/node_modules/vfile/lib/minurl.browser.js","../../frontend/node_modules/vfile/lib/index.js","../../frontend/node_modules/bail/index.js","../../frontend/node_modules/extend/index.js","../../frontend/node_modules/is-plain-obj/index.js","../../frontend/node_modules/trough/index.js","../../frontend/node_modules/unified/lib/index.js","../../frontend/node_modules/mdast-util-to-string/lib/index.js","../../frontend/node_modules/micromark-util-chunked/index.js","../../frontend/node_modules/micromark-util-combine-extensions/index.js","../../frontend/node_modules/micromark-util-character/lib/unicode-punctuation-regex.js","../../frontend/node_modules/micromark-util-character/index.js","../../frontend/node_modules/micromark-factory-space/index.js","../../frontend/node_modules/micromark/lib/initialize/content.js","../../frontend/node_modules/micromark/lib/initialize/document.js","../../frontend/node_modules/micromark-util-classify-character/index.js","../../frontend/node_modules/micromark-util-resolve-all/index.js","../../frontend/node_modules/micromark-core-commonmark/lib/attention.js","../../frontend/node_modules/micromark-core-commonmark/lib/autolink.js","../../frontend/node_modules/micromark-core-commonmark/lib/blank-line.js","../../frontend/node_modules/micromark-core-commonmark/lib/block-quote.js","../../frontend/node_modules/micromark-core-commonmark/lib/character-escape.js","../../frontend/node_modules/decode-named-character-reference/index.dom.js","../../frontend/node_modules/micromark-core-commonmark/lib/character-reference.js","../../frontend/node_modules/micromark-core-commonmark/lib/code-fenced.js","../../frontend/node_modules/micromark-core-commonmark/lib/code-indented.js","../../frontend/node_modules/micromark-core-commonmark/lib/code-text.js","../../frontend/node_modules/micromark-util-subtokenize/index.js","../../frontend/node_modules/micromark-core-commonmark/lib/content.js","../../frontend/node_modules/micromark-factory-destination/index.js","../../frontend/node_modules/micromark-factory-label/index.js","../../frontend/node_modules/micromark-factory-title/index.js","../../frontend/node_modules/micromark-factory-whitespace/index.js","../../frontend/node_modules/micromark-util-normalize-identifier/index.js","../../frontend/node_modules/micromark-core-commonmark/lib/definition.js","../../frontend/node_modules/micromark-core-commonmark/lib/hard-break-escape.js","../../frontend/node_modules/micromark-core-commonmark/lib/heading-atx.js","../../frontend/node_modules/micromark-util-html-tag-name/index.js","../../frontend/node_modules/micromark-core-commonmark/lib/html-flow.js","../../frontend/node_modules/micromark-core-commonmark/lib/html-text.js","../../frontend/node_modules/micromark-core-commonmark/lib/label-end.js","../../frontend/node_modules/micromark-core-commonmark/lib/label-start-image.js","../../frontend/node_modules/micromark-core-commonmark/lib/label-start-link.js","../../frontend/node_modules/micromark-core-commonmark/lib/line-ending.js","../../frontend/node_modules/micromark-core-commonmark/lib/thematic-break.js","../../frontend/node_modules/micromark-core-commonmark/lib/list.js","../../frontend/node_modules/micromark-core-commonmark/lib/setext-underline.js","../../frontend/node_modules/micromark/lib/initialize/flow.js","../../frontend/node_modules/micromark/lib/initialize/text.js","../../frontend/node_modules/micromark/lib/create-tokenizer.js","../../frontend/node_modules/micromark/lib/constructs.js","../../frontend/node_modules/micromark/lib/parse.js","../../frontend/node_modules/micromark/lib/preprocess.js","../../frontend/node_modules/micromark/lib/postprocess.js","../../frontend/node_modules/micromark-util-decode-numeric-character-reference/index.js","../../frontend/node_modules/micromark-util-decode-string/index.js","../../frontend/node_modules/mdast-util-from-markdown/lib/index.js","../../frontend/node_modules/remark-parse/lib/index.js","../../frontend/node_modules/unist-builder/lib/index.js","../../frontend/node_modules/mdast-util-to-hast/lib/traverse.js","../../frontend/node_modules/unist-util-is/lib/index.js","../../frontend/node_modules/unist-util-visit-parents/lib/index.js","../../frontend/node_modules/unist-util-visit/lib/index.js","../../frontend/node_modules/unist-util-position/lib/index.js","../../frontend/node_modules/unist-util-generated/lib/index.js","../../frontend/node_modules/mdast-util-definitions/lib/index.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/thematic-break.js","../../frontend/node_modules/mdast-util-to-hast/lib/wrap.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/list.js","../../frontend/node_modules/mdast-util-to-hast/lib/footer.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/blockquote.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/break.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/code.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/delete.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/emphasis.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/footnote-reference.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/footnote.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/heading.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/html.js","../../frontend/node_modules/mdurl/encode.js","../../frontend/node_modules/mdast-util-to-hast/lib/revert.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/image-reference.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/image.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/inline-code.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/link-reference.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/link.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/list-item.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/paragraph.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/root.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/strong.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/table.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/text.js","../../frontend/node_modules/mdast-util-to-hast/lib/handlers/index.js","../../frontend/node_modules/mdast-util-to-hast/lib/index.js","../../frontend/node_modules/remark-rehype/index.js","../../frontend/node_modules/prop-types/lib/ReactPropTypesSecret.js","../../frontend/node_modules/prop-types/factoryWithThrowingShims.js","../../frontend/node_modules/prop-types/index.js","../../frontend/node_modules/property-information/lib/util/schema.js","../../frontend/node_modules/property-information/lib/util/merge.js","../../frontend/node_modules/property-information/lib/normalize.js","../../frontend/node_modules/property-information/lib/util/info.js","../../frontend/node_modules/property-information/lib/util/types.js","../../frontend/node_modules/property-information/lib/util/defined-info.js","../../frontend/node_modules/property-information/lib/util/create.js","../../frontend/node_modules/property-information/lib/xlink.js","../../frontend/node_modules/property-information/lib/xml.js","../../frontend/node_modules/property-information/lib/util/case-sensitive-transform.js","../../frontend/node_modules/property-information/lib/util/case-insensitive-transform.js","../../frontend/node_modules/property-information/lib/xmlns.js","../../frontend/node_modules/property-information/lib/aria.js","../../frontend/node_modules/property-information/lib/html.js","../../frontend/node_modules/property-information/lib/svg.js","../../frontend/node_modules/property-information/lib/find.js","../../frontend/node_modules/property-information/lib/hast-to-react.js","../../frontend/node_modules/property-information/index.js","../../frontend/node_modules/react-markdown/lib/rehype-filter.js","../../frontend/node_modules/react-is/cjs/react-is.production.min.js","../../frontend/node_modules/react-is/index.js","../../frontend/node_modules/hast-util-whitespace/index.js","../../frontend/node_modules/space-separated-tokens/index.js","../../frontend/node_modules/comma-separated-tokens/index.js","../../frontend/node_modules/inline-style-parser/index.js","../../frontend/node_modules/style-to-object/index.js","../../frontend/node_modules/react-markdown/lib/ast-to-react.js","../../frontend/node_modules/react-markdown/lib/react-markdown.js","../../frontend/node_modules/micromark-extension-gfm-autolink-literal/lib/syntax.js","../../frontend/node_modules/micromark-extension-gfm-footnote/lib/syntax.js","../../frontend/node_modules/micromark-extension-gfm-strikethrough/lib/syntax.js","../../frontend/node_modules/micromark-extension-gfm-table/lib/edit-map.js","../../frontend/node_modules/micromark-extension-gfm-table/lib/infer.js","../../frontend/node_modules/micromark-extension-gfm-table/lib/syntax.js","../../frontend/node_modules/micromark-extension-gfm-task-list-item/lib/syntax.js","../../frontend/node_modules/micromark-extension-gfm/index.js","../../frontend/node_modules/ccount/index.js","../../frontend/node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp/index.js","../../frontend/node_modules/mdast-util-find-and-replace/lib/index.js","../../frontend/node_modules/mdast-util-gfm-autolink-literal/lib/index.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/association.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/container-flow.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/indent-lines.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/pattern-compile.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/pattern-in-scope.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/safe.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/track.js","../../frontend/node_modules/mdast-util-gfm-footnote/lib/index.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/container-phrasing.js","../../frontend/node_modules/mdast-util-gfm-strikethrough/lib/index.js","../../frontend/node_modules/mdast-util-to-markdown/lib/handle/inline-code.js","../../frontend/node_modules/markdown-table/index.js","../../frontend/node_modules/mdast-util-gfm-table/lib/index.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/check-bullet.js","../../frontend/node_modules/mdast-util-to-markdown/lib/util/check-list-item-indent.js","../../frontend/node_modules/mdast-util-to-markdown/lib/handle/list-item.js","../../frontend/node_modules/mdast-util-gfm-task-list-item/lib/index.js","../../frontend/node_modules/mdast-util-gfm/lib/index.js","../../frontend/node_modules/remark-gfm/index.js","../../frontend/node_modules/parse5/lib/common/unicode.js","../../frontend/node_modules/parse5/lib/common/error-codes.js","../../frontend/node_modules/parse5/lib/tokenizer/preprocessor.js","../../frontend/node_modules/parse5/lib/tokenizer/named-entity-data.js","../../frontend/node_modules/parse5/lib/tokenizer/index.js","../../frontend/node_modules/parse5/lib/common/html.js","../../frontend/node_modules/parse5/lib/parser/open-element-stack.js","../../frontend/node_modules/parse5/lib/parser/formatting-element-list.js","../../frontend/node_modules/parse5/lib/utils/mixin.js","../../frontend/node_modules/parse5/lib/extensions/position-tracking/preprocessor-mixin.js","../../frontend/node_modules/parse5/lib/extensions/location-info/tokenizer-mixin.js","../../frontend/node_modules/parse5/lib/extensions/location-info/open-element-stack-mixin.js","../../frontend/node_modules/parse5/lib/extensions/location-info/parser-mixin.js","../../frontend/node_modules/parse5/lib/extensions/error-reporting/mixin-base.js","../../frontend/node_modules/parse5/lib/extensions/error-reporting/preprocessor-mixin.js","../../frontend/node_modules/parse5/lib/extensions/error-reporting/tokenizer-mixin.js","../../frontend/node_modules/parse5/lib/extensions/error-reporting/parser-mixin.js","../../frontend/node_modules/parse5/lib/tree-adapters/default.js","../../frontend/node_modules/parse5/lib/utils/merge-options.js","../../frontend/node_modules/parse5/lib/common/doctype.js","../../frontend/node_modules/parse5/lib/common/foreign-content.js","../../frontend/node_modules/parse5/lib/parser/index.js","../../frontend/node_modules/hast-util-parse-selector/lib/index.js","../../frontend/node_modules/hastscript/lib/core.js","../../frontend/node_modules/hastscript/lib/html.js","../../frontend/node_modules/hastscript/lib/svg-case-sensitive-tag-names.js","../../frontend/node_modules/hastscript/lib/svg.js","../../frontend/node_modules/vfile-location/lib/index.js","../../frontend/node_modules/web-namespaces/index.js","../../frontend/node_modules/hast-util-from-parse5/lib/index.js","../../frontend/node_modules/zwitch/index.js","../../frontend/node_modules/hast-util-to-parse5/lib/index.js","../../frontend/node_modules/html-void-elements/index.js","../../frontend/node_modules/hast-util-raw/lib/index.js","../../frontend/node_modules/rehype-raw/index.js","../../frontend/src/api/api.ts","../../frontend/node_modules/lodash-es/_freeGlobal.js","../../frontend/node_modules/lodash-es/_root.js","../../frontend/node_modules/lodash-es/_Symbol.js","../../frontend/node_modules/lodash-es/_getRawTag.js","../../frontend/node_modules/lodash-es/_objectToString.js","../../frontend/node_modules/lodash-es/_baseGetTag.js","../../frontend/node_modules/lodash-es/isObjectLike.js","../../frontend/node_modules/lodash-es/isArray.js","../../frontend/node_modules/lodash-es/isObject.js","../../frontend/node_modules/lodash-es/isFunction.js","../../frontend/node_modules/lodash-es/_coreJsData.js","../../frontend/node_modules/lodash-es/_isMasked.js","../../frontend/node_modules/lodash-es/_toSource.js","../../frontend/node_modules/lodash-es/_baseIsNative.js","../../frontend/node_modules/lodash-es/_getValue.js","../../frontend/node_modules/lodash-es/_getNative.js","../../frontend/node_modules/lodash-es/_WeakMap.js","../../frontend/node_modules/lodash-es/_baseCreate.js","../../frontend/node_modules/lodash-es/_copyArray.js","../../frontend/node_modules/lodash-es/_defineProperty.js","../../frontend/node_modules/lodash-es/_arrayEach.js","../../frontend/node_modules/lodash-es/_isIndex.js","../../frontend/node_modules/lodash-es/_baseAssignValue.js","../../frontend/node_modules/lodash-es/eq.js","../../frontend/node_modules/lodash-es/_assignValue.js","../../frontend/node_modules/lodash-es/_copyObject.js","../../frontend/node_modules/lodash-es/isLength.js","../../frontend/node_modules/lodash-es/isArrayLike.js","../../frontend/node_modules/lodash-es/_isPrototype.js","../../frontend/node_modules/lodash-es/_baseTimes.js","../../frontend/node_modules/lodash-es/_baseIsArguments.js","../../frontend/node_modules/lodash-es/isArguments.js","../../frontend/node_modules/lodash-es/stubFalse.js","../../frontend/node_modules/lodash-es/isBuffer.js","../../frontend/node_modules/lodash-es/_baseIsTypedArray.js","../../frontend/node_modules/lodash-es/_baseUnary.js","../../frontend/node_modules/lodash-es/_nodeUtil.js","../../frontend/node_modules/lodash-es/isTypedArray.js","../../frontend/node_modules/lodash-es/_arrayLikeKeys.js","../../frontend/node_modules/lodash-es/_overArg.js","../../frontend/node_modules/lodash-es/_nativeKeys.js","../../frontend/node_modules/lodash-es/_baseKeys.js","../../frontend/node_modules/lodash-es/keys.js","../../frontend/node_modules/lodash-es/_nativeKeysIn.js","../../frontend/node_modules/lodash-es/_baseKeysIn.js","../../frontend/node_modules/lodash-es/keysIn.js","../../frontend/node_modules/lodash-es/_nativeCreate.js","../../frontend/node_modules/lodash-es/_hashClear.js","../../frontend/node_modules/lodash-es/_hashDelete.js","../../frontend/node_modules/lodash-es/_hashGet.js","../../frontend/node_modules/lodash-es/_hashHas.js","../../frontend/node_modules/lodash-es/_hashSet.js","../../frontend/node_modules/lodash-es/_Hash.js","../../frontend/node_modules/lodash-es/_listCacheClear.js","../../frontend/node_modules/lodash-es/_assocIndexOf.js","../../frontend/node_modules/lodash-es/_listCacheDelete.js","../../frontend/node_modules/lodash-es/_listCacheGet.js","../../frontend/node_modules/lodash-es/_listCacheHas.js","../../frontend/node_modules/lodash-es/_listCacheSet.js","../../frontend/node_modules/lodash-es/_ListCache.js","../../frontend/node_modules/lodash-es/_Map.js","../../frontend/node_modules/lodash-es/_mapCacheClear.js","../../frontend/node_modules/lodash-es/_isKeyable.js","../../frontend/node_modules/lodash-es/_getMapData.js","../../frontend/node_modules/lodash-es/_mapCacheDelete.js","../../frontend/node_modules/lodash-es/_mapCacheGet.js","../../frontend/node_modules/lodash-es/_mapCacheHas.js","../../frontend/node_modules/lodash-es/_mapCacheSet.js","../../frontend/node_modules/lodash-es/_MapCache.js","../../frontend/node_modules/lodash-es/_arrayPush.js","../../frontend/node_modules/lodash-es/_getPrototype.js","../../frontend/node_modules/lodash-es/_stackClear.js","../../frontend/node_modules/lodash-es/_stackDelete.js","../../frontend/node_modules/lodash-es/_stackGet.js","../../frontend/node_modules/lodash-es/_stackHas.js","../../frontend/node_modules/lodash-es/_stackSet.js","../../frontend/node_modules/lodash-es/_Stack.js","../../frontend/node_modules/lodash-es/_baseAssign.js","../../frontend/node_modules/lodash-es/_baseAssignIn.js","../../frontend/node_modules/lodash-es/_cloneBuffer.js","../../frontend/node_modules/lodash-es/_arrayFilter.js","../../frontend/node_modules/lodash-es/stubArray.js","../../frontend/node_modules/lodash-es/_getSymbols.js","../../frontend/node_modules/lodash-es/_copySymbols.js","../../frontend/node_modules/lodash-es/_getSymbolsIn.js","../../frontend/node_modules/lodash-es/_copySymbolsIn.js","../../frontend/node_modules/lodash-es/_baseGetAllKeys.js","../../frontend/node_modules/lodash-es/_getAllKeys.js","../../frontend/node_modules/lodash-es/_getAllKeysIn.js","../../frontend/node_modules/lodash-es/_DataView.js","../../frontend/node_modules/lodash-es/_Promise.js","../../frontend/node_modules/lodash-es/_Set.js","../../frontend/node_modules/lodash-es/_getTag.js","../../frontend/node_modules/lodash-es/_initCloneArray.js","../../frontend/node_modules/lodash-es/_Uint8Array.js","../../frontend/node_modules/lodash-es/_cloneArrayBuffer.js","../../frontend/node_modules/lodash-es/_cloneDataView.js","../../frontend/node_modules/lodash-es/_cloneRegExp.js","../../frontend/node_modules/lodash-es/_cloneSymbol.js","../../frontend/node_modules/lodash-es/_cloneTypedArray.js","../../frontend/node_modules/lodash-es/_initCloneByTag.js","../../frontend/node_modules/lodash-es/_initCloneObject.js","../../frontend/node_modules/lodash-es/_baseIsMap.js","../../frontend/node_modules/lodash-es/isMap.js","../../frontend/node_modules/lodash-es/_baseIsSet.js","../../frontend/node_modules/lodash-es/isSet.js","../../frontend/node_modules/lodash-es/_baseClone.js","../../frontend/node_modules/lodash-es/cloneDeep.js","../../frontend/src/components/Answer/AnswerParser.tsx","../../frontend/node_modules/remark-supersub/lib/index.js","../../frontend/src/components/Answer/Answer.tsx","../../frontend/src/assets/Send.svg","../../frontend/src/assets/mic-outline.svg","../../frontend/node_modules/@fortawesome/fontawesome-svg-core/index.mjs","../../frontend/node_modules/@fortawesome/react-fontawesome/index.es.js","../../frontend/node_modules/@fortawesome/free-solid-svg-icons/index.mjs","../../frontend/src/components/QuestionInput/QuestionInput.tsx","../../frontend/src/pages/chat/Chat.tsx","../../frontend/src/index.tsx"],"sourcesContent":["/**\n * @license React\n * react.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n'use strict';var l=Symbol.for(\"react.element\"),n=Symbol.for(\"react.portal\"),p=Symbol.for(\"react.fragment\"),q=Symbol.for(\"react.strict_mode\"),r=Symbol.for(\"react.profiler\"),t=Symbol.for(\"react.provider\"),u=Symbol.for(\"react.context\"),v=Symbol.for(\"react.forward_ref\"),w=Symbol.for(\"react.suspense\"),x=Symbol.for(\"react.memo\"),y=Symbol.for(\"react.lazy\"),z=Symbol.iterator;function A(a){if(null===a||\"object\"!==typeof a)return null;a=z&&a[z]||a[\"@@iterator\"];return\"function\"===typeof a?a:null}\nvar B={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},C=Object.assign,D={};function E(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B}E.prototype.isReactComponent={};\nE.prototype.setState=function(a,b){if(\"object\"!==typeof a&&\"function\"!==typeof a&&null!=a)throw Error(\"setState(...): takes an object of state variables to update or a function which returns an object of state variables.\");this.updater.enqueueSetState(this,a,b,\"setState\")};E.prototype.forceUpdate=function(a){this.updater.enqueueForceUpdate(this,a,\"forceUpdate\")};function F(){}F.prototype=E.prototype;function G(a,b,e){this.props=a;this.context=b;this.refs=D;this.updater=e||B}var H=G.prototype=new F;\nH.constructor=G;C(H,E.prototype);H.isPureReactComponent=!0;var I=Array.isArray,J=Object.prototype.hasOwnProperty,K={current:null},L={key:!0,ref:!0,__self:!0,__source:!0};\nfunction M(a,b,e){var d,c={},k=null,h=null;if(null!=b)for(d in void 0!==b.ref&&(h=b.ref),void 0!==b.key&&(k=\"\"+b.key),b)J.call(b,d)&&!L.hasOwnProperty(d)&&(c[d]=b[d]);var g=arguments.length-2;if(1===g)c.children=e;else if(1>>1,e=a[d];if(0>>1;dg(C,c))ng(x,C)?(a[d]=x,a[n]=c,d=n):(a[d]=C,a[m]=c,d=m);else if(ng(x,c))a[d]=x,a[n]=c,d=n;else break a}}return b}\nfunction g(a,b){var c=a.sortIndex-b.sortIndex;return 0!==c?c:a.id-b.id}if(\"object\"===typeof performance&&\"function\"===typeof performance.now){var l=performance;exports.unstable_now=function(){return l.now()}}else{var p=Date,q=p.now();exports.unstable_now=function(){return p.now()-q}}var r=[],t=[],u=1,v=null,y=3,z=!1,A=!1,B=!1,D=\"function\"===typeof setTimeout?setTimeout:null,E=\"function\"===typeof clearTimeout?clearTimeout:null,F=\"undefined\"!==typeof setImmediate?setImmediate:null;\n\"undefined\"!==typeof navigator&&void 0!==navigator.scheduling&&void 0!==navigator.scheduling.isInputPending&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function G(a){for(var b=h(t);null!==b;){if(null===b.callback)k(t);else if(b.startTime<=a)k(t),b.sortIndex=b.expirationTime,f(r,b);else break;b=h(t)}}function H(a){B=!1;G(a);if(!A)if(null!==h(r))A=!0,I(J);else{var b=h(t);null!==b&&K(H,b.startTime-a)}}\nfunction J(a,b){A=!1;B&&(B=!1,E(L),L=-1);z=!0;var c=y;try{G(b);for(v=h(r);null!==v&&(!(v.expirationTime>b)||a&&!M());){var d=v.callback;if(\"function\"===typeof d){v.callback=null;y=v.priorityLevel;var e=d(v.expirationTime<=b);b=exports.unstable_now();\"function\"===typeof e?v.callback=e:v===h(r)&&k(r);G(b)}else k(r);v=h(r)}if(null!==v)var w=!0;else{var m=h(t);null!==m&&K(H,m.startTime-b);w=!1}return w}finally{v=null,y=c,z=!1}}var N=!1,O=null,L=-1,P=5,Q=-1;\nfunction M(){return exports.unstable_now()-Qa||125d?(a.sortIndex=c,f(t,a),null===h(r)&&a===h(t)&&(B?(E(L),L=-1):B=!0,K(H,c-d))):(a.sortIndex=e,f(r,a),A||z||(A=!0,I(J)));return a};\nexports.unstable_shouldYield=M;exports.unstable_wrapCallback=function(a){var b=y;return function(){var c=y;y=b;try{return a.apply(this,arguments)}finally{y=c}}};\n","'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/scheduler.production.min.js');\n} else {\n module.exports = require('./cjs/scheduler.development.js');\n}\n","/**\n * @license React\n * react-dom.production.min.js\n *\n * Copyright (c) Facebook, Inc. and its affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n/*\n Modernizr 3.0.0pre (Custom Build) | MIT\n*/\n'use strict';var aa=require(\"react\"),ca=require(\"scheduler\");function p(a){for(var b=\"https://reactjs.org/docs/error-decoder.html?invariant=\"+a,c=1;cb}return!1}function v(a,b,c,d,e,f,g){this.acceptsBooleans=2===b||3===b||4===b;this.attributeName=d;this.attributeNamespace=e;this.mustUseProperty=c;this.propertyName=a;this.type=b;this.sanitizeURL=f;this.removeEmptyString=g}var z={};\n\"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style\".split(\" \").forEach(function(a){z[a]=new v(a,0,!1,a,null,!1,!1)});[[\"acceptCharset\",\"accept-charset\"],[\"className\",\"class\"],[\"htmlFor\",\"for\"],[\"httpEquiv\",\"http-equiv\"]].forEach(function(a){var b=a[0];z[b]=new v(b,1,!1,a[1],null,!1,!1)});[\"contentEditable\",\"draggable\",\"spellCheck\",\"value\"].forEach(function(a){z[a]=new v(a,2,!1,a.toLowerCase(),null,!1,!1)});\n[\"autoReverse\",\"externalResourcesRequired\",\"focusable\",\"preserveAlpha\"].forEach(function(a){z[a]=new v(a,2,!1,a,null,!1,!1)});\"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope\".split(\" \").forEach(function(a){z[a]=new v(a,3,!1,a.toLowerCase(),null,!1,!1)});\n[\"checked\",\"multiple\",\"muted\",\"selected\"].forEach(function(a){z[a]=new v(a,3,!0,a,null,!1,!1)});[\"capture\",\"download\"].forEach(function(a){z[a]=new v(a,4,!1,a,null,!1,!1)});[\"cols\",\"rows\",\"size\",\"span\"].forEach(function(a){z[a]=new v(a,6,!1,a,null,!1,!1)});[\"rowSpan\",\"start\"].forEach(function(a){z[a]=new v(a,5,!1,a.toLowerCase(),null,!1,!1)});var ra=/[\\-:]([a-z])/g;function sa(a){return a[1].toUpperCase()}\n\"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height\".split(\" \").forEach(function(a){var b=a.replace(ra,\nsa);z[b]=new v(b,1,!1,a,null,!1,!1)});\"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type\".split(\" \").forEach(function(a){var b=a.replace(ra,sa);z[b]=new v(b,1,!1,a,\"http://www.w3.org/1999/xlink\",!1,!1)});[\"xml:base\",\"xml:lang\",\"xml:space\"].forEach(function(a){var b=a.replace(ra,sa);z[b]=new v(b,1,!1,a,\"http://www.w3.org/XML/1998/namespace\",!1,!1)});[\"tabIndex\",\"crossOrigin\"].forEach(function(a){z[a]=new v(a,1,!1,a.toLowerCase(),null,!1,!1)});\nz.xlinkHref=new v(\"xlinkHref\",1,!1,\"xlink:href\",\"http://www.w3.org/1999/xlink\",!0,!1);[\"src\",\"href\",\"action\",\"formAction\"].forEach(function(a){z[a]=new v(a,1,!1,a.toLowerCase(),null,!0,!0)});\nfunction ta(a,b,c,d){var e=z.hasOwnProperty(b)?z[b]:null;if(null!==e?0!==e.type:d||!(2h||e[g]!==f[h]){var k=\"\\n\"+e[g].replace(\" at new \",\" at \");a.displayName&&k.includes(\"\")&&(k=k.replace(\"\",a.displayName));return k}while(1<=g&&0<=h)}break}}}finally{Na=!1,Error.prepareStackTrace=c}return(a=a?a.displayName||a.name:\"\")?Ma(a):\"\"}\nfunction Pa(a){switch(a.tag){case 5:return Ma(a.type);case 16:return Ma(\"Lazy\");case 13:return Ma(\"Suspense\");case 19:return Ma(\"SuspenseList\");case 0:case 2:case 15:return a=Oa(a.type,!1),a;case 11:return a=Oa(a.type.render,!1),a;case 1:return a=Oa(a.type,!0),a;default:return\"\"}}\nfunction Qa(a){if(null==a)return null;if(\"function\"===typeof a)return a.displayName||a.name||null;if(\"string\"===typeof a)return a;switch(a){case ya:return\"Fragment\";case wa:return\"Portal\";case Aa:return\"Profiler\";case za:return\"StrictMode\";case Ea:return\"Suspense\";case Fa:return\"SuspenseList\"}if(\"object\"===typeof a)switch(a.$$typeof){case Ca:return(a.displayName||\"Context\")+\".Consumer\";case Ba:return(a._context.displayName||\"Context\")+\".Provider\";case Da:var b=a.render;a=a.displayName;a||(a=b.displayName||\nb.name||\"\",a=\"\"!==a?\"ForwardRef(\"+a+\")\":\"ForwardRef\");return a;case Ga:return b=a.displayName||null,null!==b?b:Qa(a.type)||\"Memo\";case Ha:b=a._payload;a=a._init;try{return Qa(a(b))}catch(c){}}return null}\nfunction Ra(a){var b=a.type;switch(a.tag){case 24:return\"Cache\";case 9:return(b.displayName||\"Context\")+\".Consumer\";case 10:return(b._context.displayName||\"Context\")+\".Provider\";case 18:return\"DehydratedFragment\";case 11:return a=b.render,a=a.displayName||a.name||\"\",b.displayName||(\"\"!==a?\"ForwardRef(\"+a+\")\":\"ForwardRef\");case 7:return\"Fragment\";case 5:return b;case 4:return\"Portal\";case 3:return\"Root\";case 6:return\"Text\";case 16:return Qa(b);case 8:return b===za?\"StrictMode\":\"Mode\";case 22:return\"Offscreen\";\ncase 12:return\"Profiler\";case 21:return\"Scope\";case 13:return\"Suspense\";case 19:return\"SuspenseList\";case 25:return\"TracingMarker\";case 1:case 0:case 17:case 2:case 14:case 15:if(\"function\"===typeof b)return b.displayName||b.name||null;if(\"string\"===typeof b)return b}return null}function Sa(a){switch(typeof a){case \"boolean\":case \"number\":case \"string\":case \"undefined\":return a;case \"object\":return a;default:return\"\"}}\nfunction Ta(a){var b=a.type;return(a=a.nodeName)&&\"input\"===a.toLowerCase()&&(\"checkbox\"===b||\"radio\"===b)}\nfunction Ua(a){var b=Ta(a)?\"checked\":\"value\",c=Object.getOwnPropertyDescriptor(a.constructor.prototype,b),d=\"\"+a[b];if(!a.hasOwnProperty(b)&&\"undefined\"!==typeof c&&\"function\"===typeof c.get&&\"function\"===typeof c.set){var e=c.get,f=c.set;Object.defineProperty(a,b,{configurable:!0,get:function(){return e.call(this)},set:function(a){d=\"\"+a;f.call(this,a)}});Object.defineProperty(a,b,{enumerable:c.enumerable});return{getValue:function(){return d},setValue:function(a){d=\"\"+a},stopTracking:function(){a._valueTracker=\nnull;delete a[b]}}}}function Va(a){a._valueTracker||(a._valueTracker=Ua(a))}function Wa(a){if(!a)return!1;var b=a._valueTracker;if(!b)return!0;var c=b.getValue();var d=\"\";a&&(d=Ta(a)?a.checked?\"true\":\"false\":a.value);a=d;return a!==c?(b.setValue(a),!0):!1}function Xa(a){a=a||(\"undefined\"!==typeof document?document:void 0);if(\"undefined\"===typeof a)return null;try{return a.activeElement||a.body}catch(b){return a.body}}\nfunction Ya(a,b){var c=b.checked;return A({},b,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:null!=c?c:a._wrapperState.initialChecked})}function Za(a,b){var c=null==b.defaultValue?\"\":b.defaultValue,d=null!=b.checked?b.checked:b.defaultChecked;c=Sa(null!=b.value?b.value:c);a._wrapperState={initialChecked:d,initialValue:c,controlled:\"checkbox\"===b.type||\"radio\"===b.type?null!=b.checked:null!=b.value}}function ab(a,b){b=b.checked;null!=b&&ta(a,\"checked\",b,!1)}\nfunction bb(a,b){ab(a,b);var c=Sa(b.value),d=b.type;if(null!=c)if(\"number\"===d){if(0===c&&\"\"===a.value||a.value!=c)a.value=\"\"+c}else a.value!==\"\"+c&&(a.value=\"\"+c);else if(\"submit\"===d||\"reset\"===d){a.removeAttribute(\"value\");return}b.hasOwnProperty(\"value\")?cb(a,b.type,c):b.hasOwnProperty(\"defaultValue\")&&cb(a,b.type,Sa(b.defaultValue));null==b.checked&&null!=b.defaultChecked&&(a.defaultChecked=!!b.defaultChecked)}\nfunction db(a,b,c){if(b.hasOwnProperty(\"value\")||b.hasOwnProperty(\"defaultValue\")){var d=b.type;if(!(\"submit\"!==d&&\"reset\"!==d||void 0!==b.value&&null!==b.value))return;b=\"\"+a._wrapperState.initialValue;c||b===a.value||(a.value=b);a.defaultValue=b}c=a.name;\"\"!==c&&(a.name=\"\");a.defaultChecked=!!a._wrapperState.initialChecked;\"\"!==c&&(a.name=c)}\nfunction cb(a,b,c){if(\"number\"!==b||Xa(a.ownerDocument)!==a)null==c?a.defaultValue=\"\"+a._wrapperState.initialValue:a.defaultValue!==\"\"+c&&(a.defaultValue=\"\"+c)}var eb=Array.isArray;\nfunction fb(a,b,c,d){a=a.options;if(b){b={};for(var e=0;e\"+b.valueOf().toString()+\"\";for(b=mb.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;b.firstChild;)a.appendChild(b.firstChild)}});\nfunction ob(a,b){if(b){var c=a.firstChild;if(c&&c===a.lastChild&&3===c.nodeType){c.nodeValue=b;return}}a.textContent=b}\nvar pb={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,\nzoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},qb=[\"Webkit\",\"ms\",\"Moz\",\"O\"];Object.keys(pb).forEach(function(a){qb.forEach(function(b){b=b+a.charAt(0).toUpperCase()+a.substring(1);pb[b]=pb[a]})});function rb(a,b,c){return null==b||\"boolean\"===typeof b||\"\"===b?\"\":c||\"number\"!==typeof b||0===b||pb.hasOwnProperty(a)&&pb[a]?(\"\"+b).trim():b+\"px\"}\nfunction sb(a,b){a=a.style;for(var c in b)if(b.hasOwnProperty(c)){var d=0===c.indexOf(\"--\"),e=rb(c,b[c],d);\"float\"===c&&(c=\"cssFloat\");d?a.setProperty(c,e):a[c]=e}}var tb=A({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});\nfunction ub(a,b){if(b){if(tb[a]&&(null!=b.children||null!=b.dangerouslySetInnerHTML))throw Error(p(137,a));if(null!=b.dangerouslySetInnerHTML){if(null!=b.children)throw Error(p(60));if(\"object\"!==typeof b.dangerouslySetInnerHTML||!(\"__html\"in b.dangerouslySetInnerHTML))throw Error(p(61));}if(null!=b.style&&\"object\"!==typeof b.style)throw Error(p(62));}}\nfunction vb(a,b){if(-1===a.indexOf(\"-\"))return\"string\"===typeof b.is;switch(a){case \"annotation-xml\":case \"color-profile\":case \"font-face\":case \"font-face-src\":case \"font-face-uri\":case \"font-face-format\":case \"font-face-name\":case \"missing-glyph\":return!1;default:return!0}}var wb=null;function xb(a){a=a.target||a.srcElement||window;a.correspondingUseElement&&(a=a.correspondingUseElement);return 3===a.nodeType?a.parentNode:a}var yb=null,zb=null,Ab=null;\nfunction Bb(a){if(a=Cb(a)){if(\"function\"!==typeof yb)throw Error(p(280));var b=a.stateNode;b&&(b=Db(b),yb(a.stateNode,a.type,b))}}function Eb(a){zb?Ab?Ab.push(a):Ab=[a]:zb=a}function Fb(){if(zb){var a=zb,b=Ab;Ab=zb=null;Bb(a);if(b)for(a=0;a>>=0;return 0===a?32:31-(pc(a)/qc|0)|0}var rc=64,sc=4194304;\nfunction tc(a){switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return a&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return a&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;\ndefault:return a}}function uc(a,b){var c=a.pendingLanes;if(0===c)return 0;var d=0,e=a.suspendedLanes,f=a.pingedLanes,g=c&268435455;if(0!==g){var h=g&~e;0!==h?d=tc(h):(f&=g,0!==f&&(d=tc(f)))}else g=c&~e,0!==g?d=tc(g):0!==f&&(d=tc(f));if(0===d)return 0;if(0!==b&&b!==d&&0===(b&e)&&(e=d&-d,f=b&-b,e>=f||16===e&&0!==(f&4194240)))return b;0!==(d&4)&&(d|=c&16);b=a.entangledLanes;if(0!==b)for(a=a.entanglements,b&=d;0c;c++)b.push(a);return b}\nfunction Ac(a,b,c){a.pendingLanes|=b;536870912!==b&&(a.suspendedLanes=0,a.pingedLanes=0);a=a.eventTimes;b=31-oc(b);a[b]=c}function Bc(a,b){var c=a.pendingLanes&~b;a.pendingLanes=b;a.suspendedLanes=0;a.pingedLanes=0;a.expiredLanes&=b;a.mutableReadLanes&=b;a.entangledLanes&=b;b=a.entanglements;var d=a.eventTimes;for(a=a.expirationTimes;0=be),ee=String.fromCharCode(32),fe=!1;\nfunction ge(a,b){switch(a){case \"keyup\":return-1!==$d.indexOf(b.keyCode);case \"keydown\":return 229!==b.keyCode;case \"keypress\":case \"mousedown\":case \"focusout\":return!0;default:return!1}}function he(a){a=a.detail;return\"object\"===typeof a&&\"data\"in a?a.data:null}var ie=!1;function je(a,b){switch(a){case \"compositionend\":return he(b);case \"keypress\":if(32!==b.which)return null;fe=!0;return ee;case \"textInput\":return a=b.data,a===ee&&fe?null:a;default:return null}}\nfunction ke(a,b){if(ie)return\"compositionend\"===a||!ae&&ge(a,b)?(a=nd(),md=ld=kd=null,ie=!1,a):null;switch(a){case \"paste\":return null;case \"keypress\":if(!(b.ctrlKey||b.altKey||b.metaKey)||b.ctrlKey&&b.altKey){if(b.char&&1=b)return{node:c,offset:b-a};a=d}a:{for(;c;){if(c.nextSibling){c=c.nextSibling;break a}c=c.parentNode}c=void 0}c=Je(c)}}function Le(a,b){return a&&b?a===b?!0:a&&3===a.nodeType?!1:b&&3===b.nodeType?Le(a,b.parentNode):\"contains\"in a?a.contains(b):a.compareDocumentPosition?!!(a.compareDocumentPosition(b)&16):!1:!1}\nfunction Me(){for(var a=window,b=Xa();b instanceof a.HTMLIFrameElement;){try{var c=\"string\"===typeof b.contentWindow.location.href}catch(d){c=!1}if(c)a=b.contentWindow;else break;b=Xa(a.document)}return b}function Ne(a){var b=a&&a.nodeName&&a.nodeName.toLowerCase();return b&&(\"input\"===b&&(\"text\"===a.type||\"search\"===a.type||\"tel\"===a.type||\"url\"===a.type||\"password\"===a.type)||\"textarea\"===b||\"true\"===a.contentEditable)}\nfunction Oe(a){var b=Me(),c=a.focusedElem,d=a.selectionRange;if(b!==c&&c&&c.ownerDocument&&Le(c.ownerDocument.documentElement,c)){if(null!==d&&Ne(c))if(b=d.start,a=d.end,void 0===a&&(a=b),\"selectionStart\"in c)c.selectionStart=b,c.selectionEnd=Math.min(a,c.value.length);else if(a=(b=c.ownerDocument||document)&&b.defaultView||window,a.getSelection){a=a.getSelection();var e=c.textContent.length,f=Math.min(d.start,e);d=void 0===d.end?f:Math.min(d.end,e);!a.extend&&f>d&&(e=d,d=f,f=e);e=Ke(c,f);var g=Ke(c,\nd);e&&g&&(1!==a.rangeCount||a.anchorNode!==e.node||a.anchorOffset!==e.offset||a.focusNode!==g.node||a.focusOffset!==g.offset)&&(b=b.createRange(),b.setStart(e.node,e.offset),a.removeAllRanges(),f>d?(a.addRange(b),a.extend(g.node,g.offset)):(b.setEnd(g.node,g.offset),a.addRange(b)))}b=[];for(a=c;a=a.parentNode;)1===a.nodeType&&b.push({element:a,left:a.scrollLeft,top:a.scrollTop});\"function\"===typeof c.focus&&c.focus();for(c=0;c=document.documentMode,Qe=null,Re=null,Se=null,Te=!1;\nfunction Ue(a,b,c){var d=c.window===c?c.document:9===c.nodeType?c:c.ownerDocument;Te||null==Qe||Qe!==Xa(d)||(d=Qe,\"selectionStart\"in d&&Ne(d)?d={start:d.selectionStart,end:d.selectionEnd}:(d=(d.ownerDocument&&d.ownerDocument.defaultView||window).getSelection(),d={anchorNode:d.anchorNode,anchorOffset:d.anchorOffset,focusNode:d.focusNode,focusOffset:d.focusOffset}),Se&&Ie(Se,d)||(Se=d,d=oe(Re,\"onSelect\"),0Tf||(a.current=Sf[Tf],Sf[Tf]=null,Tf--)}function G(a,b){Tf++;Sf[Tf]=a.current;a.current=b}var Vf={},H=Uf(Vf),Wf=Uf(!1),Xf=Vf;function Yf(a,b){var c=a.type.contextTypes;if(!c)return Vf;var d=a.stateNode;if(d&&d.__reactInternalMemoizedUnmaskedChildContext===b)return d.__reactInternalMemoizedMaskedChildContext;var e={},f;for(f in c)e[f]=b[f];d&&(a=a.stateNode,a.__reactInternalMemoizedUnmaskedChildContext=b,a.__reactInternalMemoizedMaskedChildContext=e);return e}\nfunction Zf(a){a=a.childContextTypes;return null!==a&&void 0!==a}function $f(){E(Wf);E(H)}function ag(a,b,c){if(H.current!==Vf)throw Error(p(168));G(H,b);G(Wf,c)}function bg(a,b,c){var d=a.stateNode;b=b.childContextTypes;if(\"function\"!==typeof d.getChildContext)return c;d=d.getChildContext();for(var e in d)if(!(e in b))throw Error(p(108,Ra(a)||\"Unknown\",e));return A({},c,d)}\nfunction cg(a){a=(a=a.stateNode)&&a.__reactInternalMemoizedMergedChildContext||Vf;Xf=H.current;G(H,a);G(Wf,Wf.current);return!0}function dg(a,b,c){var d=a.stateNode;if(!d)throw Error(p(169));c?(a=bg(a,b,Xf),d.__reactInternalMemoizedMergedChildContext=a,E(Wf),E(H),G(H,a)):E(Wf);G(Wf,c)}var eg=null,fg=!1,gg=!1;function hg(a){null===eg?eg=[a]:eg.push(a)}function ig(a){fg=!0;hg(a)}\nfunction jg(){if(!gg&&null!==eg){gg=!0;var a=0,b=C;try{var c=eg;for(C=1;a>=g;e-=g;rg=1<<32-oc(b)+e|c<w?(x=u,u=null):x=u.sibling;var n=r(e,u,h[w],k);if(null===n){null===u&&(u=x);break}a&&u&&null===n.alternate&&b(e,u);g=f(n,g,w);null===m?l=n:m.sibling=n;m=n;u=x}if(w===h.length)return c(e,u),I&&tg(e,w),l;if(null===u){for(;ww?(x=m,m=null):x=m.sibling;var t=r(e,m,n.value,k);if(null===t){null===m&&(m=x);break}a&&m&&null===t.alternate&&b(e,m);g=f(t,g,w);null===u?l=t:u.sibling=t;u=t;m=x}if(n.done)return c(e,\nm),I&&tg(e,w),l;if(null===m){for(;!n.done;w++,n=h.next())n=q(e,n.value,k),null!==n&&(g=f(n,g,w),null===u?l=n:u.sibling=n,u=n);I&&tg(e,w);return l}for(m=d(e,m);!n.done;w++,n=h.next())n=y(m,e,w,n.value,k),null!==n&&(a&&null!==n.alternate&&m.delete(null===n.key?w:n.key),g=f(n,g,w),null===u?l=n:u.sibling=n,u=n);a&&m.forEach(function(a){return b(e,a)});I&&tg(e,w);return l}function J(a,d,f,h){\"object\"===typeof f&&null!==f&&f.type===ya&&null===f.key&&(f=f.props.children);if(\"object\"===typeof f&&null!==f){switch(f.$$typeof){case va:a:{for(var k=\nf.key,l=d;null!==l;){if(l.key===k){k=f.type;if(k===ya){if(7===l.tag){c(a,l.sibling);d=e(l,f.props.children);d.return=a;a=d;break a}}else if(l.elementType===k||\"object\"===typeof k&&null!==k&&k.$$typeof===Ha&&uh(k)===l.type){c(a,l.sibling);d=e(l,f.props);d.ref=sh(a,l,f);d.return=a;a=d;break a}c(a,l);break}else b(a,l);l=l.sibling}f.type===ya?(d=Ah(f.props.children,a.mode,h,f.key),d.return=a,a=d):(h=yh(f.type,f.key,f.props,null,a.mode,h),h.ref=sh(a,d,f),h.return=a,a=h)}return g(a);case wa:a:{for(l=f.key;null!==\nd;){if(d.key===l)if(4===d.tag&&d.stateNode.containerInfo===f.containerInfo&&d.stateNode.implementation===f.implementation){c(a,d.sibling);d=e(d,f.children||[]);d.return=a;a=d;break a}else{c(a,d);break}else b(a,d);d=d.sibling}d=zh(f,a.mode,h);d.return=a;a=d}return g(a);case Ha:return l=f._init,J(a,d,l(f._payload),h)}if(eb(f))return n(a,d,f,h);if(Ka(f))return t(a,d,f,h);th(a,f)}return\"string\"===typeof f&&\"\"!==f||\"number\"===typeof f?(f=\"\"+f,null!==d&&6===d.tag?(c(a,d.sibling),d=e(d,f),d.return=a,a=d):\n(c(a,d),d=xh(f,a.mode,h),d.return=a,a=d),g(a)):c(a,d)}return J}var Bh=vh(!0),Ch=vh(!1),Dh={},Eh=Uf(Dh),Fh=Uf(Dh),Gh=Uf(Dh);function Hh(a){if(a===Dh)throw Error(p(174));return a}function Ih(a,b){G(Gh,b);G(Fh,a);G(Eh,Dh);a=b.nodeType;switch(a){case 9:case 11:b=(b=b.documentElement)?b.namespaceURI:lb(null,\"\");break;default:a=8===a?b.parentNode:b,b=a.namespaceURI||null,a=a.tagName,b=lb(b,a)}E(Eh);G(Eh,b)}function Jh(){E(Eh);E(Fh);E(Gh)}\nfunction Kh(a){Hh(Gh.current);var b=Hh(Eh.current);var c=lb(b,a.type);b!==c&&(G(Fh,a),G(Eh,c))}function Lh(a){Fh.current===a&&(E(Eh),E(Fh))}var M=Uf(0);\nfunction Mh(a){for(var b=a;null!==b;){if(13===b.tag){var c=b.memoizedState;if(null!==c&&(c=c.dehydrated,null===c||\"$?\"===c.data||\"$!\"===c.data))return b}else if(19===b.tag&&void 0!==b.memoizedProps.revealOrder){if(0!==(b.flags&128))return b}else if(null!==b.child){b.child.return=b;b=b.child;continue}if(b===a)break;for(;null===b.sibling;){if(null===b.return||b.return===a)return null;b=b.return}b.sibling.return=b.return;b=b.sibling}return null}var Nh=[];\nfunction Oh(){for(var a=0;ac?c:4;a(!0);var d=Qh.transition;Qh.transition={};try{a(!1),b()}finally{C=c,Qh.transition=d}}function Fi(){return di().memoizedState}\nfunction Gi(a,b,c){var d=lh(a);c={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(Hi(a))Ii(b,c);else if(c=Yg(a,b,c,d),null!==c){var e=L();mh(c,a,d,e);Ji(c,b,d)}}\nfunction ri(a,b,c){var d=lh(a),e={lane:d,action:c,hasEagerState:!1,eagerState:null,next:null};if(Hi(a))Ii(b,e);else{var f=a.alternate;if(0===a.lanes&&(null===f||0===f.lanes)&&(f=b.lastRenderedReducer,null!==f))try{var g=b.lastRenderedState,h=f(g,c);e.hasEagerState=!0;e.eagerState=h;if(He(h,g)){var k=b.interleaved;null===k?(e.next=e,Xg(b)):(e.next=k.next,k.next=e);b.interleaved=e;return}}catch(l){}finally{}c=Yg(a,b,e,d);null!==c&&(e=L(),mh(c,a,d,e),Ji(c,b,d))}}\nfunction Hi(a){var b=a.alternate;return a===N||null!==b&&b===N}function Ii(a,b){Th=Sh=!0;var c=a.pending;null===c?b.next=b:(b.next=c.next,c.next=b);a.pending=b}function Ji(a,b,c){if(0!==(c&4194240)){var d=b.lanes;d&=a.pendingLanes;c|=d;b.lanes=c;Cc(a,c)}}\nvar ai={readContext:Vg,useCallback:Q,useContext:Q,useEffect:Q,useImperativeHandle:Q,useInsertionEffect:Q,useLayoutEffect:Q,useMemo:Q,useReducer:Q,useRef:Q,useState:Q,useDebugValue:Q,useDeferredValue:Q,useTransition:Q,useMutableSource:Q,useSyncExternalStore:Q,useId:Q,unstable_isNewReconciler:!1},Yh={readContext:Vg,useCallback:function(a,b){ci().memoizedState=[a,void 0===b?null:b];return a},useContext:Vg,useEffect:vi,useImperativeHandle:function(a,b,c){c=null!==c&&void 0!==c?c.concat([a]):null;return ti(4194308,\n4,yi.bind(null,b,a),c)},useLayoutEffect:function(a,b){return ti(4194308,4,a,b)},useInsertionEffect:function(a,b){return ti(4,2,a,b)},useMemo:function(a,b){var c=ci();b=void 0===b?null:b;a=a();c.memoizedState=[a,b];return a},useReducer:function(a,b,c){var d=ci();b=void 0!==c?c(b):b;d.memoizedState=d.baseState=b;a={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:b};d.queue=a;a=a.dispatch=Gi.bind(null,N,a);return[d.memoizedState,a]},useRef:function(a){var b=\nci();a={current:a};return b.memoizedState=a},useState:qi,useDebugValue:Ai,useDeferredValue:function(a){return ci().memoizedState=a},useTransition:function(){var a=qi(!1),b=a[0];a=Ei.bind(null,a[1]);ci().memoizedState=a;return[b,a]},useMutableSource:function(){},useSyncExternalStore:function(a,b,c){var d=N,e=ci();if(I){if(void 0===c)throw Error(p(407));c=c()}else{c=b();if(null===R)throw Error(p(349));0!==(Rh&30)||ni(d,b,c)}e.memoizedState=c;var f={value:c,getSnapshot:b};e.queue=f;vi(ki.bind(null,d,\nf,a),[a]);d.flags|=2048;li(9,mi.bind(null,d,f,c,b),void 0,null);return c},useId:function(){var a=ci(),b=R.identifierPrefix;if(I){var c=sg;var d=rg;c=(d&~(1<<32-oc(d)-1)).toString(32)+c;b=\":\"+b+\"R\"+c;c=Uh++;0\\x3c/script>\",a=a.removeChild(a.firstChild)):\n\"string\"===typeof d.is?a=g.createElement(c,{is:d.is}):(a=g.createElement(c),\"select\"===c&&(g=a,d.multiple?g.multiple=!0:d.size&&(g.size=d.size))):a=g.createElementNS(a,c);a[Of]=b;a[Pf]=d;Aj(a,b,!1,!1);b.stateNode=a;a:{g=vb(c,d);switch(c){case \"dialog\":D(\"cancel\",a);D(\"close\",a);e=d;break;case \"iframe\":case \"object\":case \"embed\":D(\"load\",a);e=d;break;case \"video\":case \"audio\":for(e=0;eHj&&(b.flags|=128,d=!0,Ej(f,!1),b.lanes=4194304)}else{if(!d)if(a=Mh(g),null!==a){if(b.flags|=128,d=!0,c=a.updateQueue,null!==c&&(b.updateQueue=c,b.flags|=4),Ej(f,!0),null===f.tail&&\"hidden\"===f.tailMode&&!g.alternate&&!I)return S(b),null}else 2*B()-f.renderingStartTime>Hj&&1073741824!==c&&(b.flags|=128,d=!0,Ej(f,!1),b.lanes=4194304);f.isBackwards?(g.sibling=b.child,b.child=g):(c=f.last,null!==c?c.sibling=g:b.child=g,f.last=g)}if(null!==f.tail)return b=f.tail,f.rendering=\nb,f.tail=b.sibling,f.renderingStartTime=B(),b.sibling=null,c=M.current,G(M,d?c&1|2:c&1),b;S(b);return null;case 22:case 23:return Ij(),d=null!==b.memoizedState,null!==a&&null!==a.memoizedState!==d&&(b.flags|=8192),d&&0!==(b.mode&1)?0!==(gj&1073741824)&&(S(b),b.subtreeFlags&6&&(b.flags|=8192)):S(b),null;case 24:return null;case 25:return null}throw Error(p(156,b.tag));}\nfunction Jj(a,b){wg(b);switch(b.tag){case 1:return Zf(b.type)&&$f(),a=b.flags,a&65536?(b.flags=a&-65537|128,b):null;case 3:return Jh(),E(Wf),E(H),Oh(),a=b.flags,0!==(a&65536)&&0===(a&128)?(b.flags=a&-65537|128,b):null;case 5:return Lh(b),null;case 13:E(M);a=b.memoizedState;if(null!==a&&null!==a.dehydrated){if(null===b.alternate)throw Error(p(340));Ig()}a=b.flags;return a&65536?(b.flags=a&-65537|128,b):null;case 19:return E(M),null;case 4:return Jh(),null;case 10:return Rg(b.type._context),null;case 22:case 23:return Ij(),\nnull;case 24:return null;default:return null}}var Kj=!1,U=!1,Lj=\"function\"===typeof WeakSet?WeakSet:Set,V=null;function Mj(a,b){var c=a.ref;if(null!==c)if(\"function\"===typeof c)try{c(null)}catch(d){W(a,b,d)}else c.current=null}function Nj(a,b,c){try{c()}catch(d){W(a,b,d)}}var Oj=!1;\nfunction Pj(a,b){Cf=dd;a=Me();if(Ne(a)){if(\"selectionStart\"in a)var c={start:a.selectionStart,end:a.selectionEnd};else a:{c=(c=a.ownerDocument)&&c.defaultView||window;var d=c.getSelection&&c.getSelection();if(d&&0!==d.rangeCount){c=d.anchorNode;var e=d.anchorOffset,f=d.focusNode;d=d.focusOffset;try{c.nodeType,f.nodeType}catch(F){c=null;break a}var g=0,h=-1,k=-1,l=0,m=0,q=a,r=null;b:for(;;){for(var y;;){q!==c||0!==e&&3!==q.nodeType||(h=g+e);q!==f||0!==d&&3!==q.nodeType||(k=g+d);3===q.nodeType&&(g+=\nq.nodeValue.length);if(null===(y=q.firstChild))break;r=q;q=y}for(;;){if(q===a)break b;r===c&&++l===e&&(h=g);r===f&&++m===d&&(k=g);if(null!==(y=q.nextSibling))break;q=r;r=q.parentNode}q=y}c=-1===h||-1===k?null:{start:h,end:k}}else c=null}c=c||{start:0,end:0}}else c=null;Df={focusedElem:a,selectionRange:c};dd=!1;for(V=b;null!==V;)if(b=V,a=b.child,0!==(b.subtreeFlags&1028)&&null!==a)a.return=b,V=a;else for(;null!==V;){b=V;try{var n=b.alternate;if(0!==(b.flags&1024))switch(b.tag){case 0:case 11:case 15:break;\ncase 1:if(null!==n){var t=n.memoizedProps,J=n.memoizedState,x=b.stateNode,w=x.getSnapshotBeforeUpdate(b.elementType===b.type?t:Lg(b.type,t),J);x.__reactInternalSnapshotBeforeUpdate=w}break;case 3:var u=b.stateNode.containerInfo;1===u.nodeType?u.textContent=\"\":9===u.nodeType&&u.documentElement&&u.removeChild(u.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(p(163));}}catch(F){W(b,b.return,F)}a=b.sibling;if(null!==a){a.return=b.return;V=a;break}V=b.return}n=Oj;Oj=!1;return n}\nfunction Qj(a,b,c){var d=b.updateQueue;d=null!==d?d.lastEffect:null;if(null!==d){var e=d=d.next;do{if((e.tag&a)===a){var f=e.destroy;e.destroy=void 0;void 0!==f&&Nj(b,c,f)}e=e.next}while(e!==d)}}function Rj(a,b){b=b.updateQueue;b=null!==b?b.lastEffect:null;if(null!==b){var c=b=b.next;do{if((c.tag&a)===a){var d=c.create;c.destroy=d()}c=c.next}while(c!==b)}}function Sj(a){var b=a.ref;if(null!==b){var c=a.stateNode;switch(a.tag){case 5:a=c;break;default:a=c}\"function\"===typeof b?b(a):b.current=a}}\nfunction Tj(a){var b=a.alternate;null!==b&&(a.alternate=null,Tj(b));a.child=null;a.deletions=null;a.sibling=null;5===a.tag&&(b=a.stateNode,null!==b&&(delete b[Of],delete b[Pf],delete b[of],delete b[Qf],delete b[Rf]));a.stateNode=null;a.return=null;a.dependencies=null;a.memoizedProps=null;a.memoizedState=null;a.pendingProps=null;a.stateNode=null;a.updateQueue=null}function Uj(a){return 5===a.tag||3===a.tag||4===a.tag}\nfunction Vj(a){a:for(;;){for(;null===a.sibling;){if(null===a.return||Uj(a.return))return null;a=a.return}a.sibling.return=a.return;for(a=a.sibling;5!==a.tag&&6!==a.tag&&18!==a.tag;){if(a.flags&2)continue a;if(null===a.child||4===a.tag)continue a;else a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}\nfunction Wj(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?8===c.nodeType?c.parentNode.insertBefore(a,b):c.insertBefore(a,b):(8===c.nodeType?(b=c.parentNode,b.insertBefore(a,c)):(b=c,b.appendChild(a)),c=c._reactRootContainer,null!==c&&void 0!==c||null!==b.onclick||(b.onclick=Bf));else if(4!==d&&(a=a.child,null!==a))for(Wj(a,b,c),a=a.sibling;null!==a;)Wj(a,b,c),a=a.sibling}\nfunction Xj(a,b,c){var d=a.tag;if(5===d||6===d)a=a.stateNode,b?c.insertBefore(a,b):c.appendChild(a);else if(4!==d&&(a=a.child,null!==a))for(Xj(a,b,c),a=a.sibling;null!==a;)Xj(a,b,c),a=a.sibling}var X=null,Yj=!1;function Zj(a,b,c){for(c=c.child;null!==c;)ak(a,b,c),c=c.sibling}\nfunction ak(a,b,c){if(lc&&\"function\"===typeof lc.onCommitFiberUnmount)try{lc.onCommitFiberUnmount(kc,c)}catch(h){}switch(c.tag){case 5:U||Mj(c,b);case 6:var d=X,e=Yj;X=null;Zj(a,b,c);X=d;Yj=e;null!==X&&(Yj?(a=X,c=c.stateNode,8===a.nodeType?a.parentNode.removeChild(c):a.removeChild(c)):X.removeChild(c.stateNode));break;case 18:null!==X&&(Yj?(a=X,c=c.stateNode,8===a.nodeType?Kf(a.parentNode,c):1===a.nodeType&&Kf(a,c),bd(a)):Kf(X,c.stateNode));break;case 4:d=X;e=Yj;X=c.stateNode.containerInfo;Yj=!0;\nZj(a,b,c);X=d;Yj=e;break;case 0:case 11:case 14:case 15:if(!U&&(d=c.updateQueue,null!==d&&(d=d.lastEffect,null!==d))){e=d=d.next;do{var f=e,g=f.destroy;f=f.tag;void 0!==g&&(0!==(f&2)?Nj(c,b,g):0!==(f&4)&&Nj(c,b,g));e=e.next}while(e!==d)}Zj(a,b,c);break;case 1:if(!U&&(Mj(c,b),d=c.stateNode,\"function\"===typeof d.componentWillUnmount))try{d.props=c.memoizedProps,d.state=c.memoizedState,d.componentWillUnmount()}catch(h){W(c,b,h)}Zj(a,b,c);break;case 21:Zj(a,b,c);break;case 22:c.mode&1?(U=(d=U)||null!==\nc.memoizedState,Zj(a,b,c),U=d):Zj(a,b,c);break;default:Zj(a,b,c)}}function bk(a){var b=a.updateQueue;if(null!==b){a.updateQueue=null;var c=a.stateNode;null===c&&(c=a.stateNode=new Lj);b.forEach(function(b){var d=ck.bind(null,a,b);c.has(b)||(c.add(b),b.then(d,d))})}}\nfunction dk(a,b){var c=b.deletions;if(null!==c)for(var d=0;de&&(e=g);d&=~f}d=e;d=B()-d;d=(120>d?120:480>d?480:1080>d?1080:1920>d?1920:3E3>d?3E3:4320>d?4320:1960*mk(d/1960))-d;if(10a?16:a;if(null===xk)var d=!1;else{a=xk;xk=null;yk=0;if(0!==(K&6))throw Error(p(331));var e=K;K|=4;for(V=a.current;null!==V;){var f=V,g=f.child;if(0!==(V.flags&16)){var h=f.deletions;if(null!==h){for(var k=0;kB()-gk?Lk(a,0):sk|=c);Ek(a,b)}function Zk(a,b){0===b&&(0===(a.mode&1)?b=1:(b=sc,sc<<=1,0===(sc&130023424)&&(sc=4194304)));var c=L();a=Zg(a,b);null!==a&&(Ac(a,b,c),Ek(a,c))}function vj(a){var b=a.memoizedState,c=0;null!==b&&(c=b.retryLane);Zk(a,c)}\nfunction ck(a,b){var c=0;switch(a.tag){case 13:var d=a.stateNode;var e=a.memoizedState;null!==e&&(c=e.retryLane);break;case 19:d=a.stateNode;break;default:throw Error(p(314));}null!==d&&d.delete(b);Zk(a,c)}var Wk;\nWk=function(a,b,c){if(null!==a)if(a.memoizedProps!==b.pendingProps||Wf.current)Ug=!0;else{if(0===(a.lanes&c)&&0===(b.flags&128))return Ug=!1,zj(a,b,c);Ug=0!==(a.flags&131072)?!0:!1}else Ug=!1,I&&0!==(b.flags&1048576)&&ug(b,ng,b.index);b.lanes=0;switch(b.tag){case 2:var d=b.type;jj(a,b);a=b.pendingProps;var e=Yf(b,H.current);Tg(b,c);e=Xh(null,b,d,a,e,c);var f=bi();b.flags|=1;\"object\"===typeof e&&null!==e&&\"function\"===typeof e.render&&void 0===e.$$typeof?(b.tag=1,b.memoizedState=null,b.updateQueue=\nnull,Zf(d)?(f=!0,cg(b)):f=!1,b.memoizedState=null!==e.state&&void 0!==e.state?e.state:null,ah(b),e.updater=nh,b.stateNode=e,e._reactInternals=b,rh(b,d,a,c),b=kj(null,b,d,!0,f,c)):(b.tag=0,I&&f&&vg(b),Yi(null,b,e,c),b=b.child);return b;case 16:d=b.elementType;a:{jj(a,b);a=b.pendingProps;e=d._init;d=e(d._payload);b.type=d;e=b.tag=$k(d);a=Lg(d,a);switch(e){case 0:b=dj(null,b,d,a,c);break a;case 1:b=ij(null,b,d,a,c);break a;case 11:b=Zi(null,b,d,a,c);break a;case 14:b=aj(null,b,d,Lg(d.type,a),c);break a}throw Error(p(306,\nd,\"\"));}return b;case 0:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:Lg(d,e),dj(a,b,d,e,c);case 1:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:Lg(d,e),ij(a,b,d,e,c);case 3:a:{lj(b);if(null===a)throw Error(p(387));d=b.pendingProps;f=b.memoizedState;e=f.element;bh(a,b);gh(b,d,null,c);var g=b.memoizedState;d=g.element;if(f.isDehydrated)if(f={element:d,isDehydrated:!1,cache:g.cache,pendingSuspenseBoundaries:g.pendingSuspenseBoundaries,transitions:g.transitions},b.updateQueue.baseState=\nf,b.memoizedState=f,b.flags&256){e=Ki(Error(p(423)),b);b=mj(a,b,d,c,e);break a}else if(d!==e){e=Ki(Error(p(424)),b);b=mj(a,b,d,c,e);break a}else for(yg=Lf(b.stateNode.containerInfo.firstChild),xg=b,I=!0,zg=null,c=Ch(b,null,d,c),b.child=c;c;)c.flags=c.flags&-3|4096,c=c.sibling;else{Ig();if(d===e){b=$i(a,b,c);break a}Yi(a,b,d,c)}b=b.child}return b;case 5:return Kh(b),null===a&&Eg(b),d=b.type,e=b.pendingProps,f=null!==a?a.memoizedProps:null,g=e.children,Ef(d,e)?g=null:null!==f&&Ef(d,f)&&(b.flags|=32),\nhj(a,b),Yi(a,b,g,c),b.child;case 6:return null===a&&Eg(b),null;case 13:return pj(a,b,c);case 4:return Ih(b,b.stateNode.containerInfo),d=b.pendingProps,null===a?b.child=Bh(b,null,d,c):Yi(a,b,d,c),b.child;case 11:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:Lg(d,e),Zi(a,b,d,e,c);case 7:return Yi(a,b,b.pendingProps,c),b.child;case 8:return Yi(a,b,b.pendingProps.children,c),b.child;case 12:return Yi(a,b,b.pendingProps.children,c),b.child;case 10:a:{d=b.type._context;e=b.pendingProps;f=b.memoizedProps;\ng=e.value;G(Mg,d._currentValue);d._currentValue=g;if(null!==f)if(He(f.value,g)){if(f.children===e.children&&!Wf.current){b=$i(a,b,c);break a}}else for(f=b.child,null!==f&&(f.return=b);null!==f;){var h=f.dependencies;if(null!==h){g=f.child;for(var k=h.firstContext;null!==k;){if(k.context===d){if(1===f.tag){k=ch(-1,c&-c);k.tag=2;var l=f.updateQueue;if(null!==l){l=l.shared;var m=l.pending;null===m?k.next=k:(k.next=m.next,m.next=k);l.pending=k}}f.lanes|=c;k=f.alternate;null!==k&&(k.lanes|=c);Sg(f.return,\nc,b);h.lanes|=c;break}k=k.next}}else if(10===f.tag)g=f.type===b.type?null:f.child;else if(18===f.tag){g=f.return;if(null===g)throw Error(p(341));g.lanes|=c;h=g.alternate;null!==h&&(h.lanes|=c);Sg(g,c,b);g=f.sibling}else g=f.child;if(null!==g)g.return=f;else for(g=f;null!==g;){if(g===b){g=null;break}f=g.sibling;if(null!==f){f.return=g.return;g=f;break}g=g.return}f=g}Yi(a,b,e.children,c);b=b.child}return b;case 9:return e=b.type,d=b.pendingProps.children,Tg(b,c),e=Vg(e),d=d(e),b.flags|=1,Yi(a,b,d,c),\nb.child;case 14:return d=b.type,e=Lg(d,b.pendingProps),e=Lg(d.type,e),aj(a,b,d,e,c);case 15:return cj(a,b,b.type,b.pendingProps,c);case 17:return d=b.type,e=b.pendingProps,e=b.elementType===d?e:Lg(d,e),jj(a,b),b.tag=1,Zf(d)?(a=!0,cg(b)):a=!1,Tg(b,c),ph(b,d,e),rh(b,d,e,c),kj(null,b,d,!0,a,c);case 19:return yj(a,b,c);case 22:return ej(a,b,c)}throw Error(p(156,b.tag));};function Gk(a,b){return ac(a,b)}\nfunction al(a,b,c,d){this.tag=a;this.key=c;this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null;this.index=0;this.ref=null;this.pendingProps=b;this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null;this.mode=d;this.subtreeFlags=this.flags=0;this.deletions=null;this.childLanes=this.lanes=0;this.alternate=null}function Bg(a,b,c,d){return new al(a,b,c,d)}function bj(a){a=a.prototype;return!(!a||!a.isReactComponent)}\nfunction $k(a){if(\"function\"===typeof a)return bj(a)?1:0;if(void 0!==a&&null!==a){a=a.$$typeof;if(a===Da)return 11;if(a===Ga)return 14}return 2}\nfunction wh(a,b){var c=a.alternate;null===c?(c=Bg(a.tag,b,a.key,a.mode),c.elementType=a.elementType,c.type=a.type,c.stateNode=a.stateNode,c.alternate=a,a.alternate=c):(c.pendingProps=b,c.type=a.type,c.flags=0,c.subtreeFlags=0,c.deletions=null);c.flags=a.flags&14680064;c.childLanes=a.childLanes;c.lanes=a.lanes;c.child=a.child;c.memoizedProps=a.memoizedProps;c.memoizedState=a.memoizedState;c.updateQueue=a.updateQueue;b=a.dependencies;c.dependencies=null===b?null:{lanes:b.lanes,firstContext:b.firstContext};\nc.sibling=a.sibling;c.index=a.index;c.ref=a.ref;return c}\nfunction yh(a,b,c,d,e,f){var g=2;d=a;if(\"function\"===typeof a)bj(a)&&(g=1);else if(\"string\"===typeof a)g=5;else a:switch(a){case ya:return Ah(c.children,e,f,b);case za:g=8;e|=8;break;case Aa:return a=Bg(12,c,b,e|2),a.elementType=Aa,a.lanes=f,a;case Ea:return a=Bg(13,c,b,e),a.elementType=Ea,a.lanes=f,a;case Fa:return a=Bg(19,c,b,e),a.elementType=Fa,a.lanes=f,a;case Ia:return qj(c,e,f,b);default:if(\"object\"===typeof a&&null!==a)switch(a.$$typeof){case Ba:g=10;break a;case Ca:g=9;break a;case Da:g=11;\nbreak a;case Ga:g=14;break a;case Ha:g=16;d=null;break a}throw Error(p(130,null==a?a:typeof a,\"\"));}b=Bg(g,c,b,e);b.elementType=a;b.type=d;b.lanes=f;return b}function Ah(a,b,c,d){a=Bg(7,a,d,b);a.lanes=c;return a}function qj(a,b,c,d){a=Bg(22,a,d,b);a.elementType=Ia;a.lanes=c;a.stateNode={isHidden:!1};return a}function xh(a,b,c){a=Bg(6,a,null,b);a.lanes=c;return a}\nfunction zh(a,b,c){b=Bg(4,null!==a.children?a.children:[],a.key,b);b.lanes=c;b.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation};return b}\nfunction bl(a,b,c,d,e){this.tag=b;this.containerInfo=a;this.finishedWork=this.pingCache=this.current=this.pendingChildren=null;this.timeoutHandle=-1;this.callbackNode=this.pendingContext=this.context=null;this.callbackPriority=0;this.eventTimes=zc(0);this.expirationTimes=zc(-1);this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0;this.entanglements=zc(0);this.identifierPrefix=d;this.onRecoverableError=e;this.mutableSourceEagerHydrationData=\nnull}function cl(a,b,c,d,e,f,g,h,k){a=new bl(a,b,c,h,k);1===b?(b=1,!0===f&&(b|=8)):b=0;f=Bg(3,null,null,b);a.current=f;f.stateNode=a;f.memoizedState={element:d,isDehydrated:c,cache:null,transitions:null,pendingSuspenseBoundaries:null};ah(f);return a}function dl(a,b,c){var d=3 createMemoryLocation(entry, typeof entry === \"string\" ? null : entry.state, index === 0 ? \"default\" : undefined));\n let index = clampIndex(initialIndex == null ? entries.length - 1 : initialIndex);\n let action = Action.Pop;\n let listener = null;\n function clampIndex(n) {\n return Math.min(Math.max(n, 0), entries.length - 1);\n }\n function getCurrentLocation() {\n return entries[index];\n }\n function createMemoryLocation(to, state, key) {\n if (state === void 0) {\n state = null;\n }\n let location = createLocation(entries ? getCurrentLocation().pathname : \"/\", to, state, key);\n warning(location.pathname.charAt(0) === \"/\", \"relative pathnames are not supported in memory history: \" + JSON.stringify(to));\n return location;\n }\n function createHref(to) {\n return typeof to === \"string\" ? to : createPath(to);\n }\n let history = {\n get index() {\n return index;\n },\n get action() {\n return action;\n },\n get location() {\n return getCurrentLocation();\n },\n createHref,\n createURL(to) {\n return new URL(createHref(to), \"http://localhost\");\n },\n encodeLocation(to) {\n let path = typeof to === \"string\" ? parsePath(to) : to;\n return {\n pathname: path.pathname || \"\",\n search: path.search || \"\",\n hash: path.hash || \"\"\n };\n },\n push(to, state) {\n action = Action.Push;\n let nextLocation = createMemoryLocation(to, state);\n index += 1;\n entries.splice(index, entries.length, nextLocation);\n if (v5Compat && listener) {\n listener({\n action,\n location: nextLocation,\n delta: 1\n });\n }\n },\n replace(to, state) {\n action = Action.Replace;\n let nextLocation = createMemoryLocation(to, state);\n entries[index] = nextLocation;\n if (v5Compat && listener) {\n listener({\n action,\n location: nextLocation,\n delta: 0\n });\n }\n },\n go(delta) {\n action = Action.Pop;\n let nextIndex = clampIndex(index + delta);\n let nextLocation = entries[nextIndex];\n index = nextIndex;\n if (listener) {\n listener({\n action,\n location: nextLocation,\n delta\n });\n }\n },\n listen(fn) {\n listener = fn;\n return () => {\n listener = null;\n };\n }\n };\n return history;\n}\n/**\n * Browser history stores the location in regular URLs. This is the standard for\n * most web apps, but it requires some configuration on the server to ensure you\n * serve the same app at multiple URLs.\n *\n * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory\n */\nfunction createBrowserHistory(options) {\n if (options === void 0) {\n options = {};\n }\n function createBrowserLocation(window, globalHistory) {\n let {\n pathname,\n search,\n hash\n } = window.location;\n return createLocation(\"\", {\n pathname,\n search,\n hash\n },\n // state defaults to `null` because `window.history.state` does\n globalHistory.state && globalHistory.state.usr || null, globalHistory.state && globalHistory.state.key || \"default\");\n }\n function createBrowserHref(window, to) {\n return typeof to === \"string\" ? to : createPath(to);\n }\n return getUrlBasedHistory(createBrowserLocation, createBrowserHref, null, options);\n}\n/**\n * Hash history stores the location in window.location.hash. This makes it ideal\n * for situations where you don't want to send the location to the server for\n * some reason, either because you do cannot configure it or the URL space is\n * reserved for something else.\n *\n * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory\n */\nfunction createHashHistory(options) {\n if (options === void 0) {\n options = {};\n }\n function createHashLocation(window, globalHistory) {\n let {\n pathname = \"/\",\n search = \"\",\n hash = \"\"\n } = parsePath(window.location.hash.substr(1));\n return createLocation(\"\", {\n pathname,\n search,\n hash\n },\n // state defaults to `null` because `window.history.state` does\n globalHistory.state && globalHistory.state.usr || null, globalHistory.state && globalHistory.state.key || \"default\");\n }\n function createHashHref(window, to) {\n let base = window.document.querySelector(\"base\");\n let href = \"\";\n if (base && base.getAttribute(\"href\")) {\n let url = window.location.href;\n let hashIndex = url.indexOf(\"#\");\n href = hashIndex === -1 ? url : url.slice(0, hashIndex);\n }\n return href + \"#\" + (typeof to === \"string\" ? to : createPath(to));\n }\n function validateHashLocation(location, to) {\n warning(location.pathname.charAt(0) === \"/\", \"relative pathnames are not supported in hash history.push(\" + JSON.stringify(to) + \")\");\n }\n return getUrlBasedHistory(createHashLocation, createHashHref, validateHashLocation, options);\n}\nfunction invariant(value, message) {\n if (value === false || value === null || typeof value === \"undefined\") {\n throw new Error(message);\n }\n}\nfunction warning(cond, message) {\n if (!cond) {\n // eslint-disable-next-line no-console\n if (typeof console !== \"undefined\") console.warn(message);\n try {\n // Welcome to debugging history!\n //\n // This error is thrown as a convenience so you can more easily\n // find the source for a warning that appears in the console by\n // enabling \"pause on exceptions\" in your JavaScript debugger.\n throw new Error(message);\n // eslint-disable-next-line no-empty\n } catch (e) {}\n }\n}\nfunction createKey() {\n return Math.random().toString(36).substr(2, 8);\n}\n/**\n * For browser-based histories, we combine the state and key into an object\n */\nfunction getHistoryState(location, index) {\n return {\n usr: location.state,\n key: location.key,\n idx: index\n };\n}\n/**\n * Creates a Location object with a unique key from the given Path\n */\nfunction createLocation(current, to, state, key) {\n if (state === void 0) {\n state = null;\n }\n let location = _extends({\n pathname: typeof current === \"string\" ? current : current.pathname,\n search: \"\",\n hash: \"\"\n }, typeof to === \"string\" ? parsePath(to) : to, {\n state,\n // TODO: This could be cleaned up. push/replace should probably just take\n // full Locations now and avoid the need to run through this flow at all\n // But that's a pretty big refactor to the current test suite so going to\n // keep as is for the time being and just let any incoming keys take precedence\n key: to && to.key || key || createKey()\n });\n return location;\n}\n/**\n * Creates a string URL path from the given pathname, search, and hash components.\n */\nfunction createPath(_ref) {\n let {\n pathname = \"/\",\n search = \"\",\n hash = \"\"\n } = _ref;\n if (search && search !== \"?\") pathname += search.charAt(0) === \"?\" ? search : \"?\" + search;\n if (hash && hash !== \"#\") pathname += hash.charAt(0) === \"#\" ? hash : \"#\" + hash;\n return pathname;\n}\n/**\n * Parses a string URL path into its separate pathname, search, and hash components.\n */\nfunction parsePath(path) {\n let parsedPath = {};\n if (path) {\n let hashIndex = path.indexOf(\"#\");\n if (hashIndex >= 0) {\n parsedPath.hash = path.substr(hashIndex);\n path = path.substr(0, hashIndex);\n }\n let searchIndex = path.indexOf(\"?\");\n if (searchIndex >= 0) {\n parsedPath.search = path.substr(searchIndex);\n path = path.substr(0, searchIndex);\n }\n if (path) {\n parsedPath.pathname = path;\n }\n }\n return parsedPath;\n}\nfunction getUrlBasedHistory(getLocation, createHref, validateLocation, options) {\n if (options === void 0) {\n options = {};\n }\n let {\n window = document.defaultView,\n v5Compat = false\n } = options;\n let globalHistory = window.history;\n let action = Action.Pop;\n let listener = null;\n let index = getIndex();\n // Index should only be null when we initialize. If not, it's because the\n // user called history.pushState or history.replaceState directly, in which\n // case we should log a warning as it will result in bugs.\n if (index == null) {\n index = 0;\n globalHistory.replaceState(_extends({}, globalHistory.state, {\n idx: index\n }), \"\");\n }\n function getIndex() {\n let state = globalHistory.state || {\n idx: null\n };\n return state.idx;\n }\n function handlePop() {\n action = Action.Pop;\n let nextIndex = getIndex();\n let delta = nextIndex == null ? null : nextIndex - index;\n index = nextIndex;\n if (listener) {\n listener({\n action,\n location: history.location,\n delta\n });\n }\n }\n function push(to, state) {\n action = Action.Push;\n let location = createLocation(history.location, to, state);\n if (validateLocation) validateLocation(location, to);\n index = getIndex() + 1;\n let historyState = getHistoryState(location, index);\n let url = history.createHref(location);\n // try...catch because iOS limits us to 100 pushState calls :/\n try {\n globalHistory.pushState(historyState, \"\", url);\n } catch (error) {\n // If the exception is because `state` can't be serialized, let that throw\n // outwards just like a replace call would so the dev knows the cause\n // https://html.spec.whatwg.org/multipage/nav-history-apis.html#shared-history-push/replace-state-steps\n // https://html.spec.whatwg.org/multipage/structured-data.html#structuredserializeinternal\n if (error instanceof DOMException && error.name === \"DataCloneError\") {\n throw error;\n }\n // They are going to lose state here, but there is no real\n // way to warn them about it since the page will refresh...\n window.location.assign(url);\n }\n if (v5Compat && listener) {\n listener({\n action,\n location: history.location,\n delta: 1\n });\n }\n }\n function replace(to, state) {\n action = Action.Replace;\n let location = createLocation(history.location, to, state);\n if (validateLocation) validateLocation(location, to);\n index = getIndex();\n let historyState = getHistoryState(location, index);\n let url = history.createHref(location);\n globalHistory.replaceState(historyState, \"\", url);\n if (v5Compat && listener) {\n listener({\n action,\n location: history.location,\n delta: 0\n });\n }\n }\n function createURL(to) {\n // window.location.origin is \"null\" (the literal string value) in Firefox\n // under certain conditions, notably when serving from a local HTML file\n // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297\n let base = window.location.origin !== \"null\" ? window.location.origin : window.location.href;\n let href = typeof to === \"string\" ? to : createPath(to);\n invariant(base, \"No window.location.(origin|href) available to create URL for href: \" + href);\n return new URL(href, base);\n }\n let history = {\n get action() {\n return action;\n },\n get location() {\n return getLocation(window, globalHistory);\n },\n listen(fn) {\n if (listener) {\n throw new Error(\"A history only accepts one active listener\");\n }\n window.addEventListener(PopStateEventType, handlePop);\n listener = fn;\n return () => {\n window.removeEventListener(PopStateEventType, handlePop);\n listener = null;\n };\n },\n createHref(to) {\n return createHref(window, to);\n },\n createURL,\n encodeLocation(to) {\n // Encode a Location the same way window.location would\n let url = createURL(to);\n return {\n pathname: url.pathname,\n search: url.search,\n hash: url.hash\n };\n },\n push,\n replace,\n go(n) {\n return globalHistory.go(n);\n }\n };\n return history;\n}\n//#endregion\n\nvar ResultType;\n(function (ResultType) {\n ResultType[\"data\"] = \"data\";\n ResultType[\"deferred\"] = \"deferred\";\n ResultType[\"redirect\"] = \"redirect\";\n ResultType[\"error\"] = \"error\";\n})(ResultType || (ResultType = {}));\nconst immutableRouteKeys = new Set([\"lazy\", \"caseSensitive\", \"path\", \"id\", \"index\", \"children\"]);\nfunction isIndexRoute(route) {\n return route.index === true;\n}\n// Walk the route tree generating unique IDs where necessary so we are working\n// solely with AgnosticDataRouteObject's within the Router\nfunction convertRoutesToDataRoutes(routes, mapRouteProperties, parentPath, manifest) {\n if (parentPath === void 0) {\n parentPath = [];\n }\n if (manifest === void 0) {\n manifest = {};\n }\n return routes.map((route, index) => {\n let treePath = [...parentPath, index];\n let id = typeof route.id === \"string\" ? route.id : treePath.join(\"-\");\n invariant(route.index !== true || !route.children, \"Cannot specify children on an index route\");\n invariant(!manifest[id], \"Found a route id collision on id \\\"\" + id + \"\\\". Route \" + \"id's must be globally unique within Data Router usages\");\n if (isIndexRoute(route)) {\n let indexRoute = _extends({}, route, mapRouteProperties(route), {\n id\n });\n manifest[id] = indexRoute;\n return indexRoute;\n } else {\n let pathOrLayoutRoute = _extends({}, route, mapRouteProperties(route), {\n id,\n children: undefined\n });\n manifest[id] = pathOrLayoutRoute;\n if (route.children) {\n pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, mapRouteProperties, treePath, manifest);\n }\n return pathOrLayoutRoute;\n }\n });\n}\n/**\n * Matches the given routes to a location and returns the match data.\n *\n * @see https://reactrouter.com/utils/match-routes\n */\nfunction matchRoutes(routes, locationArg, basename) {\n if (basename === void 0) {\n basename = \"/\";\n }\n let location = typeof locationArg === \"string\" ? parsePath(locationArg) : locationArg;\n let pathname = stripBasename(location.pathname || \"/\", basename);\n if (pathname == null) {\n return null;\n }\n let branches = flattenRoutes(routes);\n rankRouteBranches(branches);\n let matches = null;\n for (let i = 0; matches == null && i < branches.length; ++i) {\n matches = matchRouteBranch(branches[i],\n // Incoming pathnames are generally encoded from either window.location\n // or from router.navigate, but we want to match against the unencoded\n // paths in the route definitions. Memory router locations won't be\n // encoded here but there also shouldn't be anything to decode so this\n // should be a safe operation. This avoids needing matchRoutes to be\n // history-aware.\n safelyDecodeURI(pathname));\n }\n return matches;\n}\nfunction flattenRoutes(routes, branches, parentsMeta, parentPath) {\n if (branches === void 0) {\n branches = [];\n }\n if (parentsMeta === void 0) {\n parentsMeta = [];\n }\n if (parentPath === void 0) {\n parentPath = \"\";\n }\n let flattenRoute = (route, index, relativePath) => {\n let meta = {\n relativePath: relativePath === undefined ? route.path || \"\" : relativePath,\n caseSensitive: route.caseSensitive === true,\n childrenIndex: index,\n route\n };\n if (meta.relativePath.startsWith(\"/\")) {\n invariant(meta.relativePath.startsWith(parentPath), \"Absolute route path \\\"\" + meta.relativePath + \"\\\" nested under path \" + (\"\\\"\" + parentPath + \"\\\" is not valid. An absolute child route path \") + \"must start with the combined path of all its parent routes.\");\n meta.relativePath = meta.relativePath.slice(parentPath.length);\n }\n let path = joinPaths([parentPath, meta.relativePath]);\n let routesMeta = parentsMeta.concat(meta);\n // Add the children before adding this route to the array so we traverse the\n // route tree depth-first and child routes appear before their parents in\n // the \"flattened\" version.\n if (route.children && route.children.length > 0) {\n invariant(\n // Our types know better, but runtime JS may not!\n // @ts-expect-error\n route.index !== true, \"Index routes must not have child routes. Please remove \" + (\"all child routes from route path \\\"\" + path + \"\\\".\"));\n flattenRoutes(route.children, branches, routesMeta, path);\n }\n // Routes without a path shouldn't ever match by themselves unless they are\n // index routes, so don't add them to the list of possible branches.\n if (route.path == null && !route.index) {\n return;\n }\n branches.push({\n path,\n score: computeScore(path, route.index),\n routesMeta\n });\n };\n routes.forEach((route, index) => {\n var _route$path;\n // coarse-grain check for optional params\n if (route.path === \"\" || !((_route$path = route.path) != null && _route$path.includes(\"?\"))) {\n flattenRoute(route, index);\n } else {\n for (let exploded of explodeOptionalSegments(route.path)) {\n flattenRoute(route, index, exploded);\n }\n }\n });\n return branches;\n}\n/**\n * Computes all combinations of optional path segments for a given path,\n * excluding combinations that are ambiguous and of lower priority.\n *\n * For example, `/one/:two?/three/:four?/:five?` explodes to:\n * - `/one/three`\n * - `/one/:two/three`\n * - `/one/three/:four`\n * - `/one/three/:five`\n * - `/one/:two/three/:four`\n * - `/one/:two/three/:five`\n * - `/one/three/:four/:five`\n * - `/one/:two/three/:four/:five`\n */\nfunction explodeOptionalSegments(path) {\n let segments = path.split(\"/\");\n if (segments.length === 0) return [];\n let [first, ...rest] = segments;\n // Optional path segments are denoted by a trailing `?`\n let isOptional = first.endsWith(\"?\");\n // Compute the corresponding required segment: `foo?` -> `foo`\n let required = first.replace(/\\?$/, \"\");\n if (rest.length === 0) {\n // Intepret empty string as omitting an optional segment\n // `[\"one\", \"\", \"three\"]` corresponds to omitting `:two` from `/one/:two?/three` -> `/one/three`\n return isOptional ? [required, \"\"] : [required];\n }\n let restExploded = explodeOptionalSegments(rest.join(\"/\"));\n let result = [];\n // All child paths with the prefix. Do this for all children before the\n // optional version for all children so we get consistent ordering where the\n // parent optional aspect is preferred as required. Otherwise, we can get\n // child sections interspersed where deeper optional segments are higher than\n // parent optional segments, where for example, /:two would explodes _earlier_\n // then /:one. By always including the parent as required _for all children_\n // first, we avoid this issue\n result.push(...restExploded.map(subpath => subpath === \"\" ? required : [required, subpath].join(\"/\")));\n // Then if this is an optional value, add all child versions without\n if (isOptional) {\n result.push(...restExploded);\n }\n // for absolute paths, ensure `/` instead of empty segment\n return result.map(exploded => path.startsWith(\"/\") && exploded === \"\" ? \"/\" : exploded);\n}\nfunction rankRouteBranches(branches) {\n branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first\n : compareIndexes(a.routesMeta.map(meta => meta.childrenIndex), b.routesMeta.map(meta => meta.childrenIndex)));\n}\nconst paramRe = /^:\\w+$/;\nconst dynamicSegmentValue = 3;\nconst indexRouteValue = 2;\nconst emptySegmentValue = 1;\nconst staticSegmentValue = 10;\nconst splatPenalty = -2;\nconst isSplat = s => s === \"*\";\nfunction computeScore(path, index) {\n let segments = path.split(\"/\");\n let initialScore = segments.length;\n if (segments.some(isSplat)) {\n initialScore += splatPenalty;\n }\n if (index) {\n initialScore += indexRouteValue;\n }\n return segments.filter(s => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === \"\" ? emptySegmentValue : staticSegmentValue), initialScore);\n}\nfunction compareIndexes(a, b) {\n let siblings = a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]);\n return siblings ?\n // If two routes are siblings, we should try to match the earlier sibling\n // first. This allows people to have fine-grained control over the matching\n // behavior by simply putting routes with identical paths in the order they\n // want them tried.\n a[a.length - 1] - b[b.length - 1] :\n // Otherwise, it doesn't really make sense to rank non-siblings by index,\n // so they sort equally.\n 0;\n}\nfunction matchRouteBranch(branch, pathname) {\n let {\n routesMeta\n } = branch;\n let matchedParams = {};\n let matchedPathname = \"/\";\n let matches = [];\n for (let i = 0; i < routesMeta.length; ++i) {\n let meta = routesMeta[i];\n let end = i === routesMeta.length - 1;\n let remainingPathname = matchedPathname === \"/\" ? pathname : pathname.slice(matchedPathname.length) || \"/\";\n let match = matchPath({\n path: meta.relativePath,\n caseSensitive: meta.caseSensitive,\n end\n }, remainingPathname);\n if (!match) return null;\n Object.assign(matchedParams, match.params);\n let route = meta.route;\n matches.push({\n // TODO: Can this as be avoided?\n params: matchedParams,\n pathname: joinPaths([matchedPathname, match.pathname]),\n pathnameBase: normalizePathname(joinPaths([matchedPathname, match.pathnameBase])),\n route\n });\n if (match.pathnameBase !== \"/\") {\n matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);\n }\n }\n return matches;\n}\n/**\n * Returns a path with params interpolated.\n *\n * @see https://reactrouter.com/utils/generate-path\n */\nfunction generatePath(originalPath, params) {\n if (params === void 0) {\n params = {};\n }\n let path = originalPath;\n if (path.endsWith(\"*\") && path !== \"*\" && !path.endsWith(\"/*\")) {\n warning(false, \"Route path \\\"\" + path + \"\\\" will be treated as if it were \" + (\"\\\"\" + path.replace(/\\*$/, \"/*\") + \"\\\" because the `*` character must \") + \"always follow a `/` in the pattern. To get rid of this warning, \" + (\"please change the route path to \\\"\" + path.replace(/\\*$/, \"/*\") + \"\\\".\"));\n path = path.replace(/\\*$/, \"/*\");\n }\n // ensure `/` is added at the beginning if the path is absolute\n const prefix = path.startsWith(\"/\") ? \"/\" : \"\";\n const segments = path.split(/\\/+/).map((segment, index, array) => {\n const isLastSegment = index === array.length - 1;\n // only apply the splat if it's the last segment\n if (isLastSegment && segment === \"*\") {\n const star = \"*\";\n const starParam = params[star];\n // Apply the splat\n return starParam;\n }\n const keyMatch = segment.match(/^:(\\w+)(\\??)$/);\n if (keyMatch) {\n const [, key, optional] = keyMatch;\n let param = params[key];\n if (optional === \"?\") {\n return param == null ? \"\" : param;\n }\n if (param == null) {\n invariant(false, \"Missing \\\":\" + key + \"\\\" param\");\n }\n return param;\n }\n // Remove any optional markers from optional static segments\n return segment.replace(/\\?$/g, \"\");\n })\n // Remove empty segments\n .filter(segment => !!segment);\n return prefix + segments.join(\"/\");\n}\n/**\n * Performs pattern matching on a URL pathname and returns information about\n * the match.\n *\n * @see https://reactrouter.com/utils/match-path\n */\nfunction matchPath(pattern, pathname) {\n if (typeof pattern === \"string\") {\n pattern = {\n path: pattern,\n caseSensitive: false,\n end: true\n };\n }\n let [matcher, paramNames] = compilePath(pattern.path, pattern.caseSensitive, pattern.end);\n let match = pathname.match(matcher);\n if (!match) return null;\n let matchedPathname = match[0];\n let pathnameBase = matchedPathname.replace(/(.)\\/+$/, \"$1\");\n let captureGroups = match.slice(1);\n let params = paramNames.reduce((memo, paramName, index) => {\n // We need to compute the pathnameBase here using the raw splat value\n // instead of using params[\"*\"] later because it will be decoded then\n if (paramName === \"*\") {\n let splatValue = captureGroups[index] || \"\";\n pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\\/+$/, \"$1\");\n }\n memo[paramName] = safelyDecodeURIComponent(captureGroups[index] || \"\", paramName);\n return memo;\n }, {});\n return {\n params,\n pathname: matchedPathname,\n pathnameBase,\n pattern\n };\n}\nfunction compilePath(path, caseSensitive, end) {\n if (caseSensitive === void 0) {\n caseSensitive = false;\n }\n if (end === void 0) {\n end = true;\n }\n warning(path === \"*\" || !path.endsWith(\"*\") || path.endsWith(\"/*\"), \"Route path \\\"\" + path + \"\\\" will be treated as if it were \" + (\"\\\"\" + path.replace(/\\*$/, \"/*\") + \"\\\" because the `*` character must \") + \"always follow a `/` in the pattern. To get rid of this warning, \" + (\"please change the route path to \\\"\" + path.replace(/\\*$/, \"/*\") + \"\\\".\"));\n let paramNames = [];\n let regexpSource = \"^\" + path.replace(/\\/*\\*?$/, \"\") // Ignore trailing / and /*, we'll handle it below\n .replace(/^\\/*/, \"/\") // Make sure it has a leading /\n .replace(/[\\\\.*+^$?{}|()[\\]]/g, \"\\\\$&\") // Escape special regex chars\n .replace(/\\/:(\\w+)/g, (_, paramName) => {\n paramNames.push(paramName);\n return \"/([^\\\\/]+)\";\n });\n if (path.endsWith(\"*\")) {\n paramNames.push(\"*\");\n regexpSource += path === \"*\" || path === \"/*\" ? \"(.*)$\" // Already matched the initial /, just match the rest\n : \"(?:\\\\/(.+)|\\\\/*)$\"; // Don't include the / in params[\"*\"]\n } else if (end) {\n // When matching to the end, ignore trailing slashes\n regexpSource += \"\\\\/*$\";\n } else if (path !== \"\" && path !== \"/\") {\n // If our path is non-empty and contains anything beyond an initial slash,\n // then we have _some_ form of path in our regex so we should expect to\n // match only if we find the end of this path segment. Look for an optional\n // non-captured trailing slash (to match a portion of the URL) or the end\n // of the path (if we've matched to the end). We used to do this with a\n // word boundary but that gives false positives on routes like\n // /user-preferences since `-` counts as a word boundary.\n regexpSource += \"(?:(?=\\\\/|$))\";\n } else ;\n let matcher = new RegExp(regexpSource, caseSensitive ? undefined : \"i\");\n return [matcher, paramNames];\n}\nfunction safelyDecodeURI(value) {\n try {\n return decodeURI(value);\n } catch (error) {\n warning(false, \"The URL path \\\"\" + value + \"\\\" could not be decoded because it is is a \" + \"malformed URL segment. This is probably due to a bad percent \" + (\"encoding (\" + error + \").\"));\n return value;\n }\n}\nfunction safelyDecodeURIComponent(value, paramName) {\n try {\n return decodeURIComponent(value);\n } catch (error) {\n warning(false, \"The value for the URL param \\\"\" + paramName + \"\\\" will not be decoded because\" + (\" the string \\\"\" + value + \"\\\" is a malformed URL segment. This is probably\") + (\" due to a bad percent encoding (\" + error + \").\"));\n return value;\n }\n}\n/**\n * @private\n */\nfunction stripBasename(pathname, basename) {\n if (basename === \"/\") return pathname;\n if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {\n return null;\n }\n // We want to leave trailing slash behavior in the user's control, so if they\n // specify a basename with a trailing slash, we should support it\n let startIndex = basename.endsWith(\"/\") ? basename.length - 1 : basename.length;\n let nextChar = pathname.charAt(startIndex);\n if (nextChar && nextChar !== \"/\") {\n // pathname does not start with basename/\n return null;\n }\n return pathname.slice(startIndex) || \"/\";\n}\n/**\n * Returns a resolved path object relative to the given pathname.\n *\n * @see https://reactrouter.com/utils/resolve-path\n */\nfunction resolvePath(to, fromPathname) {\n if (fromPathname === void 0) {\n fromPathname = \"/\";\n }\n let {\n pathname: toPathname,\n search = \"\",\n hash = \"\"\n } = typeof to === \"string\" ? parsePath(to) : to;\n let pathname = toPathname ? toPathname.startsWith(\"/\") ? toPathname : resolvePathname(toPathname, fromPathname) : fromPathname;\n return {\n pathname,\n search: normalizeSearch(search),\n hash: normalizeHash(hash)\n };\n}\nfunction resolvePathname(relativePath, fromPathname) {\n let segments = fromPathname.replace(/\\/+$/, \"\").split(\"/\");\n let relativeSegments = relativePath.split(\"/\");\n relativeSegments.forEach(segment => {\n if (segment === \"..\") {\n // Keep the root \"\" segment so the pathname starts at /\n if (segments.length > 1) segments.pop();\n } else if (segment !== \".\") {\n segments.push(segment);\n }\n });\n return segments.length > 1 ? segments.join(\"/\") : \"/\";\n}\nfunction getInvalidPathError(char, field, dest, path) {\n return \"Cannot include a '\" + char + \"' character in a manually specified \" + (\"`to.\" + field + \"` field [\" + JSON.stringify(path) + \"]. Please separate it out to the \") + (\"`to.\" + dest + \"` field. Alternatively you may provide the full path as \") + \"a string in and the router will parse it for you.\";\n}\n/**\n * @private\n *\n * When processing relative navigation we want to ignore ancestor routes that\n * do not contribute to the path, such that index/pathless layout routes don't\n * interfere.\n *\n * For example, when moving a route element into an index route and/or a\n * pathless layout route, relative link behavior contained within should stay\n * the same. Both of the following examples should link back to the root:\n *\n * \n * \n * \n *\n * \n * \n * }> // <-- Does not contribute\n * // <-- Does not contribute\n * \n * \n */\nfunction getPathContributingMatches(matches) {\n return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);\n}\n/**\n * @private\n */\nfunction resolveTo(toArg, routePathnames, locationPathname, isPathRelative) {\n if (isPathRelative === void 0) {\n isPathRelative = false;\n }\n let to;\n if (typeof toArg === \"string\") {\n to = parsePath(toArg);\n } else {\n to = _extends({}, toArg);\n invariant(!to.pathname || !to.pathname.includes(\"?\"), getInvalidPathError(\"?\", \"pathname\", \"search\", to));\n invariant(!to.pathname || !to.pathname.includes(\"#\"), getInvalidPathError(\"#\", \"pathname\", \"hash\", to));\n invariant(!to.search || !to.search.includes(\"#\"), getInvalidPathError(\"#\", \"search\", \"hash\", to));\n }\n let isEmptyPath = toArg === \"\" || to.pathname === \"\";\n let toPathname = isEmptyPath ? \"/\" : to.pathname;\n let from;\n // Routing is relative to the current pathname if explicitly requested.\n //\n // If a pathname is explicitly provided in `to`, it should be relative to the\n // route context. This is explained in `Note on `` values` in our\n // migration guide from v5 as a means of disambiguation between `to` values\n // that begin with `/` and those that do not. However, this is problematic for\n // `to` values that do not provide a pathname. `to` can simply be a search or\n // hash string, in which case we should assume that the navigation is relative\n // to the current location's pathname and *not* the route pathname.\n if (isPathRelative || toPathname == null) {\n from = locationPathname;\n } else {\n let routePathnameIndex = routePathnames.length - 1;\n if (toPathname.startsWith(\"..\")) {\n let toSegments = toPathname.split(\"/\");\n // Each leading .. segment means \"go up one route\" instead of \"go up one\n // URL segment\". This is a key difference from how works and a\n // major reason we call this a \"to\" value instead of a \"href\".\n while (toSegments[0] === \"..\") {\n toSegments.shift();\n routePathnameIndex -= 1;\n }\n to.pathname = toSegments.join(\"/\");\n }\n // If there are more \"..\" segments than parent routes, resolve relative to\n // the root / URL.\n from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : \"/\";\n }\n let path = resolvePath(to, from);\n // Ensure the pathname has a trailing slash if the original \"to\" had one\n let hasExplicitTrailingSlash = toPathname && toPathname !== \"/\" && toPathname.endsWith(\"/\");\n // Or if this was a link to the current path which has a trailing slash\n let hasCurrentTrailingSlash = (isEmptyPath || toPathname === \".\") && locationPathname.endsWith(\"/\");\n if (!path.pathname.endsWith(\"/\") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) {\n path.pathname += \"/\";\n }\n return path;\n}\n/**\n * @private\n */\nfunction getToPathname(to) {\n // Empty strings should be treated the same as / paths\n return to === \"\" || to.pathname === \"\" ? \"/\" : typeof to === \"string\" ? parsePath(to).pathname : to.pathname;\n}\n/**\n * @private\n */\nconst joinPaths = paths => paths.join(\"/\").replace(/\\/\\/+/g, \"/\");\n/**\n * @private\n */\nconst normalizePathname = pathname => pathname.replace(/\\/+$/, \"\").replace(/^\\/*/, \"/\");\n/**\n * @private\n */\nconst normalizeSearch = search => !search || search === \"?\" ? \"\" : search.startsWith(\"?\") ? search : \"?\" + search;\n/**\n * @private\n */\nconst normalizeHash = hash => !hash || hash === \"#\" ? \"\" : hash.startsWith(\"#\") ? hash : \"#\" + hash;\n/**\n * This is a shortcut for creating `application/json` responses. Converts `data`\n * to JSON and sets the `Content-Type` header.\n */\nconst json = function json(data, init) {\n if (init === void 0) {\n init = {};\n }\n let responseInit = typeof init === \"number\" ? {\n status: init\n } : init;\n let headers = new Headers(responseInit.headers);\n if (!headers.has(\"Content-Type\")) {\n headers.set(\"Content-Type\", \"application/json; charset=utf-8\");\n }\n return new Response(JSON.stringify(data), _extends({}, responseInit, {\n headers\n }));\n};\nclass AbortedDeferredError extends Error {}\nclass DeferredData {\n constructor(data, responseInit) {\n this.pendingKeysSet = new Set();\n this.subscribers = new Set();\n this.deferredKeys = [];\n invariant(data && typeof data === \"object\" && !Array.isArray(data), \"defer() only accepts plain objects\");\n // Set up an AbortController + Promise we can race against to exit early\n // cancellation\n let reject;\n this.abortPromise = new Promise((_, r) => reject = r);\n this.controller = new AbortController();\n let onAbort = () => reject(new AbortedDeferredError(\"Deferred data aborted\"));\n this.unlistenAbortSignal = () => this.controller.signal.removeEventListener(\"abort\", onAbort);\n this.controller.signal.addEventListener(\"abort\", onAbort);\n this.data = Object.entries(data).reduce((acc, _ref) => {\n let [key, value] = _ref;\n return Object.assign(acc, {\n [key]: this.trackPromise(key, value)\n });\n }, {});\n if (this.done) {\n // All incoming values were resolved\n this.unlistenAbortSignal();\n }\n this.init = responseInit;\n }\n trackPromise(key, value) {\n if (!(value instanceof Promise)) {\n return value;\n }\n this.deferredKeys.push(key);\n this.pendingKeysSet.add(key);\n // We store a little wrapper promise that will be extended with\n // _data/_error props upon resolve/reject\n let promise = Promise.race([value, this.abortPromise]).then(data => this.onSettle(promise, key, null, data), error => this.onSettle(promise, key, error));\n // Register rejection listeners to avoid uncaught promise rejections on\n // errors or aborted deferred values\n promise.catch(() => {});\n Object.defineProperty(promise, \"_tracked\", {\n get: () => true\n });\n return promise;\n }\n onSettle(promise, key, error, data) {\n if (this.controller.signal.aborted && error instanceof AbortedDeferredError) {\n this.unlistenAbortSignal();\n Object.defineProperty(promise, \"_error\", {\n get: () => error\n });\n return Promise.reject(error);\n }\n this.pendingKeysSet.delete(key);\n if (this.done) {\n // Nothing left to abort!\n this.unlistenAbortSignal();\n }\n if (error) {\n Object.defineProperty(promise, \"_error\", {\n get: () => error\n });\n this.emit(false, key);\n return Promise.reject(error);\n }\n Object.defineProperty(promise, \"_data\", {\n get: () => data\n });\n this.emit(false, key);\n return data;\n }\n emit(aborted, settledKey) {\n this.subscribers.forEach(subscriber => subscriber(aborted, settledKey));\n }\n subscribe(fn) {\n this.subscribers.add(fn);\n return () => this.subscribers.delete(fn);\n }\n cancel() {\n this.controller.abort();\n this.pendingKeysSet.forEach((v, k) => this.pendingKeysSet.delete(k));\n this.emit(true);\n }\n async resolveData(signal) {\n let aborted = false;\n if (!this.done) {\n let onAbort = () => this.cancel();\n signal.addEventListener(\"abort\", onAbort);\n aborted = await new Promise(resolve => {\n this.subscribe(aborted => {\n signal.removeEventListener(\"abort\", onAbort);\n if (aborted || this.done) {\n resolve(aborted);\n }\n });\n });\n }\n return aborted;\n }\n get done() {\n return this.pendingKeysSet.size === 0;\n }\n get unwrappedData() {\n invariant(this.data !== null && this.done, \"Can only unwrap data on initialized and settled deferreds\");\n return Object.entries(this.data).reduce((acc, _ref2) => {\n let [key, value] = _ref2;\n return Object.assign(acc, {\n [key]: unwrapTrackedPromise(value)\n });\n }, {});\n }\n get pendingKeys() {\n return Array.from(this.pendingKeysSet);\n }\n}\nfunction isTrackedPromise(value) {\n return value instanceof Promise && value._tracked === true;\n}\nfunction unwrapTrackedPromise(value) {\n if (!isTrackedPromise(value)) {\n return value;\n }\n if (value._error) {\n throw value._error;\n }\n return value._data;\n}\nconst defer = function defer(data, init) {\n if (init === void 0) {\n init = {};\n }\n let responseInit = typeof init === \"number\" ? {\n status: init\n } : init;\n return new DeferredData(data, responseInit);\n};\n/**\n * A redirect response. Sets the status code and the `Location` header.\n * Defaults to \"302 Found\".\n */\nconst redirect = function redirect(url, init) {\n if (init === void 0) {\n init = 302;\n }\n let responseInit = init;\n if (typeof responseInit === \"number\") {\n responseInit = {\n status: responseInit\n };\n } else if (typeof responseInit.status === \"undefined\") {\n responseInit.status = 302;\n }\n let headers = new Headers(responseInit.headers);\n headers.set(\"Location\", url);\n return new Response(null, _extends({}, responseInit, {\n headers\n }));\n};\n/**\n * @private\n * Utility class we use to hold auto-unwrapped 4xx/5xx Response bodies\n */\nclass ErrorResponse {\n constructor(status, statusText, data, internal) {\n if (internal === void 0) {\n internal = false;\n }\n this.status = status;\n this.statusText = statusText || \"\";\n this.internal = internal;\n if (data instanceof Error) {\n this.data = data.toString();\n this.error = data;\n } else {\n this.data = data;\n }\n }\n}\n/**\n * Check if the given error is an ErrorResponse generated from a 4xx/5xx\n * Response thrown from an action/loader\n */\nfunction isRouteErrorResponse(error) {\n return error != null && typeof error.status === \"number\" && typeof error.statusText === \"string\" && typeof error.internal === \"boolean\" && \"data\" in error;\n}\n\nconst validMutationMethodsArr = [\"post\", \"put\", \"patch\", \"delete\"];\nconst validMutationMethods = new Set(validMutationMethodsArr);\nconst validRequestMethodsArr = [\"get\", ...validMutationMethodsArr];\nconst validRequestMethods = new Set(validRequestMethodsArr);\nconst redirectStatusCodes = new Set([301, 302, 303, 307, 308]);\nconst redirectPreserveMethodStatusCodes = new Set([307, 308]);\nconst IDLE_NAVIGATION = {\n state: \"idle\",\n location: undefined,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined\n};\nconst IDLE_FETCHER = {\n state: \"idle\",\n data: undefined,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined\n};\nconst IDLE_BLOCKER = {\n state: \"unblocked\",\n proceed: undefined,\n reset: undefined,\n location: undefined\n};\nconst ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i;\nconst defaultMapRouteProperties = route => ({\n hasErrorBoundary: Boolean(route.hasErrorBoundary)\n});\n//#endregion\n////////////////////////////////////////////////////////////////////////////////\n//#region createRouter\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Create a router and listen to history POP navigations\n */\nfunction createRouter(init) {\n const routerWindow = init.window ? init.window : typeof window !== \"undefined\" ? window : undefined;\n const isBrowser = typeof routerWindow !== \"undefined\" && typeof routerWindow.document !== \"undefined\" && typeof routerWindow.document.createElement !== \"undefined\";\n const isServer = !isBrowser;\n invariant(init.routes.length > 0, \"You must provide a non-empty routes array to createRouter\");\n let mapRouteProperties;\n if (init.mapRouteProperties) {\n mapRouteProperties = init.mapRouteProperties;\n } else if (init.detectErrorBoundary) {\n // If they are still using the deprecated version, wrap it with the new API\n let detectErrorBoundary = init.detectErrorBoundary;\n mapRouteProperties = route => ({\n hasErrorBoundary: detectErrorBoundary(route)\n });\n } else {\n mapRouteProperties = defaultMapRouteProperties;\n }\n // Routes keyed by ID\n let manifest = {};\n // Routes in tree format for matching\n let dataRoutes = convertRoutesToDataRoutes(init.routes, mapRouteProperties, undefined, manifest);\n let inFlightDataRoutes;\n let basename = init.basename || \"/\";\n // Config driven behavior flags\n let future = _extends({\n v7_normalizeFormMethod: false,\n v7_prependBasename: false\n }, init.future);\n // Cleanup function for history\n let unlistenHistory = null;\n // Externally-provided functions to call on all state changes\n let subscribers = new Set();\n // Externally-provided object to hold scroll restoration locations during routing\n let savedScrollPositions = null;\n // Externally-provided function to get scroll restoration keys\n let getScrollRestorationKey = null;\n // Externally-provided function to get current scroll position\n let getScrollPosition = null;\n // One-time flag to control the initial hydration scroll restoration. Because\n // we don't get the saved positions from until _after_\n // the initial render, we need to manually trigger a separate updateState to\n // send along the restoreScrollPosition\n // Set to true if we have `hydrationData` since we assume we were SSR'd and that\n // SSR did the initial scroll restoration.\n let initialScrollRestored = init.hydrationData != null;\n let initialMatches = matchRoutes(dataRoutes, init.history.location, basename);\n let initialErrors = null;\n if (initialMatches == null) {\n // If we do not match a user-provided-route, fall back to the root\n // to allow the error boundary to take over\n let error = getInternalRouterError(404, {\n pathname: init.history.location.pathname\n });\n let {\n matches,\n route\n } = getShortCircuitMatches(dataRoutes);\n initialMatches = matches;\n initialErrors = {\n [route.id]: error\n };\n }\n let initialized =\n // All initialMatches need to be loaded before we're ready. If we have lazy\n // functions around still then we'll need to run them in initialize()\n !initialMatches.some(m => m.route.lazy) && (\n // And we have to either have no loaders or have been provided hydrationData\n !initialMatches.some(m => m.route.loader) || init.hydrationData != null);\n let router;\n let state = {\n historyAction: init.history.action,\n location: init.history.location,\n matches: initialMatches,\n initialized,\n navigation: IDLE_NAVIGATION,\n // Don't restore on initial updateState() if we were SSR'd\n restoreScrollPosition: init.hydrationData != null ? false : null,\n preventScrollReset: false,\n revalidation: \"idle\",\n loaderData: init.hydrationData && init.hydrationData.loaderData || {},\n actionData: init.hydrationData && init.hydrationData.actionData || null,\n errors: init.hydrationData && init.hydrationData.errors || initialErrors,\n fetchers: new Map(),\n blockers: new Map()\n };\n // -- Stateful internal variables to manage navigations --\n // Current navigation in progress (to be committed in completeNavigation)\n let pendingAction = Action.Pop;\n // Should the current navigation prevent the scroll reset if scroll cannot\n // be restored?\n let pendingPreventScrollReset = false;\n // AbortController for the active navigation\n let pendingNavigationController;\n // We use this to avoid touching history in completeNavigation if a\n // revalidation is entirely uninterrupted\n let isUninterruptedRevalidation = false;\n // Use this internal flag to force revalidation of all loaders:\n // - submissions (completed or interrupted)\n // - useRevalidator()\n // - X-Remix-Revalidate (from redirect)\n let isRevalidationRequired = false;\n // Use this internal array to capture routes that require revalidation due\n // to a cancelled deferred on action submission\n let cancelledDeferredRoutes = [];\n // Use this internal array to capture fetcher loads that were cancelled by an\n // action navigation and require revalidation\n let cancelledFetcherLoads = [];\n // AbortControllers for any in-flight fetchers\n let fetchControllers = new Map();\n // Track loads based on the order in which they started\n let incrementingLoadId = 0;\n // Track the outstanding pending navigation data load to be compared against\n // the globally incrementing load when a fetcher load lands after a completed\n // navigation\n let pendingNavigationLoadId = -1;\n // Fetchers that triggered data reloads as a result of their actions\n let fetchReloadIds = new Map();\n // Fetchers that triggered redirect navigations\n let fetchRedirectIds = new Set();\n // Most recent href/match for fetcher.load calls for fetchers\n let fetchLoadMatches = new Map();\n // Store DeferredData instances for active route matches. When a\n // route loader returns defer() we stick one in here. Then, when a nested\n // promise resolves we update loaderData. If a new navigation starts we\n // cancel active deferreds for eliminated routes.\n let activeDeferreds = new Map();\n // Store blocker functions in a separate Map outside of router state since\n // we don't need to update UI state if they change\n let blockerFunctions = new Map();\n // Flag to ignore the next history update, so we can revert the URL change on\n // a POP navigation that was blocked by the user without touching router state\n let ignoreNextHistoryUpdate = false;\n // Initialize the router, all side effects should be kicked off from here.\n // Implemented as a Fluent API for ease of:\n // let router = createRouter(init).initialize();\n function initialize() {\n // If history informs us of a POP navigation, start the navigation but do not update\n // state. We'll update our own state once the navigation completes\n unlistenHistory = init.history.listen(_ref => {\n let {\n action: historyAction,\n location,\n delta\n } = _ref;\n // Ignore this event if it was just us resetting the URL from a\n // blocked POP navigation\n if (ignoreNextHistoryUpdate) {\n ignoreNextHistoryUpdate = false;\n return;\n }\n warning(blockerFunctions.size === 0 || delta != null, \"You are trying to use a blocker on a POP navigation to a location \" + \"that was not created by @remix-run/router. This will fail silently in \" + \"production. This can happen if you are navigating outside the router \" + \"via `window.history.pushState`/`window.location.hash` instead of using \" + \"router navigation APIs. This can also happen if you are using \" + \"createHashRouter and the user manually changes the URL.\");\n let blockerKey = shouldBlockNavigation({\n currentLocation: state.location,\n nextLocation: location,\n historyAction\n });\n if (blockerKey && delta != null) {\n // Restore the URL to match the current UI, but don't update router state\n ignoreNextHistoryUpdate = true;\n init.history.go(delta * -1);\n // Put the blocker into a blocked state\n updateBlocker(blockerKey, {\n state: \"blocked\",\n location,\n proceed() {\n updateBlocker(blockerKey, {\n state: \"proceeding\",\n proceed: undefined,\n reset: undefined,\n location\n });\n // Re-do the same POP navigation we just blocked\n init.history.go(delta);\n },\n reset() {\n deleteBlocker(blockerKey);\n updateState({\n blockers: new Map(router.state.blockers)\n });\n }\n });\n return;\n }\n return startNavigation(historyAction, location);\n });\n // Kick off initial data load if needed. Use Pop to avoid modifying history\n // Note we don't do any handling of lazy here. For SPA's it'll get handled\n // in the normal navigation flow. For SSR it's expected that lazy modules are\n // resolved prior to router creation since we can't go into a fallbackElement\n // UI for SSR'd apps\n if (!state.initialized) {\n startNavigation(Action.Pop, state.location);\n }\n return router;\n }\n // Clean up a router and it's side effects\n function dispose() {\n if (unlistenHistory) {\n unlistenHistory();\n }\n subscribers.clear();\n pendingNavigationController && pendingNavigationController.abort();\n state.fetchers.forEach((_, key) => deleteFetcher(key));\n state.blockers.forEach((_, key) => deleteBlocker(key));\n }\n // Subscribe to state updates for the router\n function subscribe(fn) {\n subscribers.add(fn);\n return () => subscribers.delete(fn);\n }\n // Update our state and notify the calling context of the change\n function updateState(newState) {\n state = _extends({}, state, newState);\n subscribers.forEach(subscriber => subscriber(state));\n }\n // Complete a navigation returning the state.navigation back to the IDLE_NAVIGATION\n // and setting state.[historyAction/location/matches] to the new route.\n // - Location is a required param\n // - Navigation will always be set to IDLE_NAVIGATION\n // - Can pass any other state in newState\n function completeNavigation(location, newState) {\n var _location$state, _location$state2;\n // Deduce if we're in a loading/actionReload state:\n // - We have committed actionData in the store\n // - The current navigation was a mutation submission\n // - We're past the submitting state and into the loading state\n // - The location being loaded is not the result of a redirect\n let isActionReload = state.actionData != null && state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && state.navigation.state === \"loading\" && ((_location$state = location.state) == null ? void 0 : _location$state._isRedirect) !== true;\n let actionData;\n if (newState.actionData) {\n if (Object.keys(newState.actionData).length > 0) {\n actionData = newState.actionData;\n } else {\n // Empty actionData -> clear prior actionData due to an action error\n actionData = null;\n }\n } else if (isActionReload) {\n // Keep the current data if we're wrapping up the action reload\n actionData = state.actionData;\n } else {\n // Clear actionData on any other completed navigations\n actionData = null;\n }\n // Always preserve any existing loaderData from re-used routes\n let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;\n // On a successful navigation we can assume we got through all blockers\n // so we can start fresh\n for (let [key] of blockerFunctions) {\n deleteBlocker(key);\n }\n // Always respect the user flag. Otherwise don't reset on mutation\n // submission navigations unless they redirect\n let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true;\n if (inFlightDataRoutes) {\n dataRoutes = inFlightDataRoutes;\n inFlightDataRoutes = undefined;\n }\n updateState(_extends({}, newState, {\n actionData,\n loaderData,\n historyAction: pendingAction,\n location,\n initialized: true,\n navigation: IDLE_NAVIGATION,\n revalidation: \"idle\",\n restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),\n preventScrollReset,\n blockers: new Map(state.blockers)\n }));\n if (isUninterruptedRevalidation) ; else if (pendingAction === Action.Pop) ; else if (pendingAction === Action.Push) {\n init.history.push(location, location.state);\n } else if (pendingAction === Action.Replace) {\n init.history.replace(location, location.state);\n }\n // Reset stateful navigation vars\n pendingAction = Action.Pop;\n pendingPreventScrollReset = false;\n isUninterruptedRevalidation = false;\n isRevalidationRequired = false;\n cancelledDeferredRoutes = [];\n cancelledFetcherLoads = [];\n }\n // Trigger a navigation event, which can either be a numerical POP or a PUSH\n // replace with an optional submission\n async function navigate(to, opts) {\n if (typeof to === \"number\") {\n init.history.go(to);\n return;\n }\n let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, to, opts == null ? void 0 : opts.fromRouteId, opts == null ? void 0 : opts.relative);\n let {\n path,\n submission,\n error\n } = normalizeNavigateOptions(future.v7_normalizeFormMethod, false, normalizedPath, opts);\n let currentLocation = state.location;\n let nextLocation = createLocation(state.location, path, opts && opts.state);\n // When using navigate as a PUSH/REPLACE we aren't reading an already-encoded\n // URL from window.location, so we need to encode it here so the behavior\n // remains the same as POP and non-data-router usages. new URL() does all\n // the same encoding we'd get from a history.pushState/window.location read\n // without having to touch history\n nextLocation = _extends({}, nextLocation, init.history.encodeLocation(nextLocation));\n let userReplace = opts && opts.replace != null ? opts.replace : undefined;\n let historyAction = Action.Push;\n if (userReplace === true) {\n historyAction = Action.Replace;\n } else if (userReplace === false) ; else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) {\n // By default on submissions to the current location we REPLACE so that\n // users don't have to double-click the back button to get to the prior\n // location. If the user redirects to a different location from the\n // action/loader this will be ignored and the redirect will be a PUSH\n historyAction = Action.Replace;\n }\n let preventScrollReset = opts && \"preventScrollReset\" in opts ? opts.preventScrollReset === true : undefined;\n let blockerKey = shouldBlockNavigation({\n currentLocation,\n nextLocation,\n historyAction\n });\n if (blockerKey) {\n // Put the blocker into a blocked state\n updateBlocker(blockerKey, {\n state: \"blocked\",\n location: nextLocation,\n proceed() {\n updateBlocker(blockerKey, {\n state: \"proceeding\",\n proceed: undefined,\n reset: undefined,\n location: nextLocation\n });\n // Send the same navigation through\n navigate(to, opts);\n },\n reset() {\n deleteBlocker(blockerKey);\n updateState({\n blockers: new Map(state.blockers)\n });\n }\n });\n return;\n }\n return await startNavigation(historyAction, nextLocation, {\n submission,\n // Send through the formData serialization error if we have one so we can\n // render at the right error boundary after we match routes\n pendingError: error,\n preventScrollReset,\n replace: opts && opts.replace\n });\n }\n // Revalidate all current loaders. If a navigation is in progress or if this\n // is interrupted by a navigation, allow this to \"succeed\" by calling all\n // loaders during the next loader round\n function revalidate() {\n interruptActiveLoads();\n updateState({\n revalidation: \"loading\"\n });\n // If we're currently submitting an action, we don't need to start a new\n // navigation, we'll just let the follow up loader execution call all loaders\n if (state.navigation.state === \"submitting\") {\n return;\n }\n // If we're currently in an idle state, start a new navigation for the current\n // action/location and mark it as uninterrupted, which will skip the history\n // update in completeNavigation\n if (state.navigation.state === \"idle\") {\n startNavigation(state.historyAction, state.location, {\n startUninterruptedRevalidation: true\n });\n return;\n }\n // Otherwise, if we're currently in a loading state, just start a new\n // navigation to the navigation.location but do not trigger an uninterrupted\n // revalidation so that history correctly updates once the navigation completes\n startNavigation(pendingAction || state.historyAction, state.navigation.location, {\n overrideNavigation: state.navigation\n });\n }\n // Start a navigation to the given action/location. Can optionally provide a\n // overrideNavigation which will override the normalLoad in the case of a redirect\n // navigation\n async function startNavigation(historyAction, location, opts) {\n // Abort any in-progress navigations and start a new one. Unset any ongoing\n // uninterrupted revalidations unless told otherwise, since we want this\n // new navigation to update history normally\n pendingNavigationController && pendingNavigationController.abort();\n pendingNavigationController = null;\n pendingAction = historyAction;\n isUninterruptedRevalidation = (opts && opts.startUninterruptedRevalidation) === true;\n // Save the current scroll position every time we start a new navigation,\n // and track whether we should reset scroll on completion\n saveScrollPosition(state.location, state.matches);\n pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;\n let routesToUse = inFlightDataRoutes || dataRoutes;\n let loadingNavigation = opts && opts.overrideNavigation;\n let matches = matchRoutes(routesToUse, location, basename);\n // Short circuit with a 404 on the root error boundary if we match nothing\n if (!matches) {\n let error = getInternalRouterError(404, {\n pathname: location.pathname\n });\n let {\n matches: notFoundMatches,\n route\n } = getShortCircuitMatches(routesToUse);\n // Cancel all pending deferred on 404s since we don't keep any routes\n cancelActiveDeferreds();\n completeNavigation(location, {\n matches: notFoundMatches,\n loaderData: {},\n errors: {\n [route.id]: error\n }\n });\n return;\n }\n // Short circuit if it's only a hash change and not a revalidation or\n // mutation submission.\n //\n // Ignore on initial page loads because since the initial load will always\n // be \"same hash\". For example, on /page#hash and submit a
\n // which will default to a navigation to /page\n if (state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {\n completeNavigation(location, {\n matches\n });\n return;\n }\n // Create a controller/Request for this navigation\n pendingNavigationController = new AbortController();\n let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);\n let pendingActionData;\n let pendingError;\n if (opts && opts.pendingError) {\n // If we have a pendingError, it means the user attempted a GET submission\n // with binary FormData so assign here and skip to handleLoaders. That\n // way we handle calling loaders above the boundary etc. It's not really\n // different from an actionError in that sense.\n pendingError = {\n [findNearestBoundary(matches).route.id]: opts.pendingError\n };\n } else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {\n // Call action if we received an action submission\n let actionOutput = await handleAction(request, location, opts.submission, matches, {\n replace: opts.replace\n });\n if (actionOutput.shortCircuited) {\n return;\n }\n pendingActionData = actionOutput.pendingActionData;\n pendingError = actionOutput.pendingActionError;\n let navigation = _extends({\n state: \"loading\",\n location\n }, opts.submission);\n loadingNavigation = navigation;\n // Create a GET request for the loaders\n request = new Request(request.url, {\n signal: request.signal\n });\n }\n // Call loaders\n let {\n shortCircuited,\n loaderData,\n errors\n } = await handleLoaders(request, location, matches, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, pendingActionData, pendingError);\n if (shortCircuited) {\n return;\n }\n // Clean up now that the action/loaders have completed. Don't clean up if\n // we short circuited because pendingNavigationController will have already\n // been assigned to a new controller for the next navigation\n pendingNavigationController = null;\n completeNavigation(location, _extends({\n matches\n }, pendingActionData ? {\n actionData: pendingActionData\n } : {}, {\n loaderData,\n errors\n }));\n }\n // Call the action matched by the leaf route for this navigation and handle\n // redirects/errors\n async function handleAction(request, location, submission, matches, opts) {\n interruptActiveLoads();\n // Put us in a submitting state\n let navigation = _extends({\n state: \"submitting\",\n location\n }, submission);\n updateState({\n navigation\n });\n // Call our action and get the result\n let result;\n let actionMatch = getTargetMatch(matches, location);\n if (!actionMatch.route.action && !actionMatch.route.lazy) {\n result = {\n type: ResultType.error,\n error: getInternalRouterError(405, {\n method: request.method,\n pathname: location.pathname,\n routeId: actionMatch.route.id\n })\n };\n } else {\n result = await callLoaderOrAction(\"action\", request, actionMatch, matches, manifest, mapRouteProperties, basename);\n if (request.signal.aborted) {\n return {\n shortCircuited: true\n };\n }\n }\n if (isRedirectResult(result)) {\n let replace;\n if (opts && opts.replace != null) {\n replace = opts.replace;\n } else {\n // If the user didn't explicity indicate replace behavior, replace if\n // we redirected to the exact same location we're currently at to avoid\n // double back-buttons\n replace = result.location === state.location.pathname + state.location.search;\n }\n await startRedirectNavigation(state, result, {\n submission,\n replace\n });\n return {\n shortCircuited: true\n };\n }\n if (isErrorResult(result)) {\n // Store off the pending error - we use it to determine which loaders\n // to call and will commit it when we complete the navigation\n let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);\n // By default, all submissions are REPLACE navigations, but if the\n // action threw an error that'll be rendered in an errorElement, we fall\n // back to PUSH so that the user can use the back button to get back to\n // the pre-submission form location to try again\n if ((opts && opts.replace) !== true) {\n pendingAction = Action.Push;\n }\n return {\n // Send back an empty object we can use to clear out any prior actionData\n pendingActionData: {},\n pendingActionError: {\n [boundaryMatch.route.id]: result.error\n }\n };\n }\n if (isDeferredResult(result)) {\n throw getInternalRouterError(400, {\n type: \"defer-action\"\n });\n }\n return {\n pendingActionData: {\n [actionMatch.route.id]: result.data\n }\n };\n }\n // Call all applicable loaders for the given matches, handling redirects,\n // errors, etc.\n async function handleLoaders(request, location, matches, overrideNavigation, submission, fetcherSubmission, replace, pendingActionData, pendingError) {\n // Figure out the right navigation we want to use for data loading\n let loadingNavigation = overrideNavigation;\n if (!loadingNavigation) {\n let navigation = _extends({\n state: \"loading\",\n location,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined\n }, submission);\n loadingNavigation = navigation;\n }\n // If this was a redirect from an action we don't have a \"submission\" but\n // we have it on the loading navigation so use that if available\n let activeSubmission = submission || fetcherSubmission ? submission || fetcherSubmission : loadingNavigation.formMethod && loadingNavigation.formAction && loadingNavigation.formData && loadingNavigation.formEncType ? {\n formMethod: loadingNavigation.formMethod,\n formAction: loadingNavigation.formAction,\n formData: loadingNavigation.formData,\n formEncType: loadingNavigation.formEncType\n } : undefined;\n let routesToUse = inFlightDataRoutes || dataRoutes;\n let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, basename, pendingActionData, pendingError);\n // Cancel pending deferreds for no-longer-matched routes or routes we're\n // about to reload. Note that if this is an action reload we would have\n // already cancelled all pending deferreds so this would be a no-op\n cancelActiveDeferreds(routeId => !(matches && matches.some(m => m.route.id === routeId)) || matchesToLoad && matchesToLoad.some(m => m.route.id === routeId));\n // Short circuit if we have no loaders to run\n if (matchesToLoad.length === 0 && revalidatingFetchers.length === 0) {\n let updatedFetchers = markFetchRedirectsDone();\n completeNavigation(location, _extends({\n matches,\n loaderData: {},\n // Commit pending error if we're short circuiting\n errors: pendingError || null\n }, pendingActionData ? {\n actionData: pendingActionData\n } : {}, updatedFetchers ? {\n fetchers: new Map(state.fetchers)\n } : {}));\n return {\n shortCircuited: true\n };\n }\n // If this is an uninterrupted revalidation, we remain in our current idle\n // state. If not, we need to switch to our loading state and load data,\n // preserving any new action data or existing action data (in the case of\n // a revalidation interrupting an actionReload)\n if (!isUninterruptedRevalidation) {\n revalidatingFetchers.forEach(rf => {\n let fetcher = state.fetchers.get(rf.key);\n let revalidatingFetcher = {\n state: \"loading\",\n data: fetcher && fetcher.data,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n \" _hasFetcherDoneAnything \": true\n };\n state.fetchers.set(rf.key, revalidatingFetcher);\n });\n let actionData = pendingActionData || state.actionData;\n updateState(_extends({\n navigation: loadingNavigation\n }, actionData ? Object.keys(actionData).length === 0 ? {\n actionData: null\n } : {\n actionData\n } : {}, revalidatingFetchers.length > 0 ? {\n fetchers: new Map(state.fetchers)\n } : {}));\n }\n pendingNavigationLoadId = ++incrementingLoadId;\n revalidatingFetchers.forEach(rf => {\n if (rf.controller) {\n // Fetchers use an independent AbortController so that aborting a fetcher\n // (via deleteFetcher) does not abort the triggering navigation that\n // triggered the revalidation\n fetchControllers.set(rf.key, rf.controller);\n }\n });\n // Proxy navigation abort through to revalidation fetchers\n let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach(f => abortFetcher(f.key));\n if (pendingNavigationController) {\n pendingNavigationController.signal.addEventListener(\"abort\", abortPendingFetchRevalidations);\n }\n let {\n results,\n loaderResults,\n fetcherResults\n } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request);\n if (request.signal.aborted) {\n return {\n shortCircuited: true\n };\n }\n // Clean up _after_ loaders have completed. Don't clean up if we short\n // circuited because fetchControllers would have been aborted and\n // reassigned to new controllers for the next navigation\n if (pendingNavigationController) {\n pendingNavigationController.signal.removeEventListener(\"abort\", abortPendingFetchRevalidations);\n }\n revalidatingFetchers.forEach(rf => fetchControllers.delete(rf.key));\n // If any loaders returned a redirect Response, start a new REPLACE navigation\n let redirect = findRedirect(results);\n if (redirect) {\n await startRedirectNavigation(state, redirect, {\n replace\n });\n return {\n shortCircuited: true\n };\n }\n // Process and commit output from loaders\n let {\n loaderData,\n errors\n } = processLoaderData(state, matches, matchesToLoad, loaderResults, pendingError, revalidatingFetchers, fetcherResults, activeDeferreds);\n // Wire up subscribers to update loaderData as promises settle\n activeDeferreds.forEach((deferredData, routeId) => {\n deferredData.subscribe(aborted => {\n // Note: No need to updateState here since the TrackedPromise on\n // loaderData is stable across resolve/reject\n // Remove this instance if we were aborted or if promises have settled\n if (aborted || deferredData.done) {\n activeDeferreds.delete(routeId);\n }\n });\n });\n let updatedFetchers = markFetchRedirectsDone();\n let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId);\n let shouldUpdateFetchers = updatedFetchers || didAbortFetchLoads || revalidatingFetchers.length > 0;\n return _extends({\n loaderData,\n errors\n }, shouldUpdateFetchers ? {\n fetchers: new Map(state.fetchers)\n } : {});\n }\n function getFetcher(key) {\n return state.fetchers.get(key) || IDLE_FETCHER;\n }\n // Trigger a fetcher load/submit for the given fetcher key\n function fetch(key, routeId, href, opts) {\n if (isServer) {\n throw new Error(\"router.fetch() was called during the server render, but it shouldn't be. \" + \"You are likely calling a useFetcher() method in the body of your component. \" + \"Try moving it to a useEffect or a callback.\");\n }\n if (fetchControllers.has(key)) abortFetcher(key);\n let routesToUse = inFlightDataRoutes || dataRoutes;\n let normalizedPath = normalizeTo(state.location, state.matches, basename, future.v7_prependBasename, href, routeId, opts == null ? void 0 : opts.relative);\n let matches = matchRoutes(routesToUse, normalizedPath, basename);\n if (!matches) {\n setFetcherError(key, routeId, getInternalRouterError(404, {\n pathname: normalizedPath\n }));\n return;\n }\n let {\n path,\n submission\n } = normalizeNavigateOptions(future.v7_normalizeFormMethod, true, normalizedPath, opts);\n let match = getTargetMatch(matches, path);\n pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;\n if (submission && isMutationMethod(submission.formMethod)) {\n handleFetcherAction(key, routeId, path, match, matches, submission);\n return;\n }\n // Store off the match so we can call it's shouldRevalidate on subsequent\n // revalidations\n fetchLoadMatches.set(key, {\n routeId,\n path\n });\n handleFetcherLoader(key, routeId, path, match, matches, submission);\n }\n // Call the action for the matched fetcher.submit(), and then handle redirects,\n // errors, and revalidation\n async function handleFetcherAction(key, routeId, path, match, requestMatches, submission) {\n interruptActiveLoads();\n fetchLoadMatches.delete(key);\n if (!match.route.action && !match.route.lazy) {\n let error = getInternalRouterError(405, {\n method: submission.formMethod,\n pathname: path,\n routeId: routeId\n });\n setFetcherError(key, routeId, error);\n return;\n }\n // Put this fetcher into it's submitting state\n let existingFetcher = state.fetchers.get(key);\n let fetcher = _extends({\n state: \"submitting\"\n }, submission, {\n data: existingFetcher && existingFetcher.data,\n \" _hasFetcherDoneAnything \": true\n });\n state.fetchers.set(key, fetcher);\n updateState({\n fetchers: new Map(state.fetchers)\n });\n // Call the action for the fetcher\n let abortController = new AbortController();\n let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);\n fetchControllers.set(key, abortController);\n let actionResult = await callLoaderOrAction(\"action\", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename);\n if (fetchRequest.signal.aborted) {\n // We can delete this so long as we weren't aborted by ou our own fetcher\n // re-submit which would have put _new_ controller is in fetchControllers\n if (fetchControllers.get(key) === abortController) {\n fetchControllers.delete(key);\n }\n return;\n }\n if (isRedirectResult(actionResult)) {\n fetchControllers.delete(key);\n fetchRedirectIds.add(key);\n let loadingFetcher = _extends({\n state: \"loading\"\n }, submission, {\n data: undefined,\n \" _hasFetcherDoneAnything \": true\n });\n state.fetchers.set(key, loadingFetcher);\n updateState({\n fetchers: new Map(state.fetchers)\n });\n return startRedirectNavigation(state, actionResult, {\n submission,\n isFetchActionRedirect: true\n });\n }\n // Process any non-redirect errors thrown\n if (isErrorResult(actionResult)) {\n setFetcherError(key, routeId, actionResult.error);\n return;\n }\n if (isDeferredResult(actionResult)) {\n throw getInternalRouterError(400, {\n type: \"defer-action\"\n });\n }\n // Start the data load for current matches, or the next location if we're\n // in the middle of a navigation\n let nextLocation = state.navigation.location || state.location;\n let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);\n let routesToUse = inFlightDataRoutes || dataRoutes;\n let matches = state.navigation.state !== \"idle\" ? matchRoutes(routesToUse, state.navigation.location, basename) : state.matches;\n invariant(matches, \"Didn't find any matches after fetcher action\");\n let loadId = ++incrementingLoadId;\n fetchReloadIds.set(key, loadId);\n let loadFetcher = _extends({\n state: \"loading\",\n data: actionResult.data\n }, submission, {\n \" _hasFetcherDoneAnything \": true\n });\n state.fetchers.set(key, loadFetcher);\n let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, basename, {\n [match.route.id]: actionResult.data\n }, undefined // No need to send through errors since we short circuit above\n );\n // Put all revalidating fetchers into the loading state, except for the\n // current fetcher which we want to keep in it's current loading state which\n // contains it's action submission info + action data\n revalidatingFetchers.filter(rf => rf.key !== key).forEach(rf => {\n let staleKey = rf.key;\n let existingFetcher = state.fetchers.get(staleKey);\n let revalidatingFetcher = {\n state: \"loading\",\n data: existingFetcher && existingFetcher.data,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n \" _hasFetcherDoneAnything \": true\n };\n state.fetchers.set(staleKey, revalidatingFetcher);\n if (rf.controller) {\n fetchControllers.set(staleKey, rf.controller);\n }\n });\n updateState({\n fetchers: new Map(state.fetchers)\n });\n let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach(rf => abortFetcher(rf.key));\n abortController.signal.addEventListener(\"abort\", abortPendingFetchRevalidations);\n let {\n results,\n loaderResults,\n fetcherResults\n } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);\n if (abortController.signal.aborted) {\n return;\n }\n abortController.signal.removeEventListener(\"abort\", abortPendingFetchRevalidations);\n fetchReloadIds.delete(key);\n fetchControllers.delete(key);\n revalidatingFetchers.forEach(r => fetchControllers.delete(r.key));\n let redirect = findRedirect(results);\n if (redirect) {\n return startRedirectNavigation(state, redirect);\n }\n // Process and commit output from loaders\n let {\n loaderData,\n errors\n } = processLoaderData(state, state.matches, matchesToLoad, loaderResults, undefined, revalidatingFetchers, fetcherResults, activeDeferreds);\n // Since we let revalidations complete even if the submitting fetcher was\n // deleted, only put it back to idle if it hasn't been deleted\n if (state.fetchers.has(key)) {\n let doneFetcher = {\n state: \"idle\",\n data: actionResult.data,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n \" _hasFetcherDoneAnything \": true\n };\n state.fetchers.set(key, doneFetcher);\n }\n let didAbortFetchLoads = abortStaleFetchLoads(loadId);\n // If we are currently in a navigation loading state and this fetcher is\n // more recent than the navigation, we want the newer data so abort the\n // navigation and complete it with the fetcher data\n if (state.navigation.state === \"loading\" && loadId > pendingNavigationLoadId) {\n invariant(pendingAction, \"Expected pending action\");\n pendingNavigationController && pendingNavigationController.abort();\n completeNavigation(state.navigation.location, {\n matches,\n loaderData,\n errors,\n fetchers: new Map(state.fetchers)\n });\n } else {\n // otherwise just update with the fetcher data, preserving any existing\n // loaderData for loaders that did not need to reload. We have to\n // manually merge here since we aren't going through completeNavigation\n updateState(_extends({\n errors,\n loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors)\n }, didAbortFetchLoads || revalidatingFetchers.length > 0 ? {\n fetchers: new Map(state.fetchers)\n } : {}));\n isRevalidationRequired = false;\n }\n }\n // Call the matched loader for fetcher.load(), handling redirects, errors, etc.\n async function handleFetcherLoader(key, routeId, path, match, matches, submission) {\n let existingFetcher = state.fetchers.get(key);\n // Put this fetcher into it's loading state\n let loadingFetcher = _extends({\n state: \"loading\",\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined\n }, submission, {\n data: existingFetcher && existingFetcher.data,\n \" _hasFetcherDoneAnything \": true\n });\n state.fetchers.set(key, loadingFetcher);\n updateState({\n fetchers: new Map(state.fetchers)\n });\n // Call the loader for this fetcher route match\n let abortController = new AbortController();\n let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);\n fetchControllers.set(key, abortController);\n let result = await callLoaderOrAction(\"loader\", fetchRequest, match, matches, manifest, mapRouteProperties, basename);\n // Deferred isn't supported for fetcher loads, await everything and treat it\n // as a normal load. resolveDeferredData will return undefined if this\n // fetcher gets aborted, so we just leave result untouched and short circuit\n // below if that happens\n if (isDeferredResult(result)) {\n result = (await resolveDeferredData(result, fetchRequest.signal, true)) || result;\n }\n // We can delete this so long as we weren't aborted by our our own fetcher\n // re-load which would have put _new_ controller is in fetchControllers\n if (fetchControllers.get(key) === abortController) {\n fetchControllers.delete(key);\n }\n if (fetchRequest.signal.aborted) {\n return;\n }\n // If the loader threw a redirect Response, start a new REPLACE navigation\n if (isRedirectResult(result)) {\n fetchRedirectIds.add(key);\n await startRedirectNavigation(state, result);\n return;\n }\n // Process any non-redirect errors thrown\n if (isErrorResult(result)) {\n let boundaryMatch = findNearestBoundary(state.matches, routeId);\n state.fetchers.delete(key);\n // TODO: In remix, this would reset to IDLE_NAVIGATION if it was a catch -\n // do we need to behave any differently with our non-redirect errors?\n // What if it was a non-redirect Response?\n updateState({\n fetchers: new Map(state.fetchers),\n errors: {\n [boundaryMatch.route.id]: result.error\n }\n });\n return;\n }\n invariant(!isDeferredResult(result), \"Unhandled fetcher deferred data\");\n // Put the fetcher back into an idle state\n let doneFetcher = {\n state: \"idle\",\n data: result.data,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n \" _hasFetcherDoneAnything \": true\n };\n state.fetchers.set(key, doneFetcher);\n updateState({\n fetchers: new Map(state.fetchers)\n });\n }\n /**\n * Utility function to handle redirects returned from an action or loader.\n * Normally, a redirect \"replaces\" the navigation that triggered it. So, for\n * example:\n *\n * - user is on /a\n * - user clicks a link to /b\n * - loader for /b redirects to /c\n *\n * In a non-JS app the browser would track the in-flight navigation to /b and\n * then replace it with /c when it encountered the redirect response. In\n * the end it would only ever update the URL bar with /c.\n *\n * In client-side routing using pushState/replaceState, we aim to emulate\n * this behavior and we also do not update history until the end of the\n * navigation (including processed redirects). This means that we never\n * actually touch history until we've processed redirects, so we just use\n * the history action from the original navigation (PUSH or REPLACE).\n */\n async function startRedirectNavigation(state, redirect, _temp) {\n let {\n submission,\n replace,\n isFetchActionRedirect\n } = _temp === void 0 ? {} : _temp;\n if (redirect.revalidate) {\n isRevalidationRequired = true;\n }\n let redirectLocation = createLocation(state.location, redirect.location, // TODO: This can be removed once we get rid of useTransition in Remix v2\n _extends({\n _isRedirect: true\n }, isFetchActionRedirect ? {\n _isFetchActionRedirect: true\n } : {}));\n invariant(redirectLocation, \"Expected a location on the redirect navigation\");\n // Check if this an absolute external redirect that goes to a new origin\n if (ABSOLUTE_URL_REGEX.test(redirect.location) && isBrowser) {\n let url = init.history.createURL(redirect.location);\n let isDifferentBasename = stripBasename(url.pathname, basename) == null;\n if (routerWindow.location.origin !== url.origin || isDifferentBasename) {\n if (replace) {\n routerWindow.location.replace(redirect.location);\n } else {\n routerWindow.location.assign(redirect.location);\n }\n return;\n }\n }\n // There's no need to abort on redirects, since we don't detect the\n // redirect until the action/loaders have settled\n pendingNavigationController = null;\n let redirectHistoryAction = replace === true ? Action.Replace : Action.Push;\n // Use the incoming submission if provided, fallback on the active one in\n // state.navigation\n let {\n formMethod,\n formAction,\n formEncType,\n formData\n } = state.navigation;\n if (!submission && formMethod && formAction && formData && formEncType) {\n submission = {\n formMethod,\n formAction,\n formEncType,\n formData\n };\n }\n // If this was a 307/308 submission we want to preserve the HTTP method and\n // re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the\n // redirected location\n if (redirectPreserveMethodStatusCodes.has(redirect.status) && submission && isMutationMethod(submission.formMethod)) {\n await startNavigation(redirectHistoryAction, redirectLocation, {\n submission: _extends({}, submission, {\n formAction: redirect.location\n }),\n // Preserve this flag across redirects\n preventScrollReset: pendingPreventScrollReset\n });\n } else if (isFetchActionRedirect) {\n // For a fetch action redirect, we kick off a new loading navigation\n // without the fetcher submission, but we send it along for shouldRevalidate\n await startNavigation(redirectHistoryAction, redirectLocation, {\n overrideNavigation: {\n state: \"loading\",\n location: redirectLocation,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined\n },\n fetcherSubmission: submission,\n // Preserve this flag across redirects\n preventScrollReset: pendingPreventScrollReset\n });\n } else {\n // Otherwise, we kick off a new loading navigation, preserving the\n // submission info for the duration of this navigation\n await startNavigation(redirectHistoryAction, redirectLocation, {\n overrideNavigation: {\n state: \"loading\",\n location: redirectLocation,\n formMethod: submission ? submission.formMethod : undefined,\n formAction: submission ? submission.formAction : undefined,\n formEncType: submission ? submission.formEncType : undefined,\n formData: submission ? submission.formData : undefined\n },\n // Preserve this flag across redirects\n preventScrollReset: pendingPreventScrollReset\n });\n }\n }\n async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {\n // Call all navigation loaders and revalidating fetcher loaders in parallel,\n // then slice off the results into separate arrays so we can handle them\n // accordingly\n let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction(\"loader\", request, match, matches, manifest, mapRouteProperties, basename)), ...fetchersToLoad.map(f => {\n if (f.matches && f.match && f.controller) {\n return callLoaderOrAction(\"loader\", createClientSideRequest(init.history, f.path, f.controller.signal), f.match, f.matches, manifest, mapRouteProperties, basename);\n } else {\n let error = {\n type: ResultType.error,\n error: getInternalRouterError(404, {\n pathname: f.path\n })\n };\n return error;\n }\n })]);\n let loaderResults = results.slice(0, matchesToLoad.length);\n let fetcherResults = results.slice(matchesToLoad.length);\n await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, loaderResults.map(() => request.signal), false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, fetchersToLoad.map(f => f.controller ? f.controller.signal : null), true)]);\n return {\n results,\n loaderResults,\n fetcherResults\n };\n }\n function interruptActiveLoads() {\n // Every interruption triggers a revalidation\n isRevalidationRequired = true;\n // Cancel pending route-level deferreds and mark cancelled routes for\n // revalidation\n cancelledDeferredRoutes.push(...cancelActiveDeferreds());\n // Abort in-flight fetcher loads\n fetchLoadMatches.forEach((_, key) => {\n if (fetchControllers.has(key)) {\n cancelledFetcherLoads.push(key);\n abortFetcher(key);\n }\n });\n }\n function setFetcherError(key, routeId, error) {\n let boundaryMatch = findNearestBoundary(state.matches, routeId);\n deleteFetcher(key);\n updateState({\n errors: {\n [boundaryMatch.route.id]: error\n },\n fetchers: new Map(state.fetchers)\n });\n }\n function deleteFetcher(key) {\n let fetcher = state.fetchers.get(key);\n // Don't abort the controller if this is a deletion of a fetcher.submit()\n // in it's loading phase since - we don't want to abort the corresponding\n // revalidation and want them to complete and land\n if (fetchControllers.has(key) && !(fetcher && fetcher.state === \"loading\" && fetchReloadIds.has(key))) {\n abortFetcher(key);\n }\n fetchLoadMatches.delete(key);\n fetchReloadIds.delete(key);\n fetchRedirectIds.delete(key);\n state.fetchers.delete(key);\n }\n function abortFetcher(key) {\n let controller = fetchControllers.get(key);\n invariant(controller, \"Expected fetch controller: \" + key);\n controller.abort();\n fetchControllers.delete(key);\n }\n function markFetchersDone(keys) {\n for (let key of keys) {\n let fetcher = getFetcher(key);\n let doneFetcher = {\n state: \"idle\",\n data: fetcher.data,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n \" _hasFetcherDoneAnything \": true\n };\n state.fetchers.set(key, doneFetcher);\n }\n }\n function markFetchRedirectsDone() {\n let doneKeys = [];\n let updatedFetchers = false;\n for (let key of fetchRedirectIds) {\n let fetcher = state.fetchers.get(key);\n invariant(fetcher, \"Expected fetcher: \" + key);\n if (fetcher.state === \"loading\") {\n fetchRedirectIds.delete(key);\n doneKeys.push(key);\n updatedFetchers = true;\n }\n }\n markFetchersDone(doneKeys);\n return updatedFetchers;\n }\n function abortStaleFetchLoads(landedId) {\n let yeetedKeys = [];\n for (let [key, id] of fetchReloadIds) {\n if (id < landedId) {\n let fetcher = state.fetchers.get(key);\n invariant(fetcher, \"Expected fetcher: \" + key);\n if (fetcher.state === \"loading\") {\n abortFetcher(key);\n fetchReloadIds.delete(key);\n yeetedKeys.push(key);\n }\n }\n }\n markFetchersDone(yeetedKeys);\n return yeetedKeys.length > 0;\n }\n function getBlocker(key, fn) {\n let blocker = state.blockers.get(key) || IDLE_BLOCKER;\n if (blockerFunctions.get(key) !== fn) {\n blockerFunctions.set(key, fn);\n }\n return blocker;\n }\n function deleteBlocker(key) {\n state.blockers.delete(key);\n blockerFunctions.delete(key);\n }\n // Utility function to update blockers, ensuring valid state transitions\n function updateBlocker(key, newBlocker) {\n let blocker = state.blockers.get(key) || IDLE_BLOCKER;\n // Poor mans state machine :)\n // https://mermaid.live/edit#pako:eNqVkc9OwzAMxl8l8nnjAYrEtDIOHEBIgwvKJTReGy3_lDpIqO27k6awMG0XcrLlnz87nwdonESogKXXBuE79rq75XZO3-yHds0RJVuv70YrPlUrCEe2HfrORS3rubqZfuhtpg5C9wk5tZ4VKcRUq88q9Z8RS0-48cE1iHJkL0ugbHuFLus9L6spZy8nX9MP2CNdomVaposqu3fGayT8T8-jJQwhepo_UtpgBQaDEUom04dZhAN1aJBDlUKJBxE1ceB2Smj0Mln-IBW5AFU2dwUiktt_2Qaq2dBfaKdEup85UV7Yd-dKjlnkabl2Pvr0DTkTreM\n invariant(blocker.state === \"unblocked\" && newBlocker.state === \"blocked\" || blocker.state === \"blocked\" && newBlocker.state === \"blocked\" || blocker.state === \"blocked\" && newBlocker.state === \"proceeding\" || blocker.state === \"blocked\" && newBlocker.state === \"unblocked\" || blocker.state === \"proceeding\" && newBlocker.state === \"unblocked\", \"Invalid blocker state transition: \" + blocker.state + \" -> \" + newBlocker.state);\n state.blockers.set(key, newBlocker);\n updateState({\n blockers: new Map(state.blockers)\n });\n }\n function shouldBlockNavigation(_ref2) {\n let {\n currentLocation,\n nextLocation,\n historyAction\n } = _ref2;\n if (blockerFunctions.size === 0) {\n return;\n }\n // We ony support a single active blocker at the moment since we don't have\n // any compelling use cases for multi-blocker yet\n if (blockerFunctions.size > 1) {\n warning(false, \"A router only supports one blocker at a time\");\n }\n let entries = Array.from(blockerFunctions.entries());\n let [blockerKey, blockerFunction] = entries[entries.length - 1];\n let blocker = state.blockers.get(blockerKey);\n if (blocker && blocker.state === \"proceeding\") {\n // If the blocker is currently proceeding, we don't need to re-check\n // it and can let this navigation continue\n return;\n }\n // At this point, we know we're unblocked/blocked so we need to check the\n // user-provided blocker function\n if (blockerFunction({\n currentLocation,\n nextLocation,\n historyAction\n })) {\n return blockerKey;\n }\n }\n function cancelActiveDeferreds(predicate) {\n let cancelledRouteIds = [];\n activeDeferreds.forEach((dfd, routeId) => {\n if (!predicate || predicate(routeId)) {\n // Cancel the deferred - but do not remove from activeDeferreds here -\n // we rely on the subscribers to do that so our tests can assert proper\n // cleanup via _internalActiveDeferreds\n dfd.cancel();\n cancelledRouteIds.push(routeId);\n activeDeferreds.delete(routeId);\n }\n });\n return cancelledRouteIds;\n }\n // Opt in to capturing and reporting scroll positions during navigations,\n // used by the component\n function enableScrollRestoration(positions, getPosition, getKey) {\n savedScrollPositions = positions;\n getScrollPosition = getPosition;\n getScrollRestorationKey = getKey || (location => location.key);\n // Perform initial hydration scroll restoration, since we miss the boat on\n // the initial updateState() because we've not yet rendered \n // and therefore have no savedScrollPositions available\n if (!initialScrollRestored && state.navigation === IDLE_NAVIGATION) {\n initialScrollRestored = true;\n let y = getSavedScrollPosition(state.location, state.matches);\n if (y != null) {\n updateState({\n restoreScrollPosition: y\n });\n }\n }\n return () => {\n savedScrollPositions = null;\n getScrollPosition = null;\n getScrollRestorationKey = null;\n };\n }\n function saveScrollPosition(location, matches) {\n if (savedScrollPositions && getScrollRestorationKey && getScrollPosition) {\n let userMatches = matches.map(m => createUseMatchesMatch(m, state.loaderData));\n let key = getScrollRestorationKey(location, userMatches) || location.key;\n savedScrollPositions[key] = getScrollPosition();\n }\n }\n function getSavedScrollPosition(location, matches) {\n if (savedScrollPositions && getScrollRestorationKey && getScrollPosition) {\n let userMatches = matches.map(m => createUseMatchesMatch(m, state.loaderData));\n let key = getScrollRestorationKey(location, userMatches) || location.key;\n let y = savedScrollPositions[key];\n if (typeof y === \"number\") {\n return y;\n }\n }\n return null;\n }\n function _internalSetRoutes(newRoutes) {\n manifest = {};\n inFlightDataRoutes = convertRoutesToDataRoutes(newRoutes, mapRouteProperties, undefined, manifest);\n }\n router = {\n get basename() {\n return basename;\n },\n get state() {\n return state;\n },\n get routes() {\n return dataRoutes;\n },\n initialize,\n subscribe,\n enableScrollRestoration,\n navigate,\n fetch,\n revalidate,\n // Passthrough to history-aware createHref used by useHref so we get proper\n // hash-aware URLs in DOM paths\n createHref: to => init.history.createHref(to),\n encodeLocation: to => init.history.encodeLocation(to),\n getFetcher,\n deleteFetcher,\n dispose,\n getBlocker,\n deleteBlocker,\n _internalFetchControllers: fetchControllers,\n _internalActiveDeferreds: activeDeferreds,\n // TODO: Remove setRoutes, it's temporary to avoid dealing with\n // updating the tree while validating the update algorithm.\n _internalSetRoutes\n };\n return router;\n}\n//#endregion\n////////////////////////////////////////////////////////////////////////////////\n//#region createStaticHandler\n////////////////////////////////////////////////////////////////////////////////\nconst UNSAFE_DEFERRED_SYMBOL = Symbol(\"deferred\");\nfunction createStaticHandler(routes, opts) {\n invariant(routes.length > 0, \"You must provide a non-empty routes array to createStaticHandler\");\n let manifest = {};\n let basename = (opts ? opts.basename : null) || \"/\";\n let mapRouteProperties;\n if (opts != null && opts.mapRouteProperties) {\n mapRouteProperties = opts.mapRouteProperties;\n } else if (opts != null && opts.detectErrorBoundary) {\n // If they are still using the deprecated version, wrap it with the new API\n let detectErrorBoundary = opts.detectErrorBoundary;\n mapRouteProperties = route => ({\n hasErrorBoundary: detectErrorBoundary(route)\n });\n } else {\n mapRouteProperties = defaultMapRouteProperties;\n }\n let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, undefined, manifest);\n /**\n * The query() method is intended for document requests, in which we want to\n * call an optional action and potentially multiple loaders for all nested\n * routes. It returns a StaticHandlerContext object, which is very similar\n * to the router state (location, loaderData, actionData, errors, etc.) and\n * also adds SSR-specific information such as the statusCode and headers\n * from action/loaders Responses.\n *\n * It _should_ never throw and should report all errors through the\n * returned context.errors object, properly associating errors to their error\n * boundary. Additionally, it tracks _deepestRenderedBoundaryId which can be\n * used to emulate React error boundaries during SSr by performing a second\n * pass only down to the boundaryId.\n *\n * The one exception where we do not return a StaticHandlerContext is when a\n * redirect response is returned or thrown from any action/loader. We\n * propagate that out and return the raw Response so the HTTP server can\n * return it directly.\n */\n async function query(request, _temp2) {\n let {\n requestContext\n } = _temp2 === void 0 ? {} : _temp2;\n let url = new URL(request.url);\n let method = request.method;\n let location = createLocation(\"\", createPath(url), null, \"default\");\n let matches = matchRoutes(dataRoutes, location, basename);\n // SSR supports HEAD requests while SPA doesn't\n if (!isValidMethod(method) && method !== \"HEAD\") {\n let error = getInternalRouterError(405, {\n method\n });\n let {\n matches: methodNotAllowedMatches,\n route\n } = getShortCircuitMatches(dataRoutes);\n return {\n basename,\n location,\n matches: methodNotAllowedMatches,\n loaderData: {},\n actionData: null,\n errors: {\n [route.id]: error\n },\n statusCode: error.status,\n loaderHeaders: {},\n actionHeaders: {},\n activeDeferreds: null\n };\n } else if (!matches) {\n let error = getInternalRouterError(404, {\n pathname: location.pathname\n });\n let {\n matches: notFoundMatches,\n route\n } = getShortCircuitMatches(dataRoutes);\n return {\n basename,\n location,\n matches: notFoundMatches,\n loaderData: {},\n actionData: null,\n errors: {\n [route.id]: error\n },\n statusCode: error.status,\n loaderHeaders: {},\n actionHeaders: {},\n activeDeferreds: null\n };\n }\n let result = await queryImpl(request, location, matches, requestContext);\n if (isResponse(result)) {\n return result;\n }\n // When returning StaticHandlerContext, we patch back in the location here\n // since we need it for React Context. But this helps keep our submit and\n // loadRouteData operating on a Request instead of a Location\n return _extends({\n location,\n basename\n }, result);\n }\n /**\n * The queryRoute() method is intended for targeted route requests, either\n * for fetch ?_data requests or resource route requests. In this case, we\n * are only ever calling a single action or loader, and we are returning the\n * returned value directly. In most cases, this will be a Response returned\n * from the action/loader, but it may be a primitive or other value as well -\n * and in such cases the calling context should handle that accordingly.\n *\n * We do respect the throw/return differentiation, so if an action/loader\n * throws, then this method will throw the value. This is important so we\n * can do proper boundary identification in Remix where a thrown Response\n * must go to the Catch Boundary but a returned Response is happy-path.\n *\n * One thing to note is that any Router-initiated Errors that make sense\n * to associate with a status code will be thrown as an ErrorResponse\n * instance which include the raw Error, such that the calling context can\n * serialize the error as they see fit while including the proper response\n * code. Examples here are 404 and 405 errors that occur prior to reaching\n * any user-defined loaders.\n */\n async function queryRoute(request, _temp3) {\n let {\n routeId,\n requestContext\n } = _temp3 === void 0 ? {} : _temp3;\n let url = new URL(request.url);\n let method = request.method;\n let location = createLocation(\"\", createPath(url), null, \"default\");\n let matches = matchRoutes(dataRoutes, location, basename);\n // SSR supports HEAD requests while SPA doesn't\n if (!isValidMethod(method) && method !== \"HEAD\" && method !== \"OPTIONS\") {\n throw getInternalRouterError(405, {\n method\n });\n } else if (!matches) {\n throw getInternalRouterError(404, {\n pathname: location.pathname\n });\n }\n let match = routeId ? matches.find(m => m.route.id === routeId) : getTargetMatch(matches, location);\n if (routeId && !match) {\n throw getInternalRouterError(403, {\n pathname: location.pathname,\n routeId\n });\n } else if (!match) {\n // This should never hit I don't think?\n throw getInternalRouterError(404, {\n pathname: location.pathname\n });\n }\n let result = await queryImpl(request, location, matches, requestContext, match);\n if (isResponse(result)) {\n return result;\n }\n let error = result.errors ? Object.values(result.errors)[0] : undefined;\n if (error !== undefined) {\n // If we got back result.errors, that means the loader/action threw\n // _something_ that wasn't a Response, but it's not guaranteed/required\n // to be an `instanceof Error` either, so we have to use throw here to\n // preserve the \"error\" state outside of queryImpl.\n throw error;\n }\n // Pick off the right state value to return\n if (result.actionData) {\n return Object.values(result.actionData)[0];\n }\n if (result.loaderData) {\n var _result$activeDeferre;\n let data = Object.values(result.loaderData)[0];\n if ((_result$activeDeferre = result.activeDeferreds) != null && _result$activeDeferre[match.route.id]) {\n data[UNSAFE_DEFERRED_SYMBOL] = result.activeDeferreds[match.route.id];\n }\n return data;\n }\n return undefined;\n }\n async function queryImpl(request, location, matches, requestContext, routeMatch) {\n invariant(request.signal, \"query()/queryRoute() requests must contain an AbortController signal\");\n try {\n if (isMutationMethod(request.method.toLowerCase())) {\n let result = await submit(request, matches, routeMatch || getTargetMatch(matches, location), requestContext, routeMatch != null);\n return result;\n }\n let result = await loadRouteData(request, matches, requestContext, routeMatch);\n return isResponse(result) ? result : _extends({}, result, {\n actionData: null,\n actionHeaders: {}\n });\n } catch (e) {\n // If the user threw/returned a Response in callLoaderOrAction, we throw\n // it to bail out and then return or throw here based on whether the user\n // returned or threw\n if (isQueryRouteResponse(e)) {\n if (e.type === ResultType.error && !isRedirectResponse(e.response)) {\n throw e.response;\n }\n return e.response;\n }\n // Redirects are always returned since they don't propagate to catch\n // boundaries\n if (isRedirectResponse(e)) {\n return e;\n }\n throw e;\n }\n }\n async function submit(request, matches, actionMatch, requestContext, isRouteRequest) {\n let result;\n if (!actionMatch.route.action && !actionMatch.route.lazy) {\n let error = getInternalRouterError(405, {\n method: request.method,\n pathname: new URL(request.url).pathname,\n routeId: actionMatch.route.id\n });\n if (isRouteRequest) {\n throw error;\n }\n result = {\n type: ResultType.error,\n error\n };\n } else {\n result = await callLoaderOrAction(\"action\", request, actionMatch, matches, manifest, mapRouteProperties, basename, true, isRouteRequest, requestContext);\n if (request.signal.aborted) {\n let method = isRouteRequest ? \"queryRoute\" : \"query\";\n throw new Error(method + \"() call aborted\");\n }\n }\n if (isRedirectResult(result)) {\n // Uhhhh - this should never happen, we should always throw these from\n // callLoaderOrAction, but the type narrowing here keeps TS happy and we\n // can get back on the \"throw all redirect responses\" train here should\n // this ever happen :/\n throw new Response(null, {\n status: result.status,\n headers: {\n Location: result.location\n }\n });\n }\n if (isDeferredResult(result)) {\n let error = getInternalRouterError(400, {\n type: \"defer-action\"\n });\n if (isRouteRequest) {\n throw error;\n }\n result = {\n type: ResultType.error,\n error\n };\n }\n if (isRouteRequest) {\n // Note: This should only be non-Response values if we get here, since\n // isRouteRequest should throw any Response received in callLoaderOrAction\n if (isErrorResult(result)) {\n throw result.error;\n }\n return {\n matches: [actionMatch],\n loaderData: {},\n actionData: {\n [actionMatch.route.id]: result.data\n },\n errors: null,\n // Note: statusCode + headers are unused here since queryRoute will\n // return the raw Response or value\n statusCode: 200,\n loaderHeaders: {},\n actionHeaders: {},\n activeDeferreds: null\n };\n }\n if (isErrorResult(result)) {\n // Store off the pending error - we use it to determine which loaders\n // to call and will commit it when we complete the navigation\n let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);\n let context = await loadRouteData(request, matches, requestContext, undefined, {\n [boundaryMatch.route.id]: result.error\n });\n // action status codes take precedence over loader status codes\n return _extends({}, context, {\n statusCode: isRouteErrorResponse(result.error) ? result.error.status : 500,\n actionData: null,\n actionHeaders: _extends({}, result.headers ? {\n [actionMatch.route.id]: result.headers\n } : {})\n });\n }\n // Create a GET request for the loaders\n let loaderRequest = new Request(request.url, {\n headers: request.headers,\n redirect: request.redirect,\n signal: request.signal\n });\n let context = await loadRouteData(loaderRequest, matches, requestContext);\n return _extends({}, context, result.statusCode ? {\n statusCode: result.statusCode\n } : {}, {\n actionData: {\n [actionMatch.route.id]: result.data\n },\n actionHeaders: _extends({}, result.headers ? {\n [actionMatch.route.id]: result.headers\n } : {})\n });\n }\n async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {\n let isRouteRequest = routeMatch != null;\n // Short circuit if we have no loaders to run (queryRoute())\n if (isRouteRequest && !(routeMatch != null && routeMatch.route.loader) && !(routeMatch != null && routeMatch.route.lazy)) {\n throw getInternalRouterError(400, {\n method: request.method,\n pathname: new URL(request.url).pathname,\n routeId: routeMatch == null ? void 0 : routeMatch.route.id\n });\n }\n let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);\n let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy);\n // Short circuit if we have no loaders to run (query())\n if (matchesToLoad.length === 0) {\n return {\n matches,\n // Add a null for all matched routes for proper revalidation on the client\n loaderData: matches.reduce((acc, m) => Object.assign(acc, {\n [m.route.id]: null\n }), {}),\n errors: pendingActionError || null,\n statusCode: 200,\n loaderHeaders: {},\n activeDeferreds: null\n };\n }\n let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction(\"loader\", request, match, matches, manifest, mapRouteProperties, basename, true, isRouteRequest, requestContext))]);\n if (request.signal.aborted) {\n let method = isRouteRequest ? \"queryRoute\" : \"query\";\n throw new Error(method + \"() call aborted\");\n }\n // Process and commit output from loaders\n let activeDeferreds = new Map();\n let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError, activeDeferreds);\n // Add a null for any non-loader matches for proper revalidation on the client\n let executedLoaders = new Set(matchesToLoad.map(match => match.route.id));\n matches.forEach(match => {\n if (!executedLoaders.has(match.route.id)) {\n context.loaderData[match.route.id] = null;\n }\n });\n return _extends({}, context, {\n matches,\n activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null\n });\n }\n return {\n dataRoutes,\n query,\n queryRoute\n };\n}\n//#endregion\n////////////////////////////////////////////////////////////////////////////////\n//#region Helpers\n////////////////////////////////////////////////////////////////////////////////\n/**\n * Given an existing StaticHandlerContext and an error thrown at render time,\n * provide an updated StaticHandlerContext suitable for a second SSR render\n */\nfunction getStaticContextFromError(routes, context, error) {\n let newContext = _extends({}, context, {\n statusCode: 500,\n errors: {\n [context._deepestRenderedBoundaryId || routes[0].id]: error\n }\n });\n return newContext;\n}\nfunction isSubmissionNavigation(opts) {\n return opts != null && \"formData\" in opts;\n}\nfunction normalizeTo(location, matches, basename, prependBasename, to, fromRouteId, relative) {\n let contextualMatches;\n let activeRouteMatch;\n if (fromRouteId != null && relative !== \"path\") {\n // Grab matches up to the calling route so our route-relative logic is\n // relative to the correct source route. When using relative:path,\n // fromRouteId is ignored since that is always relative to the current\n // location path\n contextualMatches = [];\n for (let match of matches) {\n contextualMatches.push(match);\n if (match.route.id === fromRouteId) {\n activeRouteMatch = match;\n break;\n }\n }\n } else {\n contextualMatches = matches;\n activeRouteMatch = matches[matches.length - 1];\n }\n // Resolve the relative path\n let path = resolveTo(to ? to : \".\", getPathContributingMatches(contextualMatches).map(m => m.pathnameBase), stripBasename(location.pathname, basename) || location.pathname, relative === \"path\");\n // When `to` is not specified we inherit search/hash from the current\n // location, unlike when to=\".\" and we just inherit the path.\n // See https://github.com/remix-run/remix/issues/927\n if (to == null) {\n path.search = location.search;\n path.hash = location.hash;\n }\n // Add an ?index param for matched index routes if we don't already have one\n if ((to == null || to === \"\" || to === \".\") && activeRouteMatch && activeRouteMatch.route.index && !hasNakedIndexQuery(path.search)) {\n path.search = path.search ? path.search.replace(/^\\?/, \"?index&\") : \"?index\";\n }\n // If we're operating within a basename, prepend it to the pathname. If\n // this is a root navigation, then just use the raw basename which allows\n // the basename to have full control over the presence of a trailing slash\n // on root actions\n if (prependBasename && basename !== \"/\") {\n path.pathname = path.pathname === \"/\" ? basename : joinPaths([basename, path.pathname]);\n }\n return createPath(path);\n}\n// Normalize navigation options by converting formMethod=GET formData objects to\n// URLSearchParams so they behave identically to links with query params\nfunction normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {\n // Return location verbatim on non-submission navigations\n if (!opts || !isSubmissionNavigation(opts)) {\n return {\n path\n };\n }\n if (opts.formMethod && !isValidMethod(opts.formMethod)) {\n return {\n path,\n error: getInternalRouterError(405, {\n method: opts.formMethod\n })\n };\n }\n // Create a Submission on non-GET navigations\n let submission;\n if (opts.formData) {\n let formMethod = opts.formMethod || \"get\";\n submission = {\n formMethod: normalizeFormMethod ? formMethod.toUpperCase() : formMethod.toLowerCase(),\n formAction: stripHashFromPath(path),\n formEncType: opts && opts.formEncType || \"application/x-www-form-urlencoded\",\n formData: opts.formData\n };\n if (isMutationMethod(submission.formMethod)) {\n return {\n path,\n submission\n };\n }\n }\n // Flatten submission onto URLSearchParams for GET submissions\n let parsedPath = parsePath(path);\n let searchParams = convertFormDataToSearchParams(opts.formData);\n // On GET navigation submissions we can drop the ?index param from the\n // resulting location since all loaders will run. But fetcher GET submissions\n // only run a single loader so we need to preserve any incoming ?index params\n if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) {\n searchParams.append(\"index\", \"\");\n }\n parsedPath.search = \"?\" + searchParams;\n return {\n path: createPath(parsedPath),\n submission\n };\n}\n// Filter out all routes below any caught error as they aren't going to\n// render so we don't need to load them\nfunction getLoaderMatchesUntilBoundary(matches, boundaryId) {\n let boundaryMatches = matches;\n if (boundaryId) {\n let index = matches.findIndex(m => m.route.id === boundaryId);\n if (index >= 0) {\n boundaryMatches = matches.slice(0, index);\n }\n }\n return boundaryMatches;\n}\nfunction getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, fetchLoadMatches, routesToUse, basename, pendingActionData, pendingError) {\n let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;\n let currentUrl = history.createURL(state.location);\n let nextUrl = history.createURL(location);\n // Pick navigation matches that are net-new or qualify for revalidation\n let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;\n let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);\n let navigationMatches = boundaryMatches.filter((match, index) => {\n if (match.route.lazy) {\n // We haven't loaded this route yet so we don't know if it's got a loader!\n return true;\n }\n if (match.route.loader == null) {\n return false;\n }\n // Always call the loader on new route instances and pending defer cancellations\n if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {\n return true;\n }\n // This is the default implementation for when we revalidate. If the route\n // provides it's own implementation, then we give them full control but\n // provide this value so they can leverage it if needed after they check\n // their own specific use cases\n let currentRouteMatch = state.matches[index];\n let nextRouteMatch = match;\n return shouldRevalidateLoader(match, _extends({\n currentUrl,\n currentParams: currentRouteMatch.params,\n nextUrl,\n nextParams: nextRouteMatch.params\n }, submission, {\n actionResult,\n defaultShouldRevalidate:\n // Forced revalidation due to submission, useRevalidator, or X-Remix-Revalidate\n isRevalidationRequired ||\n // Clicked the same link, resubmitted a GET form\n currentUrl.pathname + currentUrl.search === nextUrl.pathname + nextUrl.search ||\n // Search params affect all loaders\n currentUrl.search !== nextUrl.search || isNewRouteInstance(currentRouteMatch, nextRouteMatch)\n }));\n });\n // Pick fetcher.loads that need to be revalidated\n let revalidatingFetchers = [];\n fetchLoadMatches.forEach((f, key) => {\n // Don't revalidate if fetcher won't be present in the subsequent render\n if (!matches.some(m => m.route.id === f.routeId)) {\n return;\n }\n let fetcherMatches = matchRoutes(routesToUse, f.path, basename);\n // If the fetcher path no longer matches, push it in with null matches so\n // we can trigger a 404 in callLoadersAndMaybeResolveData\n if (!fetcherMatches) {\n revalidatingFetchers.push({\n key,\n routeId: f.routeId,\n path: f.path,\n matches: null,\n match: null,\n controller: null\n });\n return;\n }\n let fetcherMatch = getTargetMatch(fetcherMatches, f.path);\n if (cancelledFetcherLoads.includes(key)) {\n revalidatingFetchers.push({\n key,\n routeId: f.routeId,\n path: f.path,\n matches: fetcherMatches,\n match: fetcherMatch,\n controller: new AbortController()\n });\n return;\n }\n // Revalidating fetchers are decoupled from the route matches since they\n // hit a static href, so they _always_ check shouldRevalidate and the\n // default is strictly if a revalidation is explicitly required (action\n // submissions, useRevalidator, X-Remix-Revalidate).\n let shouldRevalidate = shouldRevalidateLoader(fetcherMatch, _extends({\n currentUrl,\n currentParams: state.matches[state.matches.length - 1].params,\n nextUrl,\n nextParams: matches[matches.length - 1].params\n }, submission, {\n actionResult,\n // Forced revalidation due to submission, useRevalidator, or X-Remix-Revalidate\n defaultShouldRevalidate: isRevalidationRequired\n }));\n if (shouldRevalidate) {\n revalidatingFetchers.push({\n key,\n routeId: f.routeId,\n path: f.path,\n matches: fetcherMatches,\n match: fetcherMatch,\n controller: new AbortController()\n });\n }\n });\n return [navigationMatches, revalidatingFetchers];\n}\nfunction isNewLoader(currentLoaderData, currentMatch, match) {\n let isNew =\n // [a] -> [a, b]\n !currentMatch ||\n // [a, b] -> [a, c]\n match.route.id !== currentMatch.route.id;\n // Handle the case that we don't have data for a re-used route, potentially\n // from a prior error or from a cancelled pending deferred\n let isMissingData = currentLoaderData[match.route.id] === undefined;\n // Always load if this is a net-new route or we don't yet have data\n return isNew || isMissingData;\n}\nfunction isNewRouteInstance(currentMatch, match) {\n let currentPath = currentMatch.route.path;\n return (\n // param change for this match, /users/123 -> /users/456\n currentMatch.pathname !== match.pathname ||\n // splat param changed, which is not present in match.path\n // e.g. /files/images/avatar.jpg -> files/finances.xls\n currentPath != null && currentPath.endsWith(\"*\") && currentMatch.params[\"*\"] !== match.params[\"*\"]\n );\n}\nfunction shouldRevalidateLoader(loaderMatch, arg) {\n if (loaderMatch.route.shouldRevalidate) {\n let routeChoice = loaderMatch.route.shouldRevalidate(arg);\n if (typeof routeChoice === \"boolean\") {\n return routeChoice;\n }\n }\n return arg.defaultShouldRevalidate;\n}\n/**\n * Execute route.lazy() methods to lazily load route modules (loader, action,\n * shouldRevalidate) and update the routeManifest in place which shares objects\n * with dataRoutes so those get updated as well.\n */\nasync function loadLazyRouteModule(route, mapRouteProperties, manifest) {\n if (!route.lazy) {\n return;\n }\n let lazyRoute = await route.lazy();\n // If the lazy route function was executed and removed by another parallel\n // call then we can return - first lazy() to finish wins because the return\n // value of lazy is expected to be static\n if (!route.lazy) {\n return;\n }\n let routeToUpdate = manifest[route.id];\n invariant(routeToUpdate, \"No route found in manifest\");\n // Update the route in place. This should be safe because there's no way\n // we could yet be sitting on this route as we can't get there without\n // resolving lazy() first.\n //\n // This is different than the HMR \"update\" use-case where we may actively be\n // on the route being updated. The main concern boils down to \"does this\n // mutation affect any ongoing navigations or any current state.matches\n // values?\". If not, it should be safe to update in place.\n let routeUpdates = {};\n for (let lazyRouteProperty in lazyRoute) {\n let staticRouteValue = routeToUpdate[lazyRouteProperty];\n let isPropertyStaticallyDefined = staticRouteValue !== undefined &&\n // This property isn't static since it should always be updated based\n // on the route updates\n lazyRouteProperty !== \"hasErrorBoundary\";\n warning(!isPropertyStaticallyDefined, \"Route \\\"\" + routeToUpdate.id + \"\\\" has a static property \\\"\" + lazyRouteProperty + \"\\\" \" + \"defined but its lazy function is also returning a value for this property. \" + (\"The lazy route property \\\"\" + lazyRouteProperty + \"\\\" will be ignored.\"));\n if (!isPropertyStaticallyDefined && !immutableRouteKeys.has(lazyRouteProperty)) {\n routeUpdates[lazyRouteProperty] = lazyRoute[lazyRouteProperty];\n }\n }\n // Mutate the route with the provided updates. Do this first so we pass\n // the updated version to mapRouteProperties\n Object.assign(routeToUpdate, routeUpdates);\n // Mutate the `hasErrorBoundary` property on the route based on the route\n // updates and remove the `lazy` function so we don't resolve the lazy\n // route again.\n Object.assign(routeToUpdate, _extends({}, mapRouteProperties(routeToUpdate), {\n lazy: undefined\n }));\n}\nasync function callLoaderOrAction(type, request, match, matches, manifest, mapRouteProperties, basename, isStaticRequest, isRouteRequest, requestContext) {\n if (isStaticRequest === void 0) {\n isStaticRequest = false;\n }\n if (isRouteRequest === void 0) {\n isRouteRequest = false;\n }\n let resultType;\n let result;\n let onReject;\n let runHandler = handler => {\n // Setup a promise we can race against so that abort signals short circuit\n let reject;\n let abortPromise = new Promise((_, r) => reject = r);\n onReject = () => reject();\n request.signal.addEventListener(\"abort\", onReject);\n return Promise.race([handler({\n request,\n params: match.params,\n context: requestContext\n }), abortPromise]);\n };\n try {\n let handler = match.route[type];\n if (match.route.lazy) {\n if (handler) {\n // Run statically defined handler in parallel with lazy()\n let values = await Promise.all([runHandler(handler), loadLazyRouteModule(match.route, mapRouteProperties, manifest)]);\n result = values[0];\n } else {\n // Load lazy route module, then run any returned handler\n await loadLazyRouteModule(match.route, mapRouteProperties, manifest);\n handler = match.route[type];\n if (handler) {\n // Handler still run even if we got interrupted to maintain consistency\n // with un-abortable behavior of handler execution on non-lazy or\n // previously-lazy-loaded routes\n result = await runHandler(handler);\n } else if (type === \"action\") {\n let url = new URL(request.url);\n let pathname = url.pathname + url.search;\n throw getInternalRouterError(405, {\n method: request.method,\n pathname,\n routeId: match.route.id\n });\n } else {\n // lazy() route has no loader to run. Short circuit here so we don't\n // hit the invariant below that errors on returning undefined.\n return {\n type: ResultType.data,\n data: undefined\n };\n }\n }\n } else if (!handler) {\n let url = new URL(request.url);\n let pathname = url.pathname + url.search;\n throw getInternalRouterError(404, {\n pathname\n });\n } else {\n result = await runHandler(handler);\n }\n invariant(result !== undefined, \"You defined \" + (type === \"action\" ? \"an action\" : \"a loader\") + \" for route \" + (\"\\\"\" + match.route.id + \"\\\" but didn't return anything from your `\" + type + \"` \") + \"function. Please return a value or `null`.\");\n } catch (e) {\n resultType = ResultType.error;\n result = e;\n } finally {\n if (onReject) {\n request.signal.removeEventListener(\"abort\", onReject);\n }\n }\n if (isResponse(result)) {\n let status = result.status;\n // Process redirects\n if (redirectStatusCodes.has(status)) {\n let location = result.headers.get(\"Location\");\n invariant(location, \"Redirects returned/thrown from loaders/actions must have a Location header\");\n // Support relative routing in internal redirects\n if (!ABSOLUTE_URL_REGEX.test(location)) {\n location = normalizeTo(new URL(request.url), matches.slice(0, matches.indexOf(match) + 1), basename, true, location);\n } else if (!isStaticRequest) {\n // Strip off the protocol+origin for same-origin + same-basename absolute\n // redirects. If this is a static request, we can let it go back to the\n // browser as-is\n let currentUrl = new URL(request.url);\n let url = location.startsWith(\"//\") ? new URL(currentUrl.protocol + location) : new URL(location);\n let isSameBasename = stripBasename(url.pathname, basename) != null;\n if (url.origin === currentUrl.origin && isSameBasename) {\n location = url.pathname + url.search + url.hash;\n }\n }\n // Don't process redirects in the router during static requests requests.\n // Instead, throw the Response and let the server handle it with an HTTP\n // redirect. We also update the Location header in place in this flow so\n // basename and relative routing is taken into account\n if (isStaticRequest) {\n result.headers.set(\"Location\", location);\n throw result;\n }\n return {\n type: ResultType.redirect,\n status,\n location,\n revalidate: result.headers.get(\"X-Remix-Revalidate\") !== null\n };\n }\n // For SSR single-route requests, we want to hand Responses back directly\n // without unwrapping. We do this with the QueryRouteResponse wrapper\n // interface so we can know whether it was returned or thrown\n if (isRouteRequest) {\n // eslint-disable-next-line no-throw-literal\n throw {\n type: resultType || ResultType.data,\n response: result\n };\n }\n let data;\n let contentType = result.headers.get(\"Content-Type\");\n // Check between word boundaries instead of startsWith() due to the last\n // paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type\n if (contentType && /\\bapplication\\/json\\b/.test(contentType)) {\n data = await result.json();\n } else {\n data = await result.text();\n }\n if (resultType === ResultType.error) {\n return {\n type: resultType,\n error: new ErrorResponse(status, result.statusText, data),\n headers: result.headers\n };\n }\n return {\n type: ResultType.data,\n data,\n statusCode: result.status,\n headers: result.headers\n };\n }\n if (resultType === ResultType.error) {\n return {\n type: resultType,\n error: result\n };\n }\n if (isDeferredData(result)) {\n var _result$init, _result$init2;\n return {\n type: ResultType.deferred,\n deferredData: result,\n statusCode: (_result$init = result.init) == null ? void 0 : _result$init.status,\n headers: ((_result$init2 = result.init) == null ? void 0 : _result$init2.headers) && new Headers(result.init.headers)\n };\n }\n return {\n type: ResultType.data,\n data: result\n };\n}\n// Utility method for creating the Request instances for loaders/actions during\n// client-side navigations and fetches. During SSR we will always have a\n// Request instance from the static handler (query/queryRoute)\nfunction createClientSideRequest(history, location, signal, submission) {\n let url = history.createURL(stripHashFromPath(location)).toString();\n let init = {\n signal\n };\n if (submission && isMutationMethod(submission.formMethod)) {\n let {\n formMethod,\n formEncType,\n formData\n } = submission;\n // Didn't think we needed this but it turns out unlike other methods, patch\n // won't be properly normalized to uppercase and results in a 405 error.\n // See: https://fetch.spec.whatwg.org/#concept-method\n init.method = formMethod.toUpperCase();\n init.body = formEncType === \"application/x-www-form-urlencoded\" ? convertFormDataToSearchParams(formData) : formData;\n }\n // Content-Type is inferred (https://fetch.spec.whatwg.org/#dom-request)\n return new Request(url, init);\n}\nfunction convertFormDataToSearchParams(formData) {\n let searchParams = new URLSearchParams();\n for (let [key, value] of formData.entries()) {\n // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#converting-an-entry-list-to-a-list-of-name-value-pairs\n searchParams.append(key, value instanceof File ? value.name : value);\n }\n return searchParams;\n}\nfunction processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds) {\n // Fill in loaderData/errors from our loaders\n let loaderData = {};\n let errors = null;\n let statusCode;\n let foundError = false;\n let loaderHeaders = {};\n // Process loader results into state.loaderData/state.errors\n results.forEach((result, index) => {\n let id = matchesToLoad[index].route.id;\n invariant(!isRedirectResult(result), \"Cannot handle redirect results in processLoaderData\");\n if (isErrorResult(result)) {\n // Look upwards from the matched route for the closest ancestor\n // error boundary, defaulting to the root match\n let boundaryMatch = findNearestBoundary(matches, id);\n let error = result.error;\n // If we have a pending action error, we report it at the highest-route\n // that throws a loader error, and then clear it out to indicate that\n // it was consumed\n if (pendingError) {\n error = Object.values(pendingError)[0];\n pendingError = undefined;\n }\n errors = errors || {};\n // Prefer higher error values if lower errors bubble to the same boundary\n if (errors[boundaryMatch.route.id] == null) {\n errors[boundaryMatch.route.id] = error;\n }\n // Clear our any prior loaderData for the throwing route\n loaderData[id] = undefined;\n // Once we find our first (highest) error, we set the status code and\n // prevent deeper status codes from overriding\n if (!foundError) {\n foundError = true;\n statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;\n }\n if (result.headers) {\n loaderHeaders[id] = result.headers;\n }\n } else {\n if (isDeferredResult(result)) {\n activeDeferreds.set(id, result.deferredData);\n loaderData[id] = result.deferredData.data;\n } else {\n loaderData[id] = result.data;\n }\n // Error status codes always override success status codes, but if all\n // loaders are successful we take the deepest status code.\n if (result.statusCode != null && result.statusCode !== 200 && !foundError) {\n statusCode = result.statusCode;\n }\n if (result.headers) {\n loaderHeaders[id] = result.headers;\n }\n }\n });\n // If we didn't consume the pending action error (i.e., all loaders\n // resolved), then consume it here. Also clear out any loaderData for the\n // throwing route\n if (pendingError) {\n errors = pendingError;\n loaderData[Object.keys(pendingError)[0]] = undefined;\n }\n return {\n loaderData,\n errors,\n statusCode: statusCode || 200,\n loaderHeaders\n };\n}\nfunction processLoaderData(state, matches, matchesToLoad, results, pendingError, revalidatingFetchers, fetcherResults, activeDeferreds) {\n let {\n loaderData,\n errors\n } = processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds);\n // Process results from our revalidating fetchers\n for (let index = 0; index < revalidatingFetchers.length; index++) {\n let {\n key,\n match,\n controller\n } = revalidatingFetchers[index];\n invariant(fetcherResults !== undefined && fetcherResults[index] !== undefined, \"Did not find corresponding fetcher result\");\n let result = fetcherResults[index];\n // Process fetcher non-redirect errors\n if (controller && controller.signal.aborted) {\n // Nothing to do for aborted fetchers\n continue;\n } else if (isErrorResult(result)) {\n let boundaryMatch = findNearestBoundary(state.matches, match == null ? void 0 : match.route.id);\n if (!(errors && errors[boundaryMatch.route.id])) {\n errors = _extends({}, errors, {\n [boundaryMatch.route.id]: result.error\n });\n }\n state.fetchers.delete(key);\n } else if (isRedirectResult(result)) {\n // Should never get here, redirects should get processed above, but we\n // keep this to type narrow to a success result in the else\n invariant(false, \"Unhandled fetcher revalidation redirect\");\n } else if (isDeferredResult(result)) {\n // Should never get here, deferred data should be awaited for fetchers\n // in resolveDeferredResults\n invariant(false, \"Unhandled fetcher deferred data\");\n } else {\n let doneFetcher = {\n state: \"idle\",\n data: result.data,\n formMethod: undefined,\n formAction: undefined,\n formEncType: undefined,\n formData: undefined,\n \" _hasFetcherDoneAnything \": true\n };\n state.fetchers.set(key, doneFetcher);\n }\n }\n return {\n loaderData,\n errors\n };\n}\nfunction mergeLoaderData(loaderData, newLoaderData, matches, errors) {\n let mergedLoaderData = _extends({}, newLoaderData);\n for (let match of matches) {\n let id = match.route.id;\n if (newLoaderData.hasOwnProperty(id)) {\n if (newLoaderData[id] !== undefined) {\n mergedLoaderData[id] = newLoaderData[id];\n }\n } else if (loaderData[id] !== undefined && match.route.loader) {\n // Preserve existing keys not included in newLoaderData and where a loader\n // wasn't removed by HMR\n mergedLoaderData[id] = loaderData[id];\n }\n if (errors && errors.hasOwnProperty(id)) {\n // Don't keep any loader data below the boundary\n break;\n }\n }\n return mergedLoaderData;\n}\n// Find the nearest error boundary, looking upwards from the leaf route (or the\n// route specified by routeId) for the closest ancestor error boundary,\n// defaulting to the root match\nfunction findNearestBoundary(matches, routeId) {\n let eligibleMatches = routeId ? matches.slice(0, matches.findIndex(m => m.route.id === routeId) + 1) : [...matches];\n return eligibleMatches.reverse().find(m => m.route.hasErrorBoundary === true) || matches[0];\n}\nfunction getShortCircuitMatches(routes) {\n // Prefer a root layout route if present, otherwise shim in a route object\n let route = routes.find(r => r.index || !r.path || r.path === \"/\") || {\n id: \"__shim-error-route__\"\n };\n return {\n matches: [{\n params: {},\n pathname: \"\",\n pathnameBase: \"\",\n route\n }],\n route\n };\n}\nfunction getInternalRouterError(status, _temp4) {\n let {\n pathname,\n routeId,\n method,\n type\n } = _temp4 === void 0 ? {} : _temp4;\n let statusText = \"Unknown Server Error\";\n let errorMessage = \"Unknown @remix-run/router error\";\n if (status === 400) {\n statusText = \"Bad Request\";\n if (method && pathname && routeId) {\n errorMessage = \"You made a \" + method + \" request to \\\"\" + pathname + \"\\\" but \" + (\"did not provide a `loader` for route \\\"\" + routeId + \"\\\", \") + \"so there is no way to handle the request.\";\n } else if (type === \"defer-action\") {\n errorMessage = \"defer() is not supported in actions\";\n }\n } else if (status === 403) {\n statusText = \"Forbidden\";\n errorMessage = \"Route \\\"\" + routeId + \"\\\" does not match URL \\\"\" + pathname + \"\\\"\";\n } else if (status === 404) {\n statusText = \"Not Found\";\n errorMessage = \"No route matches URL \\\"\" + pathname + \"\\\"\";\n } else if (status === 405) {\n statusText = \"Method Not Allowed\";\n if (method && pathname && routeId) {\n errorMessage = \"You made a \" + method.toUpperCase() + \" request to \\\"\" + pathname + \"\\\" but \" + (\"did not provide an `action` for route \\\"\" + routeId + \"\\\", \") + \"so there is no way to handle the request.\";\n } else if (method) {\n errorMessage = \"Invalid request method \\\"\" + method.toUpperCase() + \"\\\"\";\n }\n }\n return new ErrorResponse(status || 500, statusText, new Error(errorMessage), true);\n}\n// Find any returned redirect errors, starting from the lowest match\nfunction findRedirect(results) {\n for (let i = results.length - 1; i >= 0; i--) {\n let result = results[i];\n if (isRedirectResult(result)) {\n return result;\n }\n }\n}\nfunction stripHashFromPath(path) {\n let parsedPath = typeof path === \"string\" ? parsePath(path) : path;\n return createPath(_extends({}, parsedPath, {\n hash: \"\"\n }));\n}\nfunction isHashChangeOnly(a, b) {\n if (a.pathname !== b.pathname || a.search !== b.search) {\n return false;\n }\n if (a.hash === \"\") {\n // /page -> /page#hash\n return b.hash !== \"\";\n } else if (a.hash === b.hash) {\n // /page#hash -> /page#hash\n return true;\n } else if (b.hash !== \"\") {\n // /page#hash -> /page#other\n return true;\n }\n // If the hash is removed the browser will re-perform a request to the server\n // /page#hash -> /page\n return false;\n}\nfunction isDeferredResult(result) {\n return result.type === ResultType.deferred;\n}\nfunction isErrorResult(result) {\n return result.type === ResultType.error;\n}\nfunction isRedirectResult(result) {\n return (result && result.type) === ResultType.redirect;\n}\nfunction isDeferredData(value) {\n let deferred = value;\n return deferred && typeof deferred === \"object\" && typeof deferred.data === \"object\" && typeof deferred.subscribe === \"function\" && typeof deferred.cancel === \"function\" && typeof deferred.resolveData === \"function\";\n}\nfunction isResponse(value) {\n return value != null && typeof value.status === \"number\" && typeof value.statusText === \"string\" && typeof value.headers === \"object\" && typeof value.body !== \"undefined\";\n}\nfunction isRedirectResponse(result) {\n if (!isResponse(result)) {\n return false;\n }\n let status = result.status;\n let location = result.headers.get(\"Location\");\n return status >= 300 && status <= 399 && location != null;\n}\nfunction isQueryRouteResponse(obj) {\n return obj && isResponse(obj.response) && (obj.type === ResultType.data || ResultType.error);\n}\nfunction isValidMethod(method) {\n return validRequestMethods.has(method.toLowerCase());\n}\nfunction isMutationMethod(method) {\n return validMutationMethods.has(method.toLowerCase());\n}\nasync function resolveDeferredResults(currentMatches, matchesToLoad, results, signals, isFetcher, currentLoaderData) {\n for (let index = 0; index < results.length; index++) {\n let result = results[index];\n let match = matchesToLoad[index];\n // If we don't have a match, then we can have a deferred result to do\n // anything with. This is for revalidating fetchers where the route was\n // removed during HMR\n if (!match) {\n continue;\n }\n let currentMatch = currentMatches.find(m => m.route.id === match.route.id);\n let isRevalidatingLoader = currentMatch != null && !isNewRouteInstance(currentMatch, match) && (currentLoaderData && currentLoaderData[match.route.id]) !== undefined;\n if (isDeferredResult(result) && (isFetcher || isRevalidatingLoader)) {\n // Note: we do not have to touch activeDeferreds here since we race them\n // against the signal in resolveDeferredData and they'll get aborted\n // there if needed\n let signal = signals[index];\n invariant(signal, \"Expected an AbortSignal for revalidating fetcher deferred result\");\n await resolveDeferredData(result, signal, isFetcher).then(result => {\n if (result) {\n results[index] = result || results[index];\n }\n });\n }\n }\n}\nasync function resolveDeferredData(result, signal, unwrap) {\n if (unwrap === void 0) {\n unwrap = false;\n }\n let aborted = await result.deferredData.resolveData(signal);\n if (aborted) {\n return;\n }\n if (unwrap) {\n try {\n return {\n type: ResultType.data,\n data: result.deferredData.unwrappedData\n };\n } catch (e) {\n // Handle any TrackedPromise._error values encountered while unwrapping\n return {\n type: ResultType.error,\n error: e\n };\n }\n }\n return {\n type: ResultType.data,\n data: result.deferredData.data\n };\n}\nfunction hasNakedIndexQuery(search) {\n return new URLSearchParams(search).getAll(\"index\").some(v => v === \"\");\n}\n// Note: This should match the format exported by useMatches, so if you change\n// this please also change that :) Eventually we'll DRY this up\nfunction createUseMatchesMatch(match, loaderData) {\n let {\n route,\n pathname,\n params\n } = match;\n return {\n id: route.id,\n pathname,\n params,\n data: loaderData[route.id],\n handle: route.handle\n };\n}\nfunction getTargetMatch(matches, location) {\n let search = typeof location === \"string\" ? parsePath(location).search : location.search;\n if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || \"\")) {\n // Return the leaf index route when index is present\n return matches[matches.length - 1];\n }\n // Otherwise grab the deepest \"path contributing\" match (ignoring index and\n // pathless layout routes)\n let pathMatches = getPathContributingMatches(matches);\n return pathMatches[pathMatches.length - 1];\n}\n//#endregion\n\nexport { AbortedDeferredError, Action, ErrorResponse, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getPathContributingMatches as UNSAFE_getPathContributingMatches, invariant as UNSAFE_invariant, warning as UNSAFE_warning, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, isDeferredData, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, resolvePath, resolveTo, stripBasename };\n//# sourceMappingURL=router.js.map\n","/**\n * React Router v6.12.1\n *\n * Copyright (c) Remix Software Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE.md file in the root directory of this source tree.\n *\n * @license MIT\n */\nimport * as React from 'react';\nimport { UNSAFE_invariant, joinPaths, matchPath, UNSAFE_getPathContributingMatches, UNSAFE_warning, resolveTo, parsePath, matchRoutes, Action, isRouteErrorResponse, createMemoryHistory, stripBasename, AbortedDeferredError, createRouter } from '@remix-run/router';\nexport { AbortedDeferredError, Action as NavigationType, createPath, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, resolvePath } from '@remix-run/router';\n\nfunction _extends() {\n _extends = Object.assign ? Object.assign.bind() : function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n };\n return _extends.apply(this, arguments);\n}\n\n// Create react-specific types from the agnostic types in @remix-run/router to\n// export from react-router\nconst DataRouterContext = /*#__PURE__*/React.createContext(null);\nif (process.env.NODE_ENV !== \"production\") {\n DataRouterContext.displayName = \"DataRouter\";\n}\nconst DataRouterStateContext = /*#__PURE__*/React.createContext(null);\nif (process.env.NODE_ENV !== \"production\") {\n DataRouterStateContext.displayName = \"DataRouterState\";\n}\nconst AwaitContext = /*#__PURE__*/React.createContext(null);\nif (process.env.NODE_ENV !== \"production\") {\n AwaitContext.displayName = \"Await\";\n}\n\n/**\n * A Navigator is a \"location changer\"; it's how you get to different locations.\n *\n * Every history instance conforms to the Navigator interface, but the\n * distinction is useful primarily when it comes to the low-level API\n * where both the location and a navigator must be provided separately in order\n * to avoid \"tearing\" that may occur in a suspense-enabled app if the action\n * and/or location were to be read directly from the history instance.\n */\n\nconst NavigationContext = /*#__PURE__*/React.createContext(null);\nif (process.env.NODE_ENV !== \"production\") {\n NavigationContext.displayName = \"Navigation\";\n}\nconst LocationContext = /*#__PURE__*/React.createContext(null);\nif (process.env.NODE_ENV !== \"production\") {\n LocationContext.displayName = \"Location\";\n}\nconst RouteContext = /*#__PURE__*/React.createContext({\n outlet: null,\n matches: [],\n isDataRoute: false\n});\nif (process.env.NODE_ENV !== \"production\") {\n RouteContext.displayName = \"Route\";\n}\nconst RouteErrorContext = /*#__PURE__*/React.createContext(null);\nif (process.env.NODE_ENV !== \"production\") {\n RouteErrorContext.displayName = \"RouteError\";\n}\n\n/**\n * Returns the full href for the given \"to\" value. This is useful for building\n * custom links that are also accessible and preserve right-click behavior.\n *\n * @see https://reactrouter.com/hooks/use-href\n */\nfunction useHref(to, _temp) {\n let {\n relative\n } = _temp === void 0 ? {} : _temp;\n !useInRouterContext() ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the\n // router loaded. We can help them understand how to avoid that.\n \"useHref() may be used only in the context of a component.\") : UNSAFE_invariant(false) : void 0;\n let {\n basename,\n navigator\n } = React.useContext(NavigationContext);\n let {\n hash,\n pathname,\n search\n } = useResolvedPath(to, {\n relative\n });\n let joinedPathname = pathname;\n\n // If we're operating within a basename, prepend it to the pathname prior\n // to creating the href. If this is a root navigation, then just use the raw\n // basename which allows the basename to have full control over the presence\n // of a trailing slash on root links\n if (basename !== \"/\") {\n joinedPathname = pathname === \"/\" ? basename : joinPaths([basename, pathname]);\n }\n return navigator.createHref({\n pathname: joinedPathname,\n search,\n hash\n });\n}\n\n/**\n * Returns true if this component is a descendant of a .\n *\n * @see https://reactrouter.com/hooks/use-in-router-context\n */\nfunction useInRouterContext() {\n return React.useContext(LocationContext) != null;\n}\n\n/**\n * Returns the current location object, which represents the current URL in web\n * browsers.\n *\n * Note: If you're using this it may mean you're doing some of your own\n * \"routing\" in your app, and we'd like to know what your use case is. We may\n * be able to provide something higher-level to better suit your needs.\n *\n * @see https://reactrouter.com/hooks/use-location\n */\nfunction useLocation() {\n !useInRouterContext() ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the\n // router loaded. We can help them understand how to avoid that.\n \"useLocation() may be used only in the context of a component.\") : UNSAFE_invariant(false) : void 0;\n return React.useContext(LocationContext).location;\n}\n\n/**\n * Returns the current navigation action which describes how the router came to\n * the current location, either by a pop, push, or replace on the history stack.\n *\n * @see https://reactrouter.com/hooks/use-navigation-type\n */\nfunction useNavigationType() {\n return React.useContext(LocationContext).navigationType;\n}\n\n/**\n * Returns a PathMatch object if the given pattern matches the current URL.\n * This is useful for components that need to know \"active\" state, e.g.\n * .\n *\n * @see https://reactrouter.com/hooks/use-match\n */\nfunction useMatch(pattern) {\n !useInRouterContext() ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the\n // router loaded. We can help them understand how to avoid that.\n \"useMatch() may be used only in the context of a component.\") : UNSAFE_invariant(false) : void 0;\n let {\n pathname\n } = useLocation();\n return React.useMemo(() => matchPath(pattern, pathname), [pathname, pattern]);\n}\n\n/**\n * The interface for the navigate() function returned from useNavigate().\n */\n\nconst navigateEffectWarning = \"You should call navigate() in a React.useEffect(), not when \" + \"your component is first rendered.\";\n\n// Mute warnings for calls to useNavigate in SSR environments\nfunction useIsomorphicLayoutEffect(cb) {\n let isStatic = React.useContext(NavigationContext).static;\n if (!isStatic) {\n // We should be able to get rid of this once react 18.3 is released\n // See: https://github.com/facebook/react/pull/26395\n // eslint-disable-next-line react-hooks/rules-of-hooks\n React.useLayoutEffect(cb);\n }\n}\n\n/**\n * Returns an imperative method for changing the location. Used by s, but\n * may also be used by other elements to change the location.\n *\n * @see https://reactrouter.com/hooks/use-navigate\n */\nfunction useNavigate() {\n let {\n isDataRoute\n } = React.useContext(RouteContext);\n // Conditional usage is OK here because the usage of a data router is static\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return isDataRoute ? useNavigateStable() : useNavigateUnstable();\n}\nfunction useNavigateUnstable() {\n !useInRouterContext() ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the\n // router loaded. We can help them understand how to avoid that.\n \"useNavigate() may be used only in the context of a component.\") : UNSAFE_invariant(false) : void 0;\n let dataRouterContext = React.useContext(DataRouterContext);\n let {\n basename,\n navigator\n } = React.useContext(NavigationContext);\n let {\n matches\n } = React.useContext(RouteContext);\n let {\n pathname: locationPathname\n } = useLocation();\n let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase));\n let activeRef = React.useRef(false);\n useIsomorphicLayoutEffect(() => {\n activeRef.current = true;\n });\n let navigate = React.useCallback(function (to, options) {\n if (options === void 0) {\n options = {};\n }\n process.env.NODE_ENV !== \"production\" ? UNSAFE_warning(activeRef.current, navigateEffectWarning) : void 0;\n\n // Short circuit here since if this happens on first render the navigate\n // is useless because we haven't wired up our history listener yet\n if (!activeRef.current) return;\n if (typeof to === \"number\") {\n navigator.go(to);\n return;\n }\n let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, options.relative === \"path\");\n\n // If we're operating within a basename, prepend it to the pathname prior\n // to handing off to history (but only if we're not in a data router,\n // otherwise it'll prepend the basename inside of the router).\n // If this is a root navigation, then we navigate to the raw basename\n // which allows the basename to have full control over the presence of a\n // trailing slash on root links\n if (dataRouterContext == null && basename !== \"/\") {\n path.pathname = path.pathname === \"/\" ? basename : joinPaths([basename, path.pathname]);\n }\n (!!options.replace ? navigator.replace : navigator.push)(path, options.state, options);\n }, [basename, navigator, routePathnamesJson, locationPathname, dataRouterContext]);\n return navigate;\n}\nconst OutletContext = /*#__PURE__*/React.createContext(null);\n\n/**\n * Returns the context (if provided) for the child route at this level of the route\n * hierarchy.\n * @see https://reactrouter.com/hooks/use-outlet-context\n */\nfunction useOutletContext() {\n return React.useContext(OutletContext);\n}\n\n/**\n * Returns the element for the child route at this level of the route\n * hierarchy. Used internally by to render child routes.\n *\n * @see https://reactrouter.com/hooks/use-outlet\n */\nfunction useOutlet(context) {\n let outlet = React.useContext(RouteContext).outlet;\n if (outlet) {\n return /*#__PURE__*/React.createElement(OutletContext.Provider, {\n value: context\n }, outlet);\n }\n return outlet;\n}\n\n/**\n * Returns an object of key/value pairs of the dynamic params from the current\n * URL that were matched by the route path.\n *\n * @see https://reactrouter.com/hooks/use-params\n */\nfunction useParams() {\n let {\n matches\n } = React.useContext(RouteContext);\n let routeMatch = matches[matches.length - 1];\n return routeMatch ? routeMatch.params : {};\n}\n\n/**\n * Resolves the pathname of the given `to` value against the current location.\n *\n * @see https://reactrouter.com/hooks/use-resolved-path\n */\nfunction useResolvedPath(to, _temp2) {\n let {\n relative\n } = _temp2 === void 0 ? {} : _temp2;\n let {\n matches\n } = React.useContext(RouteContext);\n let {\n pathname: locationPathname\n } = useLocation();\n let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase));\n return React.useMemo(() => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, relative === \"path\"), [to, routePathnamesJson, locationPathname, relative]);\n}\n\n/**\n * Returns the element of the route that matched the current location, prepared\n * with the correct context to render the remainder of the route tree. Route\n * elements in the tree must render an to render their child route's\n * element.\n *\n * @see https://reactrouter.com/hooks/use-routes\n */\nfunction useRoutes(routes, locationArg) {\n return useRoutesImpl(routes, locationArg);\n}\n\n// Internal implementation with accept optional param for RouterProvider usage\nfunction useRoutesImpl(routes, locationArg, dataRouterState) {\n !useInRouterContext() ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of the\n // router loaded. We can help them understand how to avoid that.\n \"useRoutes() may be used only in the context of a component.\") : UNSAFE_invariant(false) : void 0;\n let {\n navigator\n } = React.useContext(NavigationContext);\n let {\n matches: parentMatches\n } = React.useContext(RouteContext);\n let routeMatch = parentMatches[parentMatches.length - 1];\n let parentParams = routeMatch ? routeMatch.params : {};\n let parentPathname = routeMatch ? routeMatch.pathname : \"/\";\n let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : \"/\";\n let parentRoute = routeMatch && routeMatch.route;\n if (process.env.NODE_ENV !== \"production\") {\n // You won't get a warning about 2 different under a \n // without a trailing *, but this is a best-effort warning anyway since we\n // cannot even give the warning unless they land at the parent route.\n //\n // Example:\n //\n // \n // {/* This route path MUST end with /* because otherwise\n // it will never match /blog/post/123 */}\n // } />\n // } />\n // \n //\n // function Blog() {\n // return (\n // \n // } />\n // \n // );\n // }\n let parentPath = parentRoute && parentRoute.path || \"\";\n warningOnce(parentPathname, !parentRoute || parentPath.endsWith(\"*\"), \"You rendered descendant (or called `useRoutes()`) at \" + (\"\\\"\" + parentPathname + \"\\\" (under ) but the \") + \"parent route path has no trailing \\\"*\\\". This means if you navigate \" + \"deeper, the parent won't match anymore and therefore the child \" + \"routes will never render.\\n\\n\" + (\"Please change the parent to .\"));\n }\n let locationFromContext = useLocation();\n let location;\n if (locationArg) {\n var _parsedLocationArg$pa;\n let parsedLocationArg = typeof locationArg === \"string\" ? parsePath(locationArg) : locationArg;\n !(parentPathnameBase === \"/\" || ((_parsedLocationArg$pa = parsedLocationArg.pathname) == null ? void 0 : _parsedLocationArg$pa.startsWith(parentPathnameBase))) ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, \"When overriding the location using `` or `useRoutes(routes, location)`, \" + \"the location pathname must begin with the portion of the URL pathname that was \" + (\"matched by all parent routes. The current pathname base is \\\"\" + parentPathnameBase + \"\\\" \") + (\"but pathname \\\"\" + parsedLocationArg.pathname + \"\\\" was given in the `location` prop.\")) : UNSAFE_invariant(false) : void 0;\n location = parsedLocationArg;\n } else {\n location = locationFromContext;\n }\n let pathname = location.pathname || \"/\";\n let remainingPathname = parentPathnameBase === \"/\" ? pathname : pathname.slice(parentPathnameBase.length) || \"/\";\n let matches = matchRoutes(routes, {\n pathname: remainingPathname\n });\n if (process.env.NODE_ENV !== \"production\") {\n process.env.NODE_ENV !== \"production\" ? UNSAFE_warning(parentRoute || matches != null, \"No routes matched location \\\"\" + location.pathname + location.search + location.hash + \"\\\" \") : void 0;\n process.env.NODE_ENV !== \"production\" ? UNSAFE_warning(matches == null || matches[matches.length - 1].route.element !== undefined || matches[matches.length - 1].route.Component !== undefined, \"Matched leaf route at location \\\"\" + location.pathname + location.search + location.hash + \"\\\" \" + \"does not have an element or Component. This means it will render an with a \" + \"null value by default resulting in an \\\"empty\\\" page.\") : void 0;\n }\n let renderedMatches = _renderMatches(matches && matches.map(match => Object.assign({}, match, {\n params: Object.assign({}, parentParams, match.params),\n pathname: joinPaths([parentPathnameBase,\n // Re-encode pathnames that were decoded inside matchRoutes\n navigator.encodeLocation ? navigator.encodeLocation(match.pathname).pathname : match.pathname]),\n pathnameBase: match.pathnameBase === \"/\" ? parentPathnameBase : joinPaths([parentPathnameBase,\n // Re-encode pathnames that were decoded inside matchRoutes\n navigator.encodeLocation ? navigator.encodeLocation(match.pathnameBase).pathname : match.pathnameBase])\n })), parentMatches, dataRouterState);\n\n // When a user passes in a `locationArg`, the associated routes need to\n // be wrapped in a new `LocationContext.Provider` in order for `useLocation`\n // to use the scoped location instead of the global location.\n if (locationArg && renderedMatches) {\n return /*#__PURE__*/React.createElement(LocationContext.Provider, {\n value: {\n location: _extends({\n pathname: \"/\",\n search: \"\",\n hash: \"\",\n state: null,\n key: \"default\"\n }, location),\n navigationType: Action.Pop\n }\n }, renderedMatches);\n }\n return renderedMatches;\n}\nfunction DefaultErrorComponent() {\n let error = useRouteError();\n let message = isRouteErrorResponse(error) ? error.status + \" \" + error.statusText : error instanceof Error ? error.message : JSON.stringify(error);\n let stack = error instanceof Error ? error.stack : null;\n let lightgrey = \"rgba(200,200,200, 0.5)\";\n let preStyles = {\n padding: \"0.5rem\",\n backgroundColor: lightgrey\n };\n let codeStyles = {\n padding: \"2px 4px\",\n backgroundColor: lightgrey\n };\n let devInfo = null;\n if (process.env.NODE_ENV !== \"production\") {\n console.error(\"Error handled by React Router default ErrorBoundary:\", error);\n devInfo = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(\"p\", null, \"\\uD83D\\uDCBF Hey developer \\uD83D\\uDC4B\"), /*#__PURE__*/React.createElement(\"p\", null, \"You can provide a way better UX than this when your app throws errors by providing your own \", /*#__PURE__*/React.createElement(\"code\", {\n style: codeStyles\n }, \"ErrorBoundary\"), \" or\", \" \", /*#__PURE__*/React.createElement(\"code\", {\n style: codeStyles\n }, \"errorElement\"), \" prop on your route.\"));\n }\n return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(\"h2\", null, \"Unexpected Application Error!\"), /*#__PURE__*/React.createElement(\"h3\", {\n style: {\n fontStyle: \"italic\"\n }\n }, message), stack ? /*#__PURE__*/React.createElement(\"pre\", {\n style: preStyles\n }, stack) : null, devInfo);\n}\nconst defaultErrorElement = /*#__PURE__*/React.createElement(DefaultErrorComponent, null);\nclass RenderErrorBoundary extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n location: props.location,\n revalidation: props.revalidation,\n error: props.error\n };\n }\n static getDerivedStateFromError(error) {\n return {\n error: error\n };\n }\n static getDerivedStateFromProps(props, state) {\n // When we get into an error state, the user will likely click \"back\" to the\n // previous page that didn't have an error. Because this wraps the entire\n // application, that will have no effect--the error page continues to display.\n // This gives us a mechanism to recover from the error when the location changes.\n //\n // Whether we're in an error state or not, we update the location in state\n // so that when we are in an error state, it gets reset when a new location\n // comes in and the user recovers from the error.\n if (state.location !== props.location || state.revalidation !== \"idle\" && props.revalidation === \"idle\") {\n return {\n error: props.error,\n location: props.location,\n revalidation: props.revalidation\n };\n }\n\n // If we're not changing locations, preserve the location but still surface\n // any new errors that may come through. We retain the existing error, we do\n // this because the error provided from the app state may be cleared without\n // the location changing.\n return {\n error: props.error || state.error,\n location: state.location,\n revalidation: props.revalidation || state.revalidation\n };\n }\n componentDidCatch(error, errorInfo) {\n console.error(\"React Router caught the following error during render\", error, errorInfo);\n }\n render() {\n return this.state.error ? /*#__PURE__*/React.createElement(RouteContext.Provider, {\n value: this.props.routeContext\n }, /*#__PURE__*/React.createElement(RouteErrorContext.Provider, {\n value: this.state.error,\n children: this.props.component\n })) : this.props.children;\n }\n}\nfunction RenderedRoute(_ref) {\n let {\n routeContext,\n match,\n children\n } = _ref;\n let dataRouterContext = React.useContext(DataRouterContext);\n\n // Track how deep we got in our render pass to emulate SSR componentDidCatch\n // in a DataStaticRouter\n if (dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && (match.route.errorElement || match.route.ErrorBoundary)) {\n dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id;\n }\n return /*#__PURE__*/React.createElement(RouteContext.Provider, {\n value: routeContext\n }, children);\n}\nfunction _renderMatches(matches, parentMatches, dataRouterState) {\n var _dataRouterState2;\n if (parentMatches === void 0) {\n parentMatches = [];\n }\n if (dataRouterState === void 0) {\n dataRouterState = null;\n }\n if (matches == null) {\n var _dataRouterState;\n if ((_dataRouterState = dataRouterState) != null && _dataRouterState.errors) {\n // Don't bail if we have data router errors so we can render them in the\n // boundary. Use the pre-matched (or shimmed) matches\n matches = dataRouterState.matches;\n } else {\n return null;\n }\n }\n let renderedMatches = matches;\n\n // If we have data errors, trim matches to the highest error boundary\n let errors = (_dataRouterState2 = dataRouterState) == null ? void 0 : _dataRouterState2.errors;\n if (errors != null) {\n let errorIndex = renderedMatches.findIndex(m => m.route.id && (errors == null ? void 0 : errors[m.route.id]));\n !(errorIndex >= 0) ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, \"Could not find a matching route for errors on route IDs: \" + Object.keys(errors).join(\",\")) : UNSAFE_invariant(false) : void 0;\n renderedMatches = renderedMatches.slice(0, Math.min(renderedMatches.length, errorIndex + 1));\n }\n return renderedMatches.reduceRight((outlet, match, index) => {\n let error = match.route.id ? errors == null ? void 0 : errors[match.route.id] : null;\n // Only data routers handle errors\n let errorElement = null;\n if (dataRouterState) {\n errorElement = match.route.errorElement || defaultErrorElement;\n }\n let matches = parentMatches.concat(renderedMatches.slice(0, index + 1));\n let getChildren = () => {\n let children;\n if (error) {\n children = errorElement;\n } else if (match.route.Component) {\n // Note: This is a de-optimized path since React won't re-use the\n // ReactElement since it's identity changes with each new\n // React.createElement call. We keep this so folks can use\n // `` in `` but generally `Component`\n // usage is only advised in `RouterProvider` when we can convert it to\n // `element` ahead of time.\n children = /*#__PURE__*/React.createElement(match.route.Component, null);\n } else if (match.route.element) {\n children = match.route.element;\n } else {\n children = outlet;\n }\n return /*#__PURE__*/React.createElement(RenderedRoute, {\n match: match,\n routeContext: {\n outlet,\n matches,\n isDataRoute: dataRouterState != null\n },\n children: children\n });\n };\n // Only wrap in an error boundary within data router usages when we have an\n // ErrorBoundary/errorElement on this route. Otherwise let it bubble up to\n // an ancestor ErrorBoundary/errorElement\n return dataRouterState && (match.route.ErrorBoundary || match.route.errorElement || index === 0) ? /*#__PURE__*/React.createElement(RenderErrorBoundary, {\n location: dataRouterState.location,\n revalidation: dataRouterState.revalidation,\n component: errorElement,\n error: error,\n children: getChildren(),\n routeContext: {\n outlet: null,\n matches,\n isDataRoute: true\n }\n }) : getChildren();\n }, null);\n}\nvar DataRouterHook;\n(function (DataRouterHook) {\n DataRouterHook[\"UseBlocker\"] = \"useBlocker\";\n DataRouterHook[\"UseRevalidator\"] = \"useRevalidator\";\n DataRouterHook[\"UseNavigateStable\"] = \"useNavigate\";\n})(DataRouterHook || (DataRouterHook = {}));\nvar DataRouterStateHook;\n(function (DataRouterStateHook) {\n DataRouterStateHook[\"UseBlocker\"] = \"useBlocker\";\n DataRouterStateHook[\"UseLoaderData\"] = \"useLoaderData\";\n DataRouterStateHook[\"UseActionData\"] = \"useActionData\";\n DataRouterStateHook[\"UseRouteError\"] = \"useRouteError\";\n DataRouterStateHook[\"UseNavigation\"] = \"useNavigation\";\n DataRouterStateHook[\"UseRouteLoaderData\"] = \"useRouteLoaderData\";\n DataRouterStateHook[\"UseMatches\"] = \"useMatches\";\n DataRouterStateHook[\"UseRevalidator\"] = \"useRevalidator\";\n DataRouterStateHook[\"UseNavigateStable\"] = \"useNavigate\";\n DataRouterStateHook[\"UseRouteId\"] = \"useRouteId\";\n})(DataRouterStateHook || (DataRouterStateHook = {}));\nfunction getDataRouterConsoleError(hookName) {\n return hookName + \" must be used within a data router. See https://reactrouter.com/routers/picking-a-router.\";\n}\nfunction useDataRouterContext(hookName) {\n let ctx = React.useContext(DataRouterContext);\n !ctx ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : UNSAFE_invariant(false) : void 0;\n return ctx;\n}\nfunction useDataRouterState(hookName) {\n let state = React.useContext(DataRouterStateContext);\n !state ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : UNSAFE_invariant(false) : void 0;\n return state;\n}\nfunction useRouteContext(hookName) {\n let route = React.useContext(RouteContext);\n !route ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, getDataRouterConsoleError(hookName)) : UNSAFE_invariant(false) : void 0;\n return route;\n}\n\n// Internal version with hookName-aware debugging\nfunction useCurrentRouteId(hookName) {\n let route = useRouteContext(hookName);\n let thisRoute = route.matches[route.matches.length - 1];\n !thisRoute.route.id ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, hookName + \" can only be used on routes that contain a unique \\\"id\\\"\") : UNSAFE_invariant(false) : void 0;\n return thisRoute.route.id;\n}\n\n/**\n * Returns the ID for the nearest contextual route\n */\nfunction useRouteId() {\n return useCurrentRouteId(DataRouterStateHook.UseRouteId);\n}\n\n/**\n * Returns the current navigation, defaulting to an \"idle\" navigation when\n * no navigation is in progress\n */\nfunction useNavigation() {\n let state = useDataRouterState(DataRouterStateHook.UseNavigation);\n return state.navigation;\n}\n\n/**\n * Returns a revalidate function for manually triggering revalidation, as well\n * as the current state of any manual revalidations\n */\nfunction useRevalidator() {\n let dataRouterContext = useDataRouterContext(DataRouterHook.UseRevalidator);\n let state = useDataRouterState(DataRouterStateHook.UseRevalidator);\n return {\n revalidate: dataRouterContext.router.revalidate,\n state: state.revalidation\n };\n}\n\n/**\n * Returns the active route matches, useful for accessing loaderData for\n * parent/child routes or the route \"handle\" property\n */\nfunction useMatches() {\n let {\n matches,\n loaderData\n } = useDataRouterState(DataRouterStateHook.UseMatches);\n return React.useMemo(() => matches.map(match => {\n let {\n pathname,\n params\n } = match;\n // Note: This structure matches that created by createUseMatchesMatch\n // in the @remix-run/router , so if you change this please also change\n // that :) Eventually we'll DRY this up\n return {\n id: match.route.id,\n pathname,\n params,\n data: loaderData[match.route.id],\n handle: match.route.handle\n };\n }), [matches, loaderData]);\n}\n\n/**\n * Returns the loader data for the nearest ancestor Route loader\n */\nfunction useLoaderData() {\n let state = useDataRouterState(DataRouterStateHook.UseLoaderData);\n let routeId = useCurrentRouteId(DataRouterStateHook.UseLoaderData);\n if (state.errors && state.errors[routeId] != null) {\n console.error(\"You cannot `useLoaderData` in an errorElement (routeId: \" + routeId + \")\");\n return undefined;\n }\n return state.loaderData[routeId];\n}\n\n/**\n * Returns the loaderData for the given routeId\n */\nfunction useRouteLoaderData(routeId) {\n let state = useDataRouterState(DataRouterStateHook.UseRouteLoaderData);\n return state.loaderData[routeId];\n}\n\n/**\n * Returns the action data for the nearest ancestor Route action\n */\nfunction useActionData() {\n let state = useDataRouterState(DataRouterStateHook.UseActionData);\n let route = React.useContext(RouteContext);\n !route ? process.env.NODE_ENV !== \"production\" ? UNSAFE_invariant(false, \"useActionData must be used inside a RouteContext\") : UNSAFE_invariant(false) : void 0;\n return Object.values((state == null ? void 0 : state.actionData) || {})[0];\n}\n\n/**\n * Returns the nearest ancestor Route error, which could be a loader/action\n * error or a render error. This is intended to be called from your\n * ErrorBoundary/errorElement to display a proper error message.\n */\nfunction useRouteError() {\n var _state$errors;\n let error = React.useContext(RouteErrorContext);\n let state = useDataRouterState(DataRouterStateHook.UseRouteError);\n let routeId = useCurrentRouteId(DataRouterStateHook.UseRouteError);\n\n // If this was a render error, we put it in a RouteError context inside\n // of RenderErrorBoundary\n if (error) {\n return error;\n }\n\n // Otherwise look for errors from our data router state\n return (_state$errors = state.errors) == null ? void 0 : _state$errors[routeId];\n}\n\n/**\n * Returns the happy-path data from the nearest ancestor value\n */\nfunction useAsyncValue() {\n let value = React.useContext(AwaitContext);\n return value == null ? void 0 : value._data;\n}\n\n/**\n * Returns the error from the nearest ancestor value\n */\nfunction useAsyncError() {\n let value = React.useContext(AwaitContext);\n return value == null ? void 0 : value._error;\n}\nlet blockerId = 0;\n\n/**\n * Allow the application to block navigations within the SPA and present the\n * user a confirmation dialog to confirm the navigation. Mostly used to avoid\n * using half-filled form data. This does not handle hard-reloads or\n * cross-origin navigations.\n */\nfunction useBlocker(shouldBlock) {\n let {\n router\n } = useDataRouterContext(DataRouterHook.UseBlocker);\n let state = useDataRouterState(DataRouterStateHook.UseBlocker);\n let [blockerKey] = React.useState(() => String(++blockerId));\n let blockerFunction = React.useCallback(args => {\n return typeof shouldBlock === \"function\" ? !!shouldBlock(args) : !!shouldBlock;\n }, [shouldBlock]);\n let blocker = router.getBlocker(blockerKey, blockerFunction);\n\n // Cleanup on unmount\n React.useEffect(() => () => router.deleteBlocker(blockerKey), [router, blockerKey]);\n\n // Prefer the blocker from state since DataRouterContext is memoized so this\n // ensures we update on blocker state updates\n return state.blockers.get(blockerKey) || blocker;\n}\n\n/**\n * Stable version of useNavigate that is used when we are in the context of\n * a RouterProvider.\n */\nfunction useNavigateStable() {\n let {\n router\n } = useDataRouterContext(DataRouterHook.UseNavigateStable);\n let id = useCurrentRouteId(DataRouterStateHook.UseNavigateStable);\n let activeRef = React.useRef(false);\n useIsomorphicLayoutEffect(() => {\n activeRef.current = true;\n });\n let navigate = React.useCallback(function (to, options) {\n if (options === void 0) {\n options = {};\n }\n process.env.NODE_ENV !== \"production\" ? UNSAFE_warning(activeRef.current, navigateEffectWarning) : void 0;\n\n // Short circuit here since if this happens on first render the navigate\n // is useless because we haven't wired up our router subscriber yet\n if (!activeRef.current) return;\n if (typeof to === \"number\") {\n router.navigate(to);\n } else {\n router.navigate(to, _extends({\n fromRouteId: id\n }, options));\n }\n }, [router, id]);\n return navigate;\n}\nconst alreadyWarned = {};\nfunction warningOnce(key, cond, message) {\n if (!cond && !alreadyWarned[key]) {\n alreadyWarned[key] = true;\n process.env.NODE_ENV !== \"production\" ? UNSAFE_warning(false, message) : void 0;\n }\n}\n\n// Webpack + React 17 fails to compile on the usage of `React.startTransition` or\n// `React[\"startTransition\"]` even if it's behind a feature detection of\n// `\"startTransition\" in React`. Moving this to a constant avoids the issue :/\nconst START_TRANSITION = \"startTransition\";\n\n/**\n * Given a Remix Router instance, render the appropriate UI\n */\nfunction RouterProvider(_ref) {\n let {\n fallbackElement,\n router\n } = _ref;\n // Need to use a layout effect here so we are subscribed early enough to\n // pick up on any render-driven redirects/navigations (useEffect/)\n let [state, setStateImpl] = React.useState(router.state);\n let setState = React.useCallback(newState => {\n START_TRANSITION in React ? React[START_TRANSITION](() => setStateImpl(newState)) : setStateImpl(newState);\n }, [setStateImpl]);\n React.useLayoutEffect(() => router.subscribe(setState), [router, setState]);\n let navigator = React.useMemo(() => {\n return {\n createHref: router.createHref,\n encodeLocation: router.encodeLocation,\n go: n => router.navigate(n),\n push: (to, state, opts) => router.navigate(to, {\n state,\n preventScrollReset: opts == null ? void 0 : opts.preventScrollReset\n }),\n replace: (to, state, opts) => router.navigate(to, {\n replace: true,\n state,\n preventScrollReset: opts == null ? void 0 : opts.preventScrollReset\n })\n };\n }, [router]);\n let basename = router.basename || \"/\";\n let dataRouterContext = React.useMemo(() => ({\n router,\n navigator,\n static: false,\n basename\n }), [router, navigator, basename]);\n\n // The fragment and {null} here are important! We need them to keep React 18's\n // useId happy when we are server-rendering since we may have a \n * ^\n * ```\n *\n * @type {State}\n */\n function continuationRawTagOpen(code) {\n if (code === 47) {\n effects.consume(code)\n buffer = ''\n return continuationRawEndTag\n }\n return continuation(code)\n }\n\n /**\n * In raw continuation, after ` | \n * ^^^^^^\n * ```\n *\n * @type {State}\n */\n function continuationRawEndTag(code) {\n if (code === 62) {\n const name = buffer.toLowerCase()\n if (htmlRawNames.includes(name)) {\n effects.consume(code)\n return continuationClose\n }\n return continuation(code)\n }\n if (asciiAlpha(code) && buffer.length < 8) {\n effects.consume(code)\n // @ts-expect-error: not null.\n buffer += String.fromCharCode(code)\n return continuationRawEndTag\n }\n return continuation(code)\n }\n\n /**\n * In cdata continuation, after `]`, expecting `]>`.\n *\n * ```markdown\n * > | &<]]>\n * ^\n * ```\n *\n * @type {State}\n */\n function continuationCdataInside(code) {\n if (code === 93) {\n effects.consume(code)\n return continuationDeclarationInside\n }\n return continuation(code)\n }\n\n /**\n * In declaration or instruction continuation, at `>`.\n *\n * ```markdown\n * > | \n * ^\n * > | \n * ^\n * > | \n * ^\n * > | \n * ^\n * > | &<]]>\n * ^\n * ```\n *\n * @type {State}\n */\n function continuationDeclarationInside(code) {\n if (code === 62) {\n effects.consume(code)\n return continuationClose\n }\n\n // More dashes.\n if (code === 45 && marker === 2) {\n effects.consume(code)\n return continuationDeclarationInside\n }\n return continuation(code)\n }\n\n /**\n * In closed continuation: everything we get until the eol/eof is part of it.\n *\n * ```markdown\n * > | \n * ^\n * ```\n *\n * @type {State}\n */\n function continuationClose(code) {\n if (code === null || markdownLineEnding(code)) {\n effects.exit('htmlFlowData')\n return continuationAfter(code)\n }\n effects.consume(code)\n return continuationClose\n }\n\n /**\n * Done.\n *\n * ```markdown\n * > | \n * ^\n * ```\n *\n * @type {State}\n */\n function continuationAfter(code) {\n effects.exit('htmlFlow')\n // // Feel free to interrupt.\n // tokenizer.interrupt = false\n // // No longer concrete.\n // tokenizer.concrete = false\n return ok(code)\n }\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeNonLazyContinuationStart(effects, ok, nok) {\n const self = this\n return start\n\n /**\n * At eol, before continuation.\n *\n * ```markdown\n * > | * ```js\n * ^\n * | b\n * ```\n *\n * @type {State}\n */\n function start(code) {\n if (markdownLineEnding(code)) {\n effects.enter('lineEnding')\n effects.consume(code)\n effects.exit('lineEnding')\n return after\n }\n return nok(code)\n }\n\n /**\n * A continuation.\n *\n * ```markdown\n * | * ```js\n * > | b\n * ^\n * ```\n *\n * @type {State}\n */\n function after(code) {\n return self.parser.lazy[self.now().line] ? nok(code) : ok(code)\n }\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeBlankLineBefore(effects, ok, nok) {\n return start\n\n /**\n * Before eol, expecting blank line.\n *\n * ```markdown\n * > |
\n * ^\n * |\n * ```\n *\n * @type {State}\n */\n function start(code) {\n effects.enter('lineEnding')\n effects.consume(code)\n effects.exit('lineEnding')\n return effects.attempt(blankLine, ok, nok)\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Code} Code\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Tokenizer} Tokenizer\n */\n\nimport {factorySpace} from 'micromark-factory-space'\nimport {\n asciiAlpha,\n asciiAlphanumeric,\n markdownLineEnding,\n markdownLineEndingOrSpace,\n markdownSpace\n} from 'micromark-util-character'\n/** @type {Construct} */\nexport const htmlText = {\n name: 'htmlText',\n tokenize: tokenizeHtmlText\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeHtmlText(effects, ok, nok) {\n const self = this\n /** @type {NonNullable | undefined} */\n let marker\n /** @type {number} */\n let index\n /** @type {State} */\n let returnState\n return start\n\n /**\n * Start of HTML (text).\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function start(code) {\n effects.enter('htmlText')\n effects.enter('htmlTextData')\n effects.consume(code)\n return open\n }\n\n /**\n * After `<`, at tag name or other stuff.\n *\n * ```markdown\n * > | a c\n * ^\n * > | a c\n * ^\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function open(code) {\n if (code === 33) {\n effects.consume(code)\n return declarationOpen\n }\n if (code === 47) {\n effects.consume(code)\n return tagCloseStart\n }\n if (code === 63) {\n effects.consume(code)\n return instruction\n }\n\n // ASCII alphabetical.\n if (asciiAlpha(code)) {\n effects.consume(code)\n return tagOpen\n }\n return nok(code)\n }\n\n /**\n * After ` | a c\n * ^\n * > | a c\n * ^\n * > | a &<]]> c\n * ^\n * ```\n *\n * @type {State}\n */\n function declarationOpen(code) {\n if (code === 45) {\n effects.consume(code)\n return commentOpenInside\n }\n if (code === 91) {\n effects.consume(code)\n index = 0\n return cdataOpenInside\n }\n if (asciiAlpha(code)) {\n effects.consume(code)\n return declaration\n }\n return nok(code)\n }\n\n /**\n * In a comment, after ` | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function commentOpenInside(code) {\n if (code === 45) {\n effects.consume(code)\n return commentEnd\n }\n return nok(code)\n }\n\n /**\n * In comment.\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function comment(code) {\n if (code === null) {\n return nok(code)\n }\n if (code === 45) {\n effects.consume(code)\n return commentClose\n }\n if (markdownLineEnding(code)) {\n returnState = comment\n return lineEndingBefore(code)\n }\n effects.consume(code)\n return comment\n }\n\n /**\n * In comment, after `-`.\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function commentClose(code) {\n if (code === 45) {\n effects.consume(code)\n return commentEnd\n }\n return comment(code)\n }\n\n /**\n * In comment, after `--`.\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function commentEnd(code) {\n return code === 62\n ? end(code)\n : code === 45\n ? commentClose(code)\n : comment(code)\n }\n\n /**\n * After ` | a &<]]> b\n * ^^^^^^\n * ```\n *\n * @type {State}\n */\n function cdataOpenInside(code) {\n const value = 'CDATA['\n if (code === value.charCodeAt(index++)) {\n effects.consume(code)\n return index === value.length ? cdata : cdataOpenInside\n }\n return nok(code)\n }\n\n /**\n * In CDATA.\n *\n * ```markdown\n * > | a &<]]> b\n * ^^^\n * ```\n *\n * @type {State}\n */\n function cdata(code) {\n if (code === null) {\n return nok(code)\n }\n if (code === 93) {\n effects.consume(code)\n return cdataClose\n }\n if (markdownLineEnding(code)) {\n returnState = cdata\n return lineEndingBefore(code)\n }\n effects.consume(code)\n return cdata\n }\n\n /**\n * In CDATA, after `]`, at another `]`.\n *\n * ```markdown\n * > | a &<]]> b\n * ^\n * ```\n *\n * @type {State}\n */\n function cdataClose(code) {\n if (code === 93) {\n effects.consume(code)\n return cdataEnd\n }\n return cdata(code)\n }\n\n /**\n * In CDATA, after `]]`, at `>`.\n *\n * ```markdown\n * > | a &<]]> b\n * ^\n * ```\n *\n * @type {State}\n */\n function cdataEnd(code) {\n if (code === 62) {\n return end(code)\n }\n if (code === 93) {\n effects.consume(code)\n return cdataEnd\n }\n return cdata(code)\n }\n\n /**\n * In declaration.\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function declaration(code) {\n if (code === null || code === 62) {\n return end(code)\n }\n if (markdownLineEnding(code)) {\n returnState = declaration\n return lineEndingBefore(code)\n }\n effects.consume(code)\n return declaration\n }\n\n /**\n * In instruction.\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function instruction(code) {\n if (code === null) {\n return nok(code)\n }\n if (code === 63) {\n effects.consume(code)\n return instructionClose\n }\n if (markdownLineEnding(code)) {\n returnState = instruction\n return lineEndingBefore(code)\n }\n effects.consume(code)\n return instruction\n }\n\n /**\n * In instruction, after `?`, at `>`.\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function instructionClose(code) {\n return code === 62 ? end(code) : instruction(code)\n }\n\n /**\n * After ` | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function tagCloseStart(code) {\n // ASCII alphabetical.\n if (asciiAlpha(code)) {\n effects.consume(code)\n return tagClose\n }\n return nok(code)\n }\n\n /**\n * After ` | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function tagClose(code) {\n // ASCII alphanumerical and `-`.\n if (code === 45 || asciiAlphanumeric(code)) {\n effects.consume(code)\n return tagClose\n }\n return tagCloseBetween(code)\n }\n\n /**\n * In closing tag, after tag name.\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function tagCloseBetween(code) {\n if (markdownLineEnding(code)) {\n returnState = tagCloseBetween\n return lineEndingBefore(code)\n }\n if (markdownSpace(code)) {\n effects.consume(code)\n return tagCloseBetween\n }\n return end(code)\n }\n\n /**\n * After ` | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function tagOpen(code) {\n // ASCII alphanumerical and `-`.\n if (code === 45 || asciiAlphanumeric(code)) {\n effects.consume(code)\n return tagOpen\n }\n if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {\n return tagOpenBetween(code)\n }\n return nok(code)\n }\n\n /**\n * In opening tag, after tag name.\n *\n * ```markdown\n * > | a c\n * ^\n * ```\n *\n * @type {State}\n */\n function tagOpenBetween(code) {\n if (code === 47) {\n effects.consume(code)\n return end\n }\n\n // ASCII alphabetical and `:` and `_`.\n if (code === 58 || code === 95 || asciiAlpha(code)) {\n effects.consume(code)\n return tagOpenAttributeName\n }\n if (markdownLineEnding(code)) {\n returnState = tagOpenBetween\n return lineEndingBefore(code)\n }\n if (markdownSpace(code)) {\n effects.consume(code)\n return tagOpenBetween\n }\n return end(code)\n }\n\n /**\n * In attribute name.\n *\n * ```markdown\n * > | a d\n * ^\n * ```\n *\n * @type {State}\n */\n function tagOpenAttributeName(code) {\n // ASCII alphabetical and `-`, `.`, `:`, and `_`.\n if (\n code === 45 ||\n code === 46 ||\n code === 58 ||\n code === 95 ||\n asciiAlphanumeric(code)\n ) {\n effects.consume(code)\n return tagOpenAttributeName\n }\n return tagOpenAttributeNameAfter(code)\n }\n\n /**\n * After attribute name, before initializer, the end of the tag, or\n * whitespace.\n *\n * ```markdown\n * > | a d\n * ^\n * ```\n *\n * @type {State}\n */\n function tagOpenAttributeNameAfter(code) {\n if (code === 61) {\n effects.consume(code)\n return tagOpenAttributeValueBefore\n }\n if (markdownLineEnding(code)) {\n returnState = tagOpenAttributeNameAfter\n return lineEndingBefore(code)\n }\n if (markdownSpace(code)) {\n effects.consume(code)\n return tagOpenAttributeNameAfter\n }\n return tagOpenBetween(code)\n }\n\n /**\n * Before unquoted, double quoted, or single quoted attribute value, allowing\n * whitespace.\n *\n * ```markdown\n * > | a e\n * ^\n * ```\n *\n * @type {State}\n */\n function tagOpenAttributeValueBefore(code) {\n if (\n code === null ||\n code === 60 ||\n code === 61 ||\n code === 62 ||\n code === 96\n ) {\n return nok(code)\n }\n if (code === 34 || code === 39) {\n effects.consume(code)\n marker = code\n return tagOpenAttributeValueQuoted\n }\n if (markdownLineEnding(code)) {\n returnState = tagOpenAttributeValueBefore\n return lineEndingBefore(code)\n }\n if (markdownSpace(code)) {\n effects.consume(code)\n return tagOpenAttributeValueBefore\n }\n effects.consume(code)\n return tagOpenAttributeValueUnquoted\n }\n\n /**\n * In double or single quoted attribute value.\n *\n * ```markdown\n * > | a e\n * ^\n * ```\n *\n * @type {State}\n */\n function tagOpenAttributeValueQuoted(code) {\n if (code === marker) {\n effects.consume(code)\n marker = undefined\n return tagOpenAttributeValueQuotedAfter\n }\n if (code === null) {\n return nok(code)\n }\n if (markdownLineEnding(code)) {\n returnState = tagOpenAttributeValueQuoted\n return lineEndingBefore(code)\n }\n effects.consume(code)\n return tagOpenAttributeValueQuoted\n }\n\n /**\n * In unquoted attribute value.\n *\n * ```markdown\n * > | a e\n * ^\n * ```\n *\n * @type {State}\n */\n function tagOpenAttributeValueUnquoted(code) {\n if (\n code === null ||\n code === 34 ||\n code === 39 ||\n code === 60 ||\n code === 61 ||\n code === 96\n ) {\n return nok(code)\n }\n if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {\n return tagOpenBetween(code)\n }\n effects.consume(code)\n return tagOpenAttributeValueUnquoted\n }\n\n /**\n * After double or single quoted attribute value, before whitespace or the end\n * of the tag.\n *\n * ```markdown\n * > | a e\n * ^\n * ```\n *\n * @type {State}\n */\n function tagOpenAttributeValueQuotedAfter(code) {\n if (code === 47 || code === 62 || markdownLineEndingOrSpace(code)) {\n return tagOpenBetween(code)\n }\n return nok(code)\n }\n\n /**\n * In certain circumstances of a tag where only an `>` is allowed.\n *\n * ```markdown\n * > | a e\n * ^\n * ```\n *\n * @type {State}\n */\n function end(code) {\n if (code === 62) {\n effects.consume(code)\n effects.exit('htmlTextData')\n effects.exit('htmlText')\n return ok\n }\n return nok(code)\n }\n\n /**\n * At eol.\n *\n * > 👉 **Note**: we can’t have blank lines in text, so no need to worry about\n * > empty tokens.\n *\n * ```markdown\n * > | a \n * ```\n *\n * @type {State}\n */\n function lineEndingBefore(code) {\n effects.exit('htmlTextData')\n effects.enter('lineEnding')\n effects.consume(code)\n effects.exit('lineEnding')\n return lineEndingAfter\n }\n\n /**\n * After eol, at optional whitespace.\n *\n * > 👉 **Note**: we can’t have blank lines in text, so no need to worry about\n * > empty tokens.\n *\n * ```markdown\n * | a \n * ^\n * ```\n *\n * @type {State}\n */\n function lineEndingAfter(code) {\n // Always populated by defaults.\n\n return markdownSpace(code)\n ? factorySpace(\n effects,\n lineEndingAfterPrefix,\n 'linePrefix',\n self.parser.constructs.disable.null.includes('codeIndented')\n ? undefined\n : 4\n )(code)\n : lineEndingAfterPrefix(code)\n }\n\n /**\n * After eol, after optional whitespace.\n *\n * > 👉 **Note**: we can’t have blank lines in text, so no need to worry about\n * > empty tokens.\n *\n * ```markdown\n * | a \n * ^\n * ```\n *\n * @type {State}\n */\n function lineEndingAfterPrefix(code) {\n effects.enter('htmlTextData')\n return returnState(code)\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').Event} Event\n * @typedef {import('micromark-util-types').Resolver} Resolver\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').Token} Token\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Tokenizer} Tokenizer\n */\n\nimport {factoryDestination} from 'micromark-factory-destination'\nimport {factoryLabel} from 'micromark-factory-label'\nimport {factoryTitle} from 'micromark-factory-title'\nimport {factoryWhitespace} from 'micromark-factory-whitespace'\nimport {markdownLineEndingOrSpace} from 'micromark-util-character'\nimport {push, splice} from 'micromark-util-chunked'\nimport {normalizeIdentifier} from 'micromark-util-normalize-identifier'\nimport {resolveAll} from 'micromark-util-resolve-all'\n/** @type {Construct} */\nexport const labelEnd = {\n name: 'labelEnd',\n tokenize: tokenizeLabelEnd,\n resolveTo: resolveToLabelEnd,\n resolveAll: resolveAllLabelEnd\n}\n\n/** @type {Construct} */\nconst resourceConstruct = {\n tokenize: tokenizeResource\n}\n/** @type {Construct} */\nconst referenceFullConstruct = {\n tokenize: tokenizeReferenceFull\n}\n/** @type {Construct} */\nconst referenceCollapsedConstruct = {\n tokenize: tokenizeReferenceCollapsed\n}\n\n/** @type {Resolver} */\nfunction resolveAllLabelEnd(events) {\n let index = -1\n while (++index < events.length) {\n const token = events[index][1]\n if (\n token.type === 'labelImage' ||\n token.type === 'labelLink' ||\n token.type === 'labelEnd'\n ) {\n // Remove the marker.\n events.splice(index + 1, token.type === 'labelImage' ? 4 : 2)\n token.type = 'data'\n index++\n }\n }\n return events\n}\n\n/** @type {Resolver} */\nfunction resolveToLabelEnd(events, context) {\n let index = events.length\n let offset = 0\n /** @type {Token} */\n let token\n /** @type {number | undefined} */\n let open\n /** @type {number | undefined} */\n let close\n /** @type {Array} */\n let media\n\n // Find an opening.\n while (index--) {\n token = events[index][1]\n if (open) {\n // If we see another link, or inactive link label, we’ve been here before.\n if (\n token.type === 'link' ||\n (token.type === 'labelLink' && token._inactive)\n ) {\n break\n }\n\n // Mark other link openings as inactive, as we can’t have links in\n // links.\n if (events[index][0] === 'enter' && token.type === 'labelLink') {\n token._inactive = true\n }\n } else if (close) {\n if (\n events[index][0] === 'enter' &&\n (token.type === 'labelImage' || token.type === 'labelLink') &&\n !token._balanced\n ) {\n open = index\n if (token.type !== 'labelLink') {\n offset = 2\n break\n }\n }\n } else if (token.type === 'labelEnd') {\n close = index\n }\n }\n const group = {\n type: events[open][1].type === 'labelLink' ? 'link' : 'image',\n start: Object.assign({}, events[open][1].start),\n end: Object.assign({}, events[events.length - 1][1].end)\n }\n const label = {\n type: 'label',\n start: Object.assign({}, events[open][1].start),\n end: Object.assign({}, events[close][1].end)\n }\n const text = {\n type: 'labelText',\n start: Object.assign({}, events[open + offset + 2][1].end),\n end: Object.assign({}, events[close - 2][1].start)\n }\n media = [\n ['enter', group, context],\n ['enter', label, context]\n ]\n\n // Opening marker.\n media = push(media, events.slice(open + 1, open + offset + 3))\n\n // Text open.\n media = push(media, [['enter', text, context]])\n\n // Always populated by defaults.\n\n // Between.\n media = push(\n media,\n resolveAll(\n context.parser.constructs.insideSpan.null,\n events.slice(open + offset + 4, close - 3),\n context\n )\n )\n\n // Text close, marker close, label close.\n media = push(media, [\n ['exit', text, context],\n events[close - 2],\n events[close - 1],\n ['exit', label, context]\n ])\n\n // Reference, resource, or so.\n media = push(media, events.slice(close + 1))\n\n // Media close.\n media = push(media, [['exit', group, context]])\n splice(events, open, events.length, media)\n return events\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeLabelEnd(effects, ok, nok) {\n const self = this\n let index = self.events.length\n /** @type {Token} */\n let labelStart\n /** @type {boolean} */\n let defined\n\n // Find an opening.\n while (index--) {\n if (\n (self.events[index][1].type === 'labelImage' ||\n self.events[index][1].type === 'labelLink') &&\n !self.events[index][1]._balanced\n ) {\n labelStart = self.events[index][1]\n break\n }\n }\n return start\n\n /**\n * Start of label end.\n *\n * ```markdown\n * > | [a](b) c\n * ^\n * > | [a][b] c\n * ^\n * > | [a][] b\n * ^\n * > | [a] b\n * ```\n *\n * @type {State}\n */\n function start(code) {\n // If there is not an okay opening.\n if (!labelStart) {\n return nok(code)\n }\n\n // If the corresponding label (link) start is marked as inactive,\n // it means we’d be wrapping a link, like this:\n //\n // ```markdown\n // > | a [b [c](d) e](f) g.\n // ^\n // ```\n //\n // We can’t have that, so it’s just balanced brackets.\n if (labelStart._inactive) {\n return labelEndNok(code)\n }\n defined = self.parser.defined.includes(\n normalizeIdentifier(\n self.sliceSerialize({\n start: labelStart.end,\n end: self.now()\n })\n )\n )\n effects.enter('labelEnd')\n effects.enter('labelMarker')\n effects.consume(code)\n effects.exit('labelMarker')\n effects.exit('labelEnd')\n return after\n }\n\n /**\n * After `]`.\n *\n * ```markdown\n * > | [a](b) c\n * ^\n * > | [a][b] c\n * ^\n * > | [a][] b\n * ^\n * > | [a] b\n * ^\n * ```\n *\n * @type {State}\n */\n function after(code) {\n // Note: `markdown-rs` also parses GFM footnotes here, which for us is in\n // an extension.\n\n // Resource (`[asd](fgh)`)?\n if (code === 40) {\n return effects.attempt(\n resourceConstruct,\n labelEndOk,\n defined ? labelEndOk : labelEndNok\n )(code)\n }\n\n // Full (`[asd][fgh]`) or collapsed (`[asd][]`) reference?\n if (code === 91) {\n return effects.attempt(\n referenceFullConstruct,\n labelEndOk,\n defined ? referenceNotFull : labelEndNok\n )(code)\n }\n\n // Shortcut (`[asd]`) reference?\n return defined ? labelEndOk(code) : labelEndNok(code)\n }\n\n /**\n * After `]`, at `[`, but not at a full reference.\n *\n * > 👉 **Note**: we only get here if the label is defined.\n *\n * ```markdown\n * > | [a][] b\n * ^\n * > | [a] b\n * ^\n * ```\n *\n * @type {State}\n */\n function referenceNotFull(code) {\n return effects.attempt(\n referenceCollapsedConstruct,\n labelEndOk,\n labelEndNok\n )(code)\n }\n\n /**\n * Done, we found something.\n *\n * ```markdown\n * > | [a](b) c\n * ^\n * > | [a][b] c\n * ^\n * > | [a][] b\n * ^\n * > | [a] b\n * ^\n * ```\n *\n * @type {State}\n */\n function labelEndOk(code) {\n // Note: `markdown-rs` does a bunch of stuff here.\n return ok(code)\n }\n\n /**\n * Done, it’s nothing.\n *\n * There was an okay opening, but we didn’t match anything.\n *\n * ```markdown\n * > | [a](b c\n * ^\n * > | [a][b c\n * ^\n * > | [a] b\n * ^\n * ```\n *\n * @type {State}\n */\n function labelEndNok(code) {\n labelStart._balanced = true\n return nok(code)\n }\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeResource(effects, ok, nok) {\n return resourceStart\n\n /**\n * At a resource.\n *\n * ```markdown\n * > | [a](b) c\n * ^\n * ```\n *\n * @type {State}\n */\n function resourceStart(code) {\n effects.enter('resource')\n effects.enter('resourceMarker')\n effects.consume(code)\n effects.exit('resourceMarker')\n return resourceBefore\n }\n\n /**\n * In resource, after `(`, at optional whitespace.\n *\n * ```markdown\n * > | [a](b) c\n * ^\n * ```\n *\n * @type {State}\n */\n function resourceBefore(code) {\n return markdownLineEndingOrSpace(code)\n ? factoryWhitespace(effects, resourceOpen)(code)\n : resourceOpen(code)\n }\n\n /**\n * In resource, after optional whitespace, at `)` or a destination.\n *\n * ```markdown\n * > | [a](b) c\n * ^\n * ```\n *\n * @type {State}\n */\n function resourceOpen(code) {\n if (code === 41) {\n return resourceEnd(code)\n }\n return factoryDestination(\n effects,\n resourceDestinationAfter,\n resourceDestinationMissing,\n 'resourceDestination',\n 'resourceDestinationLiteral',\n 'resourceDestinationLiteralMarker',\n 'resourceDestinationRaw',\n 'resourceDestinationString',\n 32\n )(code)\n }\n\n /**\n * In resource, after destination, at optional whitespace.\n *\n * ```markdown\n * > | [a](b) c\n * ^\n * ```\n *\n * @type {State}\n */\n function resourceDestinationAfter(code) {\n return markdownLineEndingOrSpace(code)\n ? factoryWhitespace(effects, resourceBetween)(code)\n : resourceEnd(code)\n }\n\n /**\n * At invalid destination.\n *\n * ```markdown\n * > | [a](<<) b\n * ^\n * ```\n *\n * @type {State}\n */\n function resourceDestinationMissing(code) {\n return nok(code)\n }\n\n /**\n * In resource, after destination and whitespace, at `(` or title.\n *\n * ```markdown\n * > | [a](b ) c\n * ^\n * ```\n *\n * @type {State}\n */\n function resourceBetween(code) {\n if (code === 34 || code === 39 || code === 40) {\n return factoryTitle(\n effects,\n resourceTitleAfter,\n nok,\n 'resourceTitle',\n 'resourceTitleMarker',\n 'resourceTitleString'\n )(code)\n }\n return resourceEnd(code)\n }\n\n /**\n * In resource, after title, at optional whitespace.\n *\n * ```markdown\n * > | [a](b \"c\") d\n * ^\n * ```\n *\n * @type {State}\n */\n function resourceTitleAfter(code) {\n return markdownLineEndingOrSpace(code)\n ? factoryWhitespace(effects, resourceEnd)(code)\n : resourceEnd(code)\n }\n\n /**\n * In resource, at `)`.\n *\n * ```markdown\n * > | [a](b) d\n * ^\n * ```\n *\n * @type {State}\n */\n function resourceEnd(code) {\n if (code === 41) {\n effects.enter('resourceMarker')\n effects.consume(code)\n effects.exit('resourceMarker')\n effects.exit('resource')\n return ok\n }\n return nok(code)\n }\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeReferenceFull(effects, ok, nok) {\n const self = this\n return referenceFull\n\n /**\n * In a reference (full), at the `[`.\n *\n * ```markdown\n * > | [a][b] d\n * ^\n * ```\n *\n * @type {State}\n */\n function referenceFull(code) {\n return factoryLabel.call(\n self,\n effects,\n referenceFullAfter,\n referenceFullMissing,\n 'reference',\n 'referenceMarker',\n 'referenceString'\n )(code)\n }\n\n /**\n * In a reference (full), after `]`.\n *\n * ```markdown\n * > | [a][b] d\n * ^\n * ```\n *\n * @type {State}\n */\n function referenceFullAfter(code) {\n return self.parser.defined.includes(\n normalizeIdentifier(\n self.sliceSerialize(self.events[self.events.length - 1][1]).slice(1, -1)\n )\n )\n ? ok(code)\n : nok(code)\n }\n\n /**\n * In reference (full) that was missing.\n *\n * ```markdown\n * > | [a][b d\n * ^\n * ```\n *\n * @type {State}\n */\n function referenceFullMissing(code) {\n return nok(code)\n }\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeReferenceCollapsed(effects, ok, nok) {\n return referenceCollapsedStart\n\n /**\n * In reference (collapsed), at `[`.\n *\n * > 👉 **Note**: we only get here if the label is defined.\n *\n * ```markdown\n * > | [a][] d\n * ^\n * ```\n *\n * @type {State}\n */\n function referenceCollapsedStart(code) {\n // We only attempt a collapsed label if there’s a `[`.\n\n effects.enter('reference')\n effects.enter('referenceMarker')\n effects.consume(code)\n effects.exit('referenceMarker')\n return referenceCollapsedOpen\n }\n\n /**\n * In reference (collapsed), at `]`.\n *\n * > 👉 **Note**: we only get here if the label is defined.\n *\n * ```markdown\n * > | [a][] d\n * ^\n * ```\n *\n * @type {State}\n */\n function referenceCollapsedOpen(code) {\n if (code === 93) {\n effects.enter('referenceMarker')\n effects.consume(code)\n effects.exit('referenceMarker')\n effects.exit('reference')\n return ok\n }\n return nok(code)\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Tokenizer} Tokenizer\n */\n\nimport {labelEnd} from './label-end.js'\n\n/** @type {Construct} */\nexport const labelStartImage = {\n name: 'labelStartImage',\n tokenize: tokenizeLabelStartImage,\n resolveAll: labelEnd.resolveAll\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeLabelStartImage(effects, ok, nok) {\n const self = this\n return start\n\n /**\n * Start of label (image) start.\n *\n * ```markdown\n * > | a ![b] c\n * ^\n * ```\n *\n * @type {State}\n */\n function start(code) {\n effects.enter('labelImage')\n effects.enter('labelImageMarker')\n effects.consume(code)\n effects.exit('labelImageMarker')\n return open\n }\n\n /**\n * After `!`, at `[`.\n *\n * ```markdown\n * > | a ![b] c\n * ^\n * ```\n *\n * @type {State}\n */\n function open(code) {\n if (code === 91) {\n effects.enter('labelMarker')\n effects.consume(code)\n effects.exit('labelMarker')\n effects.exit('labelImage')\n return after\n }\n return nok(code)\n }\n\n /**\n * After `![`.\n *\n * ```markdown\n * > | a ![b] c\n * ^\n * ```\n *\n * This is needed in because, when GFM footnotes are enabled, images never\n * form when started with a `^`.\n * Instead, links form:\n *\n * ```markdown\n * ![^a](b)\n *\n * ![^a][b]\n *\n * [b]: c\n * ```\n *\n * ```html\n *

!^a

\n *

!^a

\n * ```\n *\n * @type {State}\n */\n function after(code) {\n // To do: use a new field to do this, this is still needed for\n // `micromark-extension-gfm-footnote`, but the `label-start-link`\n // behavior isn’t.\n // Hidden footnotes hook.\n /* c8 ignore next 3 */\n return code === 94 && '_hiddenFootnoteSupport' in self.parser.constructs\n ? nok(code)\n : ok(code)\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Tokenizer} Tokenizer\n */\n\nimport {labelEnd} from './label-end.js'\n\n/** @type {Construct} */\nexport const labelStartLink = {\n name: 'labelStartLink',\n tokenize: tokenizeLabelStartLink,\n resolveAll: labelEnd.resolveAll\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeLabelStartLink(effects, ok, nok) {\n const self = this\n return start\n\n /**\n * Start of label (link) start.\n *\n * ```markdown\n * > | a [b] c\n * ^\n * ```\n *\n * @type {State}\n */\n function start(code) {\n effects.enter('labelLink')\n effects.enter('labelMarker')\n effects.consume(code)\n effects.exit('labelMarker')\n effects.exit('labelLink')\n return after\n }\n\n /** @type {State} */\n function after(code) {\n // To do: this isn’t needed in `micromark-extension-gfm-footnote`,\n // remove.\n // Hidden footnotes hook.\n /* c8 ignore next 3 */\n return code === 94 && '_hiddenFootnoteSupport' in self.parser.constructs\n ? nok(code)\n : ok(code)\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Tokenizer} Tokenizer\n */\n\nimport {factorySpace} from 'micromark-factory-space'\nimport {markdownLineEnding} from 'micromark-util-character'\n/** @type {Construct} */\nexport const lineEnding = {\n name: 'lineEnding',\n tokenize: tokenizeLineEnding\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeLineEnding(effects, ok) {\n return start\n\n /** @type {State} */\n function start(code) {\n effects.enter('lineEnding')\n effects.consume(code)\n effects.exit('lineEnding')\n return factorySpace(effects, ok, 'linePrefix')\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Code} Code\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Tokenizer} Tokenizer\n */\n\nimport {factorySpace} from 'micromark-factory-space'\nimport {markdownLineEnding, markdownSpace} from 'micromark-util-character'\n/** @type {Construct} */\nexport const thematicBreak = {\n name: 'thematicBreak',\n tokenize: tokenizeThematicBreak\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeThematicBreak(effects, ok, nok) {\n let size = 0\n /** @type {NonNullable} */\n let marker\n return start\n\n /**\n * Start of thematic break.\n *\n * ```markdown\n * > | ***\n * ^\n * ```\n *\n * @type {State}\n */\n function start(code) {\n effects.enter('thematicBreak')\n // To do: parse indent like `markdown-rs`.\n return before(code)\n }\n\n /**\n * After optional whitespace, at marker.\n *\n * ```markdown\n * > | ***\n * ^\n * ```\n *\n * @type {State}\n */\n function before(code) {\n marker = code\n return atBreak(code)\n }\n\n /**\n * After something, before something else.\n *\n * ```markdown\n * > | ***\n * ^\n * ```\n *\n * @type {State}\n */\n function atBreak(code) {\n if (code === marker) {\n effects.enter('thematicBreakSequence')\n return sequence(code)\n }\n if (size >= 3 && (code === null || markdownLineEnding(code))) {\n effects.exit('thematicBreak')\n return ok(code)\n }\n return nok(code)\n }\n\n /**\n * In sequence.\n *\n * ```markdown\n * > | ***\n * ^\n * ```\n *\n * @type {State}\n */\n function sequence(code) {\n if (code === marker) {\n effects.consume(code)\n size++\n return sequence\n }\n effects.exit('thematicBreakSequence')\n return markdownSpace(code)\n ? factorySpace(effects, atBreak, 'whitespace')(code)\n : atBreak(code)\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Code} Code\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').ContainerState} ContainerState\n * @typedef {import('micromark-util-types').Exiter} Exiter\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Tokenizer} Tokenizer\n */\n\nimport {factorySpace} from 'micromark-factory-space'\nimport {asciiDigit, markdownSpace} from 'micromark-util-character'\nimport {blankLine} from './blank-line.js'\nimport {thematicBreak} from './thematic-break.js'\n\n/** @type {Construct} */\nexport const list = {\n name: 'list',\n tokenize: tokenizeListStart,\n continuation: {\n tokenize: tokenizeListContinuation\n },\n exit: tokenizeListEnd\n}\n\n/** @type {Construct} */\nconst listItemPrefixWhitespaceConstruct = {\n tokenize: tokenizeListItemPrefixWhitespace,\n partial: true\n}\n\n/** @type {Construct} */\nconst indentConstruct = {\n tokenize: tokenizeIndent,\n partial: true\n}\n\n// To do: `markdown-rs` parses list items on their own and later stitches them\n// together.\n\n/**\n * @type {Tokenizer}\n * @this {TokenizeContext}\n */\nfunction tokenizeListStart(effects, ok, nok) {\n const self = this\n const tail = self.events[self.events.length - 1]\n let initialSize =\n tail && tail[1].type === 'linePrefix'\n ? tail[2].sliceSerialize(tail[1], true).length\n : 0\n let size = 0\n return start\n\n /** @type {State} */\n function start(code) {\n const kind =\n self.containerState.type ||\n (code === 42 || code === 43 || code === 45\n ? 'listUnordered'\n : 'listOrdered')\n if (\n kind === 'listUnordered'\n ? !self.containerState.marker || code === self.containerState.marker\n : asciiDigit(code)\n ) {\n if (!self.containerState.type) {\n self.containerState.type = kind\n effects.enter(kind, {\n _container: true\n })\n }\n if (kind === 'listUnordered') {\n effects.enter('listItemPrefix')\n return code === 42 || code === 45\n ? effects.check(thematicBreak, nok, atMarker)(code)\n : atMarker(code)\n }\n if (!self.interrupt || code === 49) {\n effects.enter('listItemPrefix')\n effects.enter('listItemValue')\n return inside(code)\n }\n }\n return nok(code)\n }\n\n /** @type {State} */\n function inside(code) {\n if (asciiDigit(code) && ++size < 10) {\n effects.consume(code)\n return inside\n }\n if (\n (!self.interrupt || size < 2) &&\n (self.containerState.marker\n ? code === self.containerState.marker\n : code === 41 || code === 46)\n ) {\n effects.exit('listItemValue')\n return atMarker(code)\n }\n return nok(code)\n }\n\n /**\n * @type {State}\n **/\n function atMarker(code) {\n effects.enter('listItemMarker')\n effects.consume(code)\n effects.exit('listItemMarker')\n self.containerState.marker = self.containerState.marker || code\n return effects.check(\n blankLine,\n // Can’t be empty when interrupting.\n self.interrupt ? nok : onBlank,\n effects.attempt(\n listItemPrefixWhitespaceConstruct,\n endOfPrefix,\n otherPrefix\n )\n )\n }\n\n /** @type {State} */\n function onBlank(code) {\n self.containerState.initialBlankLine = true\n initialSize++\n return endOfPrefix(code)\n }\n\n /** @type {State} */\n function otherPrefix(code) {\n if (markdownSpace(code)) {\n effects.enter('listItemPrefixWhitespace')\n effects.consume(code)\n effects.exit('listItemPrefixWhitespace')\n return endOfPrefix\n }\n return nok(code)\n }\n\n /** @type {State} */\n function endOfPrefix(code) {\n self.containerState.size =\n initialSize +\n self.sliceSerialize(effects.exit('listItemPrefix'), true).length\n return ok(code)\n }\n}\n\n/**\n * @type {Tokenizer}\n * @this {TokenizeContext}\n */\nfunction tokenizeListContinuation(effects, ok, nok) {\n const self = this\n self.containerState._closeFlow = undefined\n return effects.check(blankLine, onBlank, notBlank)\n\n /** @type {State} */\n function onBlank(code) {\n self.containerState.furtherBlankLines =\n self.containerState.furtherBlankLines ||\n self.containerState.initialBlankLine\n\n // We have a blank line.\n // Still, try to consume at most the items size.\n return factorySpace(\n effects,\n ok,\n 'listItemIndent',\n self.containerState.size + 1\n )(code)\n }\n\n /** @type {State} */\n function notBlank(code) {\n if (self.containerState.furtherBlankLines || !markdownSpace(code)) {\n self.containerState.furtherBlankLines = undefined\n self.containerState.initialBlankLine = undefined\n return notInCurrentItem(code)\n }\n self.containerState.furtherBlankLines = undefined\n self.containerState.initialBlankLine = undefined\n return effects.attempt(indentConstruct, ok, notInCurrentItem)(code)\n }\n\n /** @type {State} */\n function notInCurrentItem(code) {\n // While we do continue, we signal that the flow should be closed.\n self.containerState._closeFlow = true\n // As we’re closing flow, we’re no longer interrupting.\n self.interrupt = undefined\n // Always populated by defaults.\n\n return factorySpace(\n effects,\n effects.attempt(list, ok, nok),\n 'linePrefix',\n self.parser.constructs.disable.null.includes('codeIndented')\n ? undefined\n : 4\n )(code)\n }\n}\n\n/**\n * @type {Tokenizer}\n * @this {TokenizeContext}\n */\nfunction tokenizeIndent(effects, ok, nok) {\n const self = this\n return factorySpace(\n effects,\n afterPrefix,\n 'listItemIndent',\n self.containerState.size + 1\n )\n\n /** @type {State} */\n function afterPrefix(code) {\n const tail = self.events[self.events.length - 1]\n return tail &&\n tail[1].type === 'listItemIndent' &&\n tail[2].sliceSerialize(tail[1], true).length === self.containerState.size\n ? ok(code)\n : nok(code)\n }\n}\n\n/**\n * @type {Exiter}\n * @this {TokenizeContext}\n */\nfunction tokenizeListEnd(effects) {\n effects.exit(this.containerState.type)\n}\n\n/**\n * @type {Tokenizer}\n * @this {TokenizeContext}\n */\nfunction tokenizeListItemPrefixWhitespace(effects, ok, nok) {\n const self = this\n\n // Always populated by defaults.\n\n return factorySpace(\n effects,\n afterPrefix,\n 'listItemPrefixWhitespace',\n self.parser.constructs.disable.null.includes('codeIndented')\n ? undefined\n : 4 + 1\n )\n\n /** @type {State} */\n function afterPrefix(code) {\n const tail = self.events[self.events.length - 1]\n return !markdownSpace(code) &&\n tail &&\n tail[1].type === 'listItemPrefixWhitespace'\n ? ok(code)\n : nok(code)\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Code} Code\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').Resolver} Resolver\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Tokenizer} Tokenizer\n */\n\nimport {factorySpace} from 'micromark-factory-space'\nimport {markdownLineEnding, markdownSpace} from 'micromark-util-character'\n/** @type {Construct} */\nexport const setextUnderline = {\n name: 'setextUnderline',\n tokenize: tokenizeSetextUnderline,\n resolveTo: resolveToSetextUnderline\n}\n\n/** @type {Resolver} */\nfunction resolveToSetextUnderline(events, context) {\n // To do: resolve like `markdown-rs`.\n let index = events.length\n /** @type {number | undefined} */\n let content\n /** @type {number | undefined} */\n let text\n /** @type {number | undefined} */\n let definition\n\n // Find the opening of the content.\n // It’ll always exist: we don’t tokenize if it isn’t there.\n while (index--) {\n if (events[index][0] === 'enter') {\n if (events[index][1].type === 'content') {\n content = index\n break\n }\n if (events[index][1].type === 'paragraph') {\n text = index\n }\n }\n // Exit\n else {\n if (events[index][1].type === 'content') {\n // Remove the content end (if needed we’ll add it later)\n events.splice(index, 1)\n }\n if (!definition && events[index][1].type === 'definition') {\n definition = index\n }\n }\n }\n const heading = {\n type: 'setextHeading',\n start: Object.assign({}, events[text][1].start),\n end: Object.assign({}, events[events.length - 1][1].end)\n }\n\n // Change the paragraph to setext heading text.\n events[text][1].type = 'setextHeadingText'\n\n // If we have definitions in the content, we’ll keep on having content,\n // but we need move it.\n if (definition) {\n events.splice(text, 0, ['enter', heading, context])\n events.splice(definition + 1, 0, ['exit', events[content][1], context])\n events[content][1].end = Object.assign({}, events[definition][1].end)\n } else {\n events[content][1] = heading\n }\n\n // Add the heading exit at the end.\n events.push(['exit', heading, context])\n return events\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Tokenizer}\n */\nfunction tokenizeSetextUnderline(effects, ok, nok) {\n const self = this\n /** @type {NonNullable} */\n let marker\n return start\n\n /**\n * At start of heading (setext) underline.\n *\n * ```markdown\n * | aa\n * > | ==\n * ^\n * ```\n *\n * @type {State}\n */\n function start(code) {\n let index = self.events.length\n /** @type {boolean | undefined} */\n let paragraph\n // Find an opening.\n while (index--) {\n // Skip enter/exit of line ending, line prefix, and content.\n // We can now either have a definition or a paragraph.\n if (\n self.events[index][1].type !== 'lineEnding' &&\n self.events[index][1].type !== 'linePrefix' &&\n self.events[index][1].type !== 'content'\n ) {\n paragraph = self.events[index][1].type === 'paragraph'\n break\n }\n }\n\n // To do: handle lazy/pierce like `markdown-rs`.\n // To do: parse indent like `markdown-rs`.\n if (!self.parser.lazy[self.now().line] && (self.interrupt || paragraph)) {\n effects.enter('setextHeadingLine')\n marker = code\n return before(code)\n }\n return nok(code)\n }\n\n /**\n * After optional whitespace, at `-` or `=`.\n *\n * ```markdown\n * | aa\n * > | ==\n * ^\n * ```\n *\n * @type {State}\n */\n function before(code) {\n effects.enter('setextHeadingLineSequence')\n return inside(code)\n }\n\n /**\n * In sequence.\n *\n * ```markdown\n * | aa\n * > | ==\n * ^\n * ```\n *\n * @type {State}\n */\n function inside(code) {\n if (code === marker) {\n effects.consume(code)\n return inside\n }\n effects.exit('setextHeadingLineSequence')\n return markdownSpace(code)\n ? factorySpace(effects, after, 'lineSuffix')(code)\n : after(code)\n }\n\n /**\n * After sequence, after optional whitespace.\n *\n * ```markdown\n * | aa\n * > | ==\n * ^\n * ```\n *\n * @type {State}\n */\n function after(code) {\n if (code === null || markdownLineEnding(code)) {\n effects.exit('setextHeadingLine')\n return ok(code)\n }\n return nok(code)\n }\n}\n","/**\n * @typedef {import('micromark-util-types').InitialConstruct} InitialConstruct\n * @typedef {import('micromark-util-types').Initializer} Initializer\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n */\n\nimport {blankLine, content} from 'micromark-core-commonmark'\nimport {factorySpace} from 'micromark-factory-space'\nimport {markdownLineEnding} from 'micromark-util-character'\n/** @type {InitialConstruct} */\nexport const flow = {\n tokenize: initializeFlow\n}\n\n/**\n * @this {TokenizeContext}\n * @type {Initializer}\n */\nfunction initializeFlow(effects) {\n const self = this\n const initial = effects.attempt(\n // Try to parse a blank line.\n blankLine,\n atBlankEnding,\n // Try to parse initial flow (essentially, only code).\n effects.attempt(\n this.parser.constructs.flowInitial,\n afterConstruct,\n factorySpace(\n effects,\n effects.attempt(\n this.parser.constructs.flow,\n afterConstruct,\n effects.attempt(content, afterConstruct)\n ),\n 'linePrefix'\n )\n )\n )\n return initial\n\n /** @type {State} */\n function atBlankEnding(code) {\n if (code === null) {\n effects.consume(code)\n return\n }\n effects.enter('lineEndingBlank')\n effects.consume(code)\n effects.exit('lineEndingBlank')\n self.currentConstruct = undefined\n return initial\n }\n\n /** @type {State} */\n function afterConstruct(code) {\n if (code === null) {\n effects.consume(code)\n return\n }\n effects.enter('lineEnding')\n effects.consume(code)\n effects.exit('lineEnding')\n self.currentConstruct = undefined\n return initial\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Code} Code\n * @typedef {import('micromark-util-types').InitialConstruct} InitialConstruct\n * @typedef {import('micromark-util-types').Initializer} Initializer\n * @typedef {import('micromark-util-types').Resolver} Resolver\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n */\n\nexport const resolver = {\n resolveAll: createResolver()\n}\nexport const string = initializeFactory('string')\nexport const text = initializeFactory('text')\n\n/**\n * @param {'string' | 'text'} field\n * @returns {InitialConstruct}\n */\nfunction initializeFactory(field) {\n return {\n tokenize: initializeText,\n resolveAll: createResolver(\n field === 'text' ? resolveAllLineSuffixes : undefined\n )\n }\n\n /**\n * @this {TokenizeContext}\n * @type {Initializer}\n */\n function initializeText(effects) {\n const self = this\n const constructs = this.parser.constructs[field]\n const text = effects.attempt(constructs, start, notText)\n return start\n\n /** @type {State} */\n function start(code) {\n return atBreak(code) ? text(code) : notText(code)\n }\n\n /** @type {State} */\n function notText(code) {\n if (code === null) {\n effects.consume(code)\n return\n }\n effects.enter('data')\n effects.consume(code)\n return data\n }\n\n /** @type {State} */\n function data(code) {\n if (atBreak(code)) {\n effects.exit('data')\n return text(code)\n }\n\n // Data.\n effects.consume(code)\n return data\n }\n\n /**\n * @param {Code} code\n * @returns {boolean}\n */\n function atBreak(code) {\n if (code === null) {\n return true\n }\n const list = constructs[code]\n let index = -1\n if (list) {\n // Always populated by defaults.\n\n while (++index < list.length) {\n const item = list[index]\n if (!item.previous || item.previous.call(self, self.previous)) {\n return true\n }\n }\n }\n return false\n }\n }\n}\n\n/**\n * @param {Resolver | undefined} [extraResolver]\n * @returns {Resolver}\n */\nfunction createResolver(extraResolver) {\n return resolveAllText\n\n /** @type {Resolver} */\n function resolveAllText(events, context) {\n let index = -1\n /** @type {number | undefined} */\n let enter\n\n // A rather boring computation (to merge adjacent `data` events) which\n // improves mm performance by 29%.\n while (++index <= events.length) {\n if (enter === undefined) {\n if (events[index] && events[index][1].type === 'data') {\n enter = index\n index++\n }\n } else if (!events[index] || events[index][1].type !== 'data') {\n // Don’t do anything if there is one data token.\n if (index !== enter + 2) {\n events[enter][1].end = events[index - 1][1].end\n events.splice(enter + 2, index - enter - 2)\n index = enter + 2\n }\n enter = undefined\n }\n }\n return extraResolver ? extraResolver(events, context) : events\n }\n}\n\n/**\n * A rather ugly set of instructions which again looks at chunks in the input\n * stream.\n * The reason to do this here is that it is *much* faster to parse in reverse.\n * And that we can’t hook into `null` to split the line suffix before an EOF.\n * To do: figure out if we can make this into a clean utility, or even in core.\n * As it will be useful for GFMs literal autolink extension (and maybe even\n * tables?)\n *\n * @type {Resolver}\n */\nfunction resolveAllLineSuffixes(events, context) {\n let eventIndex = 0 // Skip first.\n\n while (++eventIndex <= events.length) {\n if (\n (eventIndex === events.length ||\n events[eventIndex][1].type === 'lineEnding') &&\n events[eventIndex - 1][1].type === 'data'\n ) {\n const data = events[eventIndex - 1][1]\n const chunks = context.sliceStream(data)\n let index = chunks.length\n let bufferIndex = -1\n let size = 0\n /** @type {boolean | undefined} */\n let tabs\n while (index--) {\n const chunk = chunks[index]\n if (typeof chunk === 'string') {\n bufferIndex = chunk.length\n while (chunk.charCodeAt(bufferIndex - 1) === 32) {\n size++\n bufferIndex--\n }\n if (bufferIndex) break\n bufferIndex = -1\n }\n // Number\n else if (chunk === -2) {\n tabs = true\n size++\n } else if (chunk === -1) {\n // Empty\n } else {\n // Replacement character, exit.\n index++\n break\n }\n }\n if (size) {\n const token = {\n type:\n eventIndex === events.length || tabs || size < 2\n ? 'lineSuffix'\n : 'hardBreakTrailing',\n start: {\n line: data.end.line,\n column: data.end.column - size,\n offset: data.end.offset - size,\n _index: data.start._index + index,\n _bufferIndex: index\n ? bufferIndex\n : data.start._bufferIndex + bufferIndex\n },\n end: Object.assign({}, data.end)\n }\n data.end = Object.assign({}, token.start)\n if (data.start.offset === data.end.offset) {\n Object.assign(data, token)\n } else {\n events.splice(\n eventIndex,\n 0,\n ['enter', token, context],\n ['exit', token, context]\n )\n eventIndex += 2\n }\n }\n eventIndex++\n }\n }\n return events\n}\n","/**\n * @typedef {import('micromark-util-types').Chunk} Chunk\n * @typedef {import('micromark-util-types').Code} Code\n * @typedef {import('micromark-util-types').Construct} Construct\n * @typedef {import('micromark-util-types').ConstructRecord} ConstructRecord\n * @typedef {import('micromark-util-types').Effects} Effects\n * @typedef {import('micromark-util-types').InitialConstruct} InitialConstruct\n * @typedef {import('micromark-util-types').ParseContext} ParseContext\n * @typedef {import('micromark-util-types').Point} Point\n * @typedef {import('micromark-util-types').State} State\n * @typedef {import('micromark-util-types').Token} Token\n * @typedef {import('micromark-util-types').TokenType} TokenType\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n */\n\n/**\n * @callback Restore\n * @returns {void}\n *\n * @typedef Info\n * @property {Restore} restore\n * @property {number} from\n *\n * @callback ReturnHandle\n * Handle a successful run.\n * @param {Construct} construct\n * @param {Info} info\n * @returns {void}\n */\n\nimport {markdownLineEnding} from 'micromark-util-character'\nimport {push, splice} from 'micromark-util-chunked'\nimport {resolveAll} from 'micromark-util-resolve-all'\n/**\n * Create a tokenizer.\n * Tokenizers deal with one type of data (e.g., containers, flow, text).\n * The parser is the object dealing with it all.\n * `initialize` works like other constructs, except that only its `tokenize`\n * function is used, in which case it doesn’t receive an `ok` or `nok`.\n * `from` can be given to set the point before the first character, although\n * when further lines are indented, they must be set with `defineSkip`.\n *\n * @param {ParseContext} parser\n * @param {InitialConstruct} initialize\n * @param {Omit | undefined} [from]\n * @returns {TokenizeContext}\n */\nexport function createTokenizer(parser, initialize, from) {\n /** @type {Point} */\n let point = Object.assign(\n from\n ? Object.assign({}, from)\n : {\n line: 1,\n column: 1,\n offset: 0\n },\n {\n _index: 0,\n _bufferIndex: -1\n }\n )\n /** @type {Record} */\n const columnStart = {}\n /** @type {Array} */\n const resolveAllConstructs = []\n /** @type {Array} */\n let chunks = []\n /** @type {Array} */\n let stack = []\n /** @type {boolean | undefined} */\n let consumed = true\n\n /**\n * Tools used for tokenizing.\n *\n * @type {Effects}\n */\n const effects = {\n consume,\n enter,\n exit,\n attempt: constructFactory(onsuccessfulconstruct),\n check: constructFactory(onsuccessfulcheck),\n interrupt: constructFactory(onsuccessfulcheck, {\n interrupt: true\n })\n }\n\n /**\n * State and tools for resolving and serializing.\n *\n * @type {TokenizeContext}\n */\n const context = {\n previous: null,\n code: null,\n containerState: {},\n events: [],\n parser,\n sliceStream,\n sliceSerialize,\n now,\n defineSkip,\n write\n }\n\n /**\n * The state function.\n *\n * @type {State | void}\n */\n let state = initialize.tokenize.call(context, effects)\n\n /**\n * Track which character we expect to be consumed, to catch bugs.\n *\n * @type {Code}\n */\n let expectedCode\n if (initialize.resolveAll) {\n resolveAllConstructs.push(initialize)\n }\n return context\n\n /** @type {TokenizeContext['write']} */\n function write(slice) {\n chunks = push(chunks, slice)\n main()\n\n // Exit if we’re not done, resolve might change stuff.\n if (chunks[chunks.length - 1] !== null) {\n return []\n }\n addResult(initialize, 0)\n\n // Otherwise, resolve, and exit.\n context.events = resolveAll(resolveAllConstructs, context.events, context)\n return context.events\n }\n\n //\n // Tools.\n //\n\n /** @type {TokenizeContext['sliceSerialize']} */\n function sliceSerialize(token, expandTabs) {\n return serializeChunks(sliceStream(token), expandTabs)\n }\n\n /** @type {TokenizeContext['sliceStream']} */\n function sliceStream(token) {\n return sliceChunks(chunks, token)\n }\n\n /** @type {TokenizeContext['now']} */\n function now() {\n // This is a hot path, so we clone manually instead of `Object.assign({}, point)`\n const {line, column, offset, _index, _bufferIndex} = point\n return {\n line,\n column,\n offset,\n _index,\n _bufferIndex\n }\n }\n\n /** @type {TokenizeContext['defineSkip']} */\n function defineSkip(value) {\n columnStart[value.line] = value.column\n accountForPotentialSkip()\n }\n\n //\n // State management.\n //\n\n /**\n * Main loop (note that `_index` and `_bufferIndex` in `point` are modified by\n * `consume`).\n * Here is where we walk through the chunks, which either include strings of\n * several characters, or numerical character codes.\n * The reason to do this in a loop instead of a call is so the stack can\n * drain.\n *\n * @returns {void}\n */\n function main() {\n /** @type {number} */\n let chunkIndex\n while (point._index < chunks.length) {\n const chunk = chunks[point._index]\n\n // If we’re in a buffer chunk, loop through it.\n if (typeof chunk === 'string') {\n chunkIndex = point._index\n if (point._bufferIndex < 0) {\n point._bufferIndex = 0\n }\n while (\n point._index === chunkIndex &&\n point._bufferIndex < chunk.length\n ) {\n go(chunk.charCodeAt(point._bufferIndex))\n }\n } else {\n go(chunk)\n }\n }\n }\n\n /**\n * Deal with one code.\n *\n * @param {Code} code\n * @returns {void}\n */\n function go(code) {\n consumed = undefined\n expectedCode = code\n state = state(code)\n }\n\n /** @type {Effects['consume']} */\n function consume(code) {\n if (markdownLineEnding(code)) {\n point.line++\n point.column = 1\n point.offset += code === -3 ? 2 : 1\n accountForPotentialSkip()\n } else if (code !== -1) {\n point.column++\n point.offset++\n }\n\n // Not in a string chunk.\n if (point._bufferIndex < 0) {\n point._index++\n } else {\n point._bufferIndex++\n\n // At end of string chunk.\n // @ts-expect-error Points w/ non-negative `_bufferIndex` reference\n // strings.\n if (point._bufferIndex === chunks[point._index].length) {\n point._bufferIndex = -1\n point._index++\n }\n }\n\n // Expose the previous character.\n context.previous = code\n\n // Mark as consumed.\n consumed = true\n }\n\n /** @type {Effects['enter']} */\n function enter(type, fields) {\n /** @type {Token} */\n // @ts-expect-error Patch instead of assign required fields to help GC.\n const token = fields || {}\n token.type = type\n token.start = now()\n context.events.push(['enter', token, context])\n stack.push(token)\n return token\n }\n\n /** @type {Effects['exit']} */\n function exit(type) {\n const token = stack.pop()\n token.end = now()\n context.events.push(['exit', token, context])\n return token\n }\n\n /**\n * Use results.\n *\n * @type {ReturnHandle}\n */\n function onsuccessfulconstruct(construct, info) {\n addResult(construct, info.from)\n }\n\n /**\n * Discard results.\n *\n * @type {ReturnHandle}\n */\n function onsuccessfulcheck(_, info) {\n info.restore()\n }\n\n /**\n * Factory to attempt/check/interrupt.\n *\n * @param {ReturnHandle} onreturn\n * @param {{interrupt?: boolean | undefined} | undefined} [fields]\n */\n function constructFactory(onreturn, fields) {\n return hook\n\n /**\n * Handle either an object mapping codes to constructs, a list of\n * constructs, or a single construct.\n *\n * @param {Array | Construct | ConstructRecord} constructs\n * @param {State} returnState\n * @param {State | undefined} [bogusState]\n * @returns {State}\n */\n function hook(constructs, returnState, bogusState) {\n /** @type {Array} */\n let listOfConstructs\n /** @type {number} */\n let constructIndex\n /** @type {Construct} */\n let currentConstruct\n /** @type {Info} */\n let info\n return Array.isArray(constructs) /* c8 ignore next 1 */\n ? handleListOfConstructs(constructs)\n : 'tokenize' in constructs\n ? // @ts-expect-error Looks like a construct.\n handleListOfConstructs([constructs])\n : handleMapOfConstructs(constructs)\n\n /**\n * Handle a list of construct.\n *\n * @param {ConstructRecord} map\n * @returns {State}\n */\n function handleMapOfConstructs(map) {\n return start\n\n /** @type {State} */\n function start(code) {\n const def = code !== null && map[code]\n const all = code !== null && map.null\n const list = [\n // To do: add more extension tests.\n /* c8 ignore next 2 */\n ...(Array.isArray(def) ? def : def ? [def] : []),\n ...(Array.isArray(all) ? all : all ? [all] : [])\n ]\n return handleListOfConstructs(list)(code)\n }\n }\n\n /**\n * Handle a list of construct.\n *\n * @param {Array} list\n * @returns {State}\n */\n function handleListOfConstructs(list) {\n listOfConstructs = list\n constructIndex = 0\n if (list.length === 0) {\n return bogusState\n }\n return handleConstruct(list[constructIndex])\n }\n\n /**\n * Handle a single construct.\n *\n * @param {Construct} construct\n * @returns {State}\n */\n function handleConstruct(construct) {\n return start\n\n /** @type {State} */\n function start(code) {\n // To do: not needed to store if there is no bogus state, probably?\n // Currently doesn’t work because `inspect` in document does a check\n // w/o a bogus, which doesn’t make sense. But it does seem to help perf\n // by not storing.\n info = store()\n currentConstruct = construct\n if (!construct.partial) {\n context.currentConstruct = construct\n }\n\n // Always populated by defaults.\n\n if (\n construct.name &&\n context.parser.constructs.disable.null.includes(construct.name)\n ) {\n return nok(code)\n }\n return construct.tokenize.call(\n // If we do have fields, create an object w/ `context` as its\n // prototype.\n // This allows a “live binding”, which is needed for `interrupt`.\n fields ? Object.assign(Object.create(context), fields) : context,\n effects,\n ok,\n nok\n )(code)\n }\n }\n\n /** @type {State} */\n function ok(code) {\n consumed = true\n onreturn(currentConstruct, info)\n return returnState\n }\n\n /** @type {State} */\n function nok(code) {\n consumed = true\n info.restore()\n if (++constructIndex < listOfConstructs.length) {\n return handleConstruct(listOfConstructs[constructIndex])\n }\n return bogusState\n }\n }\n }\n\n /**\n * @param {Construct} construct\n * @param {number} from\n * @returns {void}\n */\n function addResult(construct, from) {\n if (construct.resolveAll && !resolveAllConstructs.includes(construct)) {\n resolveAllConstructs.push(construct)\n }\n if (construct.resolve) {\n splice(\n context.events,\n from,\n context.events.length - from,\n construct.resolve(context.events.slice(from), context)\n )\n }\n if (construct.resolveTo) {\n context.events = construct.resolveTo(context.events, context)\n }\n }\n\n /**\n * Store state.\n *\n * @returns {Info}\n */\n function store() {\n const startPoint = now()\n const startPrevious = context.previous\n const startCurrentConstruct = context.currentConstruct\n const startEventsIndex = context.events.length\n const startStack = Array.from(stack)\n return {\n restore,\n from: startEventsIndex\n }\n\n /**\n * Restore state.\n *\n * @returns {void}\n */\n function restore() {\n point = startPoint\n context.previous = startPrevious\n context.currentConstruct = startCurrentConstruct\n context.events.length = startEventsIndex\n stack = startStack\n accountForPotentialSkip()\n }\n }\n\n /**\n * Move the current point a bit forward in the line when it’s on a column\n * skip.\n *\n * @returns {void}\n */\n function accountForPotentialSkip() {\n if (point.line in columnStart && point.column < 2) {\n point.column = columnStart[point.line]\n point.offset += columnStart[point.line] - 1\n }\n }\n}\n\n/**\n * Get the chunks from a slice of chunks in the range of a token.\n *\n * @param {Array} chunks\n * @param {Pick} token\n * @returns {Array}\n */\nfunction sliceChunks(chunks, token) {\n const startIndex = token.start._index\n const startBufferIndex = token.start._bufferIndex\n const endIndex = token.end._index\n const endBufferIndex = token.end._bufferIndex\n /** @type {Array} */\n let view\n if (startIndex === endIndex) {\n // @ts-expect-error `_bufferIndex` is used on string chunks.\n view = [chunks[startIndex].slice(startBufferIndex, endBufferIndex)]\n } else {\n view = chunks.slice(startIndex, endIndex)\n if (startBufferIndex > -1) {\n const head = view[0]\n if (typeof head === 'string') {\n view[0] = head.slice(startBufferIndex)\n } else {\n view.shift()\n }\n }\n if (endBufferIndex > 0) {\n // @ts-expect-error `_bufferIndex` is used on string chunks.\n view.push(chunks[endIndex].slice(0, endBufferIndex))\n }\n }\n return view\n}\n\n/**\n * Get the string value of a slice of chunks.\n *\n * @param {Array} chunks\n * @param {boolean | undefined} [expandTabs=false]\n * @returns {string}\n */\nfunction serializeChunks(chunks, expandTabs) {\n let index = -1\n /** @type {Array} */\n const result = []\n /** @type {boolean | undefined} */\n let atTab\n while (++index < chunks.length) {\n const chunk = chunks[index]\n /** @type {string} */\n let value\n if (typeof chunk === 'string') {\n value = chunk\n } else\n switch (chunk) {\n case -5: {\n value = '\\r'\n break\n }\n case -4: {\n value = '\\n'\n break\n }\n case -3: {\n value = '\\r' + '\\n'\n break\n }\n case -2: {\n value = expandTabs ? ' ' : '\\t'\n break\n }\n case -1: {\n if (!expandTabs && atTab) continue\n value = ' '\n break\n }\n default: {\n // Currently only replacement character.\n value = String.fromCharCode(chunk)\n }\n }\n atTab = chunk === -2\n result.push(value)\n }\n return result.join('')\n}\n","/**\n * @typedef {import('micromark-util-types').Extension} Extension\n */\n\nimport {\n attention,\n autolink,\n blockQuote,\n characterEscape,\n characterReference,\n codeFenced,\n codeIndented,\n codeText,\n definition,\n hardBreakEscape,\n headingAtx,\n htmlFlow,\n htmlText,\n labelEnd,\n labelStartImage,\n labelStartLink,\n lineEnding,\n list,\n setextUnderline,\n thematicBreak\n} from 'micromark-core-commonmark'\nimport {resolver as resolveText} from './initialize/text.js'\n\n/** @satisfies {Extension['document']} */\nexport const document = {\n [42]: list,\n [43]: list,\n [45]: list,\n [48]: list,\n [49]: list,\n [50]: list,\n [51]: list,\n [52]: list,\n [53]: list,\n [54]: list,\n [55]: list,\n [56]: list,\n [57]: list,\n [62]: blockQuote\n}\n\n/** @satisfies {Extension['contentInitial']} */\nexport const contentInitial = {\n [91]: definition\n}\n\n/** @satisfies {Extension['flowInitial']} */\nexport const flowInitial = {\n [-2]: codeIndented,\n [-1]: codeIndented,\n [32]: codeIndented\n}\n\n/** @satisfies {Extension['flow']} */\nexport const flow = {\n [35]: headingAtx,\n [42]: thematicBreak,\n [45]: [setextUnderline, thematicBreak],\n [60]: htmlFlow,\n [61]: setextUnderline,\n [95]: thematicBreak,\n [96]: codeFenced,\n [126]: codeFenced\n}\n\n/** @satisfies {Extension['string']} */\nexport const string = {\n [38]: characterReference,\n [92]: characterEscape\n}\n\n/** @satisfies {Extension['text']} */\nexport const text = {\n [-5]: lineEnding,\n [-4]: lineEnding,\n [-3]: lineEnding,\n [33]: labelStartImage,\n [38]: characterReference,\n [42]: attention,\n [60]: [autolink, htmlText],\n [91]: labelStartLink,\n [92]: [hardBreakEscape, characterEscape],\n [93]: labelEnd,\n [95]: attention,\n [96]: codeText\n}\n\n/** @satisfies {Extension['insideSpan']} */\nexport const insideSpan = {\n null: [attention, resolveText]\n}\n\n/** @satisfies {Extension['attentionMarkers']} */\nexport const attentionMarkers = {\n null: [42, 95]\n}\n\n/** @satisfies {Extension['disable']} */\nexport const disable = {\n null: []\n}\n","/**\n * @typedef {import('micromark-util-types').Create} Create\n * @typedef {import('micromark-util-types').FullNormalizedExtension} FullNormalizedExtension\n * @typedef {import('micromark-util-types').InitialConstruct} InitialConstruct\n * @typedef {import('micromark-util-types').ParseContext} ParseContext\n * @typedef {import('micromark-util-types').ParseOptions} ParseOptions\n */\n\nimport {combineExtensions} from 'micromark-util-combine-extensions'\nimport {content} from './initialize/content.js'\nimport {document} from './initialize/document.js'\nimport {flow} from './initialize/flow.js'\nimport {text, string} from './initialize/text.js'\nimport {createTokenizer} from './create-tokenizer.js'\nimport * as defaultConstructs from './constructs.js'\n\n/**\n * @param {ParseOptions | null | undefined} [options]\n * @returns {ParseContext}\n */\nexport function parse(options) {\n const settings = options || {}\n const constructs =\n /** @type {FullNormalizedExtension} */\n combineExtensions([defaultConstructs, ...(settings.extensions || [])])\n\n /** @type {ParseContext} */\n const parser = {\n defined: [],\n lazy: {},\n constructs,\n content: create(content),\n document: create(document),\n flow: create(flow),\n string: create(string),\n text: create(text)\n }\n return parser\n\n /**\n * @param {InitialConstruct} initial\n */\n function create(initial) {\n return creator\n /** @type {Create} */\n function creator(from) {\n return createTokenizer(parser, initial, from)\n }\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Chunk} Chunk\n * @typedef {import('micromark-util-types').Code} Code\n * @typedef {import('micromark-util-types').Encoding} Encoding\n * @typedef {import('micromark-util-types').Value} Value\n */\n\n/**\n * @callback Preprocessor\n * @param {Value} value\n * @param {Encoding | null | undefined} [encoding]\n * @param {boolean | null | undefined} [end=false]\n * @returns {Array}\n */\n\nconst search = /[\\0\\t\\n\\r]/g\n\n/**\n * @returns {Preprocessor}\n */\nexport function preprocess() {\n let column = 1\n let buffer = ''\n /** @type {boolean | undefined} */\n let start = true\n /** @type {boolean | undefined} */\n let atCarriageReturn\n return preprocessor\n\n /** @type {Preprocessor} */\n function preprocessor(value, encoding, end) {\n /** @type {Array} */\n const chunks = []\n /** @type {RegExpMatchArray | null} */\n let match\n /** @type {number} */\n let next\n /** @type {number} */\n let startPosition\n /** @type {number} */\n let endPosition\n /** @type {Code} */\n let code\n\n // @ts-expect-error `Buffer` does allow an encoding.\n value = buffer + value.toString(encoding)\n startPosition = 0\n buffer = ''\n if (start) {\n // To do: `markdown-rs` actually parses BOMs (byte order mark).\n if (value.charCodeAt(0) === 65279) {\n startPosition++\n }\n start = undefined\n }\n while (startPosition < value.length) {\n search.lastIndex = startPosition\n match = search.exec(value)\n endPosition =\n match && match.index !== undefined ? match.index : value.length\n code = value.charCodeAt(endPosition)\n if (!match) {\n buffer = value.slice(startPosition)\n break\n }\n if (code === 10 && startPosition === endPosition && atCarriageReturn) {\n chunks.push(-3)\n atCarriageReturn = undefined\n } else {\n if (atCarriageReturn) {\n chunks.push(-5)\n atCarriageReturn = undefined\n }\n if (startPosition < endPosition) {\n chunks.push(value.slice(startPosition, endPosition))\n column += endPosition - startPosition\n }\n switch (code) {\n case 0: {\n chunks.push(65533)\n column++\n break\n }\n case 9: {\n next = Math.ceil(column / 4) * 4\n chunks.push(-2)\n while (column++ < next) chunks.push(-1)\n break\n }\n case 10: {\n chunks.push(-4)\n column = 1\n break\n }\n default: {\n atCarriageReturn = true\n column = 1\n }\n }\n }\n startPosition = endPosition + 1\n }\n if (end) {\n if (atCarriageReturn) chunks.push(-5)\n if (buffer) chunks.push(buffer)\n chunks.push(null)\n }\n return chunks\n }\n}\n","/**\n * @typedef {import('micromark-util-types').Event} Event\n */\n\nimport {subtokenize} from 'micromark-util-subtokenize'\n\n/**\n * @param {Array} events\n * @returns {Array}\n */\nexport function postprocess(events) {\n while (!subtokenize(events)) {\n // Empty\n }\n return events\n}\n","/**\n * Turn the number (in string form as either hexa- or plain decimal) coming from\n * a numeric character reference into a character.\n *\n * Sort of like `String.fromCharCode(Number.parseInt(value, base))`, but makes\n * non-characters and control characters safe.\n *\n * @param {string} value\n * Value to decode.\n * @param {number} base\n * Numeric base.\n * @returns {string}\n * Character.\n */\nexport function decodeNumericCharacterReference(value, base) {\n const code = Number.parseInt(value, base)\n if (\n // C0 except for HT, LF, FF, CR, space.\n code < 9 ||\n code === 11 ||\n (code > 13 && code < 32) ||\n // Control character (DEL) of C0, and C1 controls.\n (code > 126 && code < 160) ||\n // Lone high surrogates and low surrogates.\n (code > 55295 && code < 57344) ||\n // Noncharacters.\n (code > 64975 && code < 65008) /* eslint-disable no-bitwise */ ||\n (code & 65535) === 65535 ||\n (code & 65535) === 65534 /* eslint-enable no-bitwise */ ||\n // Out of range\n code > 1114111\n ) {\n return '\\uFFFD'\n }\n return String.fromCharCode(code)\n}\n","import {decodeNamedCharacterReference} from 'decode-named-character-reference'\nimport {decodeNumericCharacterReference} from 'micromark-util-decode-numeric-character-reference'\nconst characterEscapeOrReference =\n /\\\\([!-/:-@[-`{-~])|&(#(?:\\d{1,7}|x[\\da-f]{1,6})|[\\da-z]{1,31});/gi\n\n/**\n * Decode markdown strings (which occur in places such as fenced code info\n * strings, destinations, labels, and titles).\n *\n * The “string” content type allows character escapes and -references.\n * This decodes those.\n *\n * @param {string} value\n * Value to decode.\n * @returns {string}\n * Decoded value.\n */\nexport function decodeString(value) {\n return value.replace(characterEscapeOrReference, decode)\n}\n\n/**\n * @param {string} $0\n * @param {string} $1\n * @param {string} $2\n * @returns {string}\n */\nfunction decode($0, $1, $2) {\n if ($1) {\n // Escape.\n return $1\n }\n\n // Reference.\n const head = $2.charCodeAt(0)\n if (head === 35) {\n const head = $2.charCodeAt(1)\n const hex = head === 120 || head === 88\n return decodeNumericCharacterReference($2.slice(hex ? 2 : 1), hex ? 16 : 10)\n }\n return decodeNamedCharacterReference($2) || $0\n}\n","/**\n * @typedef {import('micromark-util-types').Encoding} Encoding\n * @typedef {import('micromark-util-types').Event} Event\n * @typedef {import('micromark-util-types').ParseOptions} ParseOptions\n * @typedef {import('micromark-util-types').Token} Token\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\n * @typedef {import('micromark-util-types').Value} Value\n *\n * @typedef {import('unist').Parent} UnistParent\n * @typedef {import('unist').Point} Point\n *\n * @typedef {import('mdast').PhrasingContent} PhrasingContent\n * @typedef {import('mdast').StaticPhrasingContent} StaticPhrasingContent\n * @typedef {import('mdast').Content} Content\n * @typedef {import('mdast').Break} Break\n * @typedef {import('mdast').Blockquote} Blockquote\n * @typedef {import('mdast').Code} Code\n * @typedef {import('mdast').Definition} Definition\n * @typedef {import('mdast').Emphasis} Emphasis\n * @typedef {import('mdast').Heading} Heading\n * @typedef {import('mdast').HTML} HTML\n * @typedef {import('mdast').Image} Image\n * @typedef {import('mdast').ImageReference} ImageReference\n * @typedef {import('mdast').InlineCode} InlineCode\n * @typedef {import('mdast').Link} Link\n * @typedef {import('mdast').LinkReference} LinkReference\n * @typedef {import('mdast').List} List\n * @typedef {import('mdast').ListItem} ListItem\n * @typedef {import('mdast').Paragraph} Paragraph\n * @typedef {import('mdast').Root} Root\n * @typedef {import('mdast').Strong} Strong\n * @typedef {import('mdast').Text} Text\n * @typedef {import('mdast').ThematicBreak} ThematicBreak\n * @typedef {import('mdast').ReferenceType} ReferenceType\n * @typedef {import('../index.js').CompileData} CompileData\n */\n\n/**\n * @typedef {Root | Content} Node\n * @typedef {Extract} Parent\n *\n * @typedef {Omit & {type: 'fragment', children: Array}} Fragment\n */\n\n/**\n * @callback Transform\n * Extra transform, to change the AST afterwards.\n * @param {Root} tree\n * Tree to transform.\n * @returns {Root | undefined | null | void}\n * New tree or nothing (in which case the current tree is used).\n *\n * @callback Handle\n * Handle a token.\n * @param {CompileContext} this\n * Context.\n * @param {Token} token\n * Current token.\n * @returns {void}\n * Nothing.\n *\n * @typedef {Record} Handles\n * Token types mapping to handles\n *\n * @callback OnEnterError\n * Handle the case where the `right` token is open, but it is closed (by the\n * `left` token) or because we reached the end of the document.\n * @param {Omit} this\n * Context.\n * @param {Token | undefined} left\n * Left token.\n * @param {Token} right\n * Right token.\n * @returns {void}\n * Nothing.\n *\n * @callback OnExitError\n * Handle the case where the `right` token is open but it is closed by\n * exiting the `left` token.\n * @param {Omit} this\n * Context.\n * @param {Token} left\n * Left token.\n * @param {Token} right\n * Right token.\n * @returns {void}\n * Nothing.\n *\n * @typedef {[Token, OnEnterError | undefined]} TokenTuple\n * Open token on the stack, with an optional error handler for when\n * that token isn’t closed properly.\n */\n\n/**\n * @typedef Config\n * Configuration.\n *\n * We have our defaults, but extensions will add more.\n * @property {Array} canContainEols\n * Token types where line endings are used.\n * @property {Handles} enter\n * Opening handles.\n * @property {Handles} exit\n * Closing handles.\n * @property {Array} transforms\n * Tree transforms.\n *\n * @typedef {Partial} Extension\n * Change how markdown tokens from micromark are turned into mdast.\n *\n * @typedef CompileContext\n * mdast compiler context.\n * @property {Array} stack\n * Stack of nodes.\n * @property {Array} tokenStack\n * Stack of tokens.\n * @property {(key: Key) => CompileData[Key]} getData\n * Get data from the key/value store.\n * @property {(key: Key, value?: CompileData[Key]) => void} setData\n * Set data into the key/value store.\n * @property {(this: CompileContext) => void} buffer\n * Capture some of the output data.\n * @property {(this: CompileContext) => string} resume\n * Stop capturing and access the output data.\n * @property {(this: CompileContext, node: Kind, token: Token, onError?: OnEnterError) => Kind} enter\n * Enter a token.\n * @property {(this: CompileContext, token: Token, onError?: OnExitError) => Node} exit\n * Exit a token.\n * @property {TokenizeContext['sliceSerialize']} sliceSerialize\n * Get the string value of a token.\n * @property {Config} config\n * Configuration.\n *\n * @typedef FromMarkdownOptions\n * Configuration for how to build mdast.\n * @property {Array> | null | undefined} [mdastExtensions]\n * Extensions for this utility to change how tokens are turned into a tree.\n *\n * @typedef {ParseOptions & FromMarkdownOptions} Options\n * Configuration.\n */\n\n// To do: micromark: create a registry of tokens?\n// To do: next major: don’t return given `Node` from `enter`.\n// To do: next major: remove setter/getter.\n\nimport {toString} from 'mdast-util-to-string'\nimport {parse} from 'micromark/lib/parse.js'\nimport {preprocess} from 'micromark/lib/preprocess.js'\nimport {postprocess} from 'micromark/lib/postprocess.js'\nimport {decodeNumericCharacterReference} from 'micromark-util-decode-numeric-character-reference'\nimport {decodeString} from 'micromark-util-decode-string'\nimport {normalizeIdentifier} from 'micromark-util-normalize-identifier'\nimport {decodeNamedCharacterReference} from 'decode-named-character-reference'\nimport {stringifyPosition} from 'unist-util-stringify-position'\nconst own = {}.hasOwnProperty\n\n/**\n * @param value\n * Markdown to parse.\n * @param encoding\n * Character encoding for when `value` is `Buffer`.\n * @param options\n * Configuration.\n * @returns\n * mdast tree.\n */\nexport const fromMarkdown =\n /**\n * @type {(\n * ((value: Value, encoding: Encoding, options?: Options | null | undefined) => Root) &\n * ((value: Value, options?: Options | null | undefined) => Root)\n * )}\n */\n\n /**\n * @param {Value} value\n * @param {Encoding | Options | null | undefined} [encoding]\n * @param {Options | null | undefined} [options]\n * @returns {Root}\n */\n function (value, encoding, options) {\n if (typeof encoding !== 'string') {\n options = encoding\n encoding = undefined\n }\n return compiler(options)(\n postprocess(\n parse(options).document().write(preprocess()(value, encoding, true))\n )\n )\n }\n\n/**\n * Note this compiler only understand complete buffering, not streaming.\n *\n * @param {Options | null | undefined} [options]\n */\nfunction compiler(options) {\n /** @type {Config} */\n const config = {\n transforms: [],\n canContainEols: ['emphasis', 'fragment', 'heading', 'paragraph', 'strong'],\n enter: {\n autolink: opener(link),\n autolinkProtocol: onenterdata,\n autolinkEmail: onenterdata,\n atxHeading: opener(heading),\n blockQuote: opener(blockQuote),\n characterEscape: onenterdata,\n characterReference: onenterdata,\n codeFenced: opener(codeFlow),\n codeFencedFenceInfo: buffer,\n codeFencedFenceMeta: buffer,\n codeIndented: opener(codeFlow, buffer),\n codeText: opener(codeText, buffer),\n codeTextData: onenterdata,\n data: onenterdata,\n codeFlowValue: onenterdata,\n definition: opener(definition),\n definitionDestinationString: buffer,\n definitionLabelString: buffer,\n definitionTitleString: buffer,\n emphasis: opener(emphasis),\n hardBreakEscape: opener(hardBreak),\n hardBreakTrailing: opener(hardBreak),\n htmlFlow: opener(html, buffer),\n htmlFlowData: onenterdata,\n htmlText: opener(html, buffer),\n htmlTextData: onenterdata,\n image: opener(image),\n label: buffer,\n link: opener(link),\n listItem: opener(listItem),\n listItemValue: onenterlistitemvalue,\n listOrdered: opener(list, onenterlistordered),\n listUnordered: opener(list),\n paragraph: opener(paragraph),\n reference: onenterreference,\n referenceString: buffer,\n resourceDestinationString: buffer,\n resourceTitleString: buffer,\n setextHeading: opener(heading),\n strong: opener(strong),\n thematicBreak: opener(thematicBreak)\n },\n exit: {\n atxHeading: closer(),\n atxHeadingSequence: onexitatxheadingsequence,\n autolink: closer(),\n autolinkEmail: onexitautolinkemail,\n autolinkProtocol: onexitautolinkprotocol,\n blockQuote: closer(),\n characterEscapeValue: onexitdata,\n characterReferenceMarkerHexadecimal: onexitcharacterreferencemarker,\n characterReferenceMarkerNumeric: onexitcharacterreferencemarker,\n characterReferenceValue: onexitcharacterreferencevalue,\n codeFenced: closer(onexitcodefenced),\n codeFencedFence: onexitcodefencedfence,\n codeFencedFenceInfo: onexitcodefencedfenceinfo,\n codeFencedFenceMeta: onexitcodefencedfencemeta,\n codeFlowValue: onexitdata,\n codeIndented: closer(onexitcodeindented),\n codeText: closer(onexitcodetext),\n codeTextData: onexitdata,\n data: onexitdata,\n definition: closer(),\n definitionDestinationString: onexitdefinitiondestinationstring,\n definitionLabelString: onexitdefinitionlabelstring,\n definitionTitleString: onexitdefinitiontitlestring,\n emphasis: closer(),\n hardBreakEscape: closer(onexithardbreak),\n hardBreakTrailing: closer(onexithardbreak),\n htmlFlow: closer(onexithtmlflow),\n htmlFlowData: onexitdata,\n htmlText: closer(onexithtmltext),\n htmlTextData: onexitdata,\n image: closer(onexitimage),\n label: onexitlabel,\n labelText: onexitlabeltext,\n lineEnding: onexitlineending,\n link: closer(onexitlink),\n listItem: closer(),\n listOrdered: closer(),\n listUnordered: closer(),\n paragraph: closer(),\n referenceString: onexitreferencestring,\n resourceDestinationString: onexitresourcedestinationstring,\n resourceTitleString: onexitresourcetitlestring,\n resource: onexitresource,\n setextHeading: closer(onexitsetextheading),\n setextHeadingLineSequence: onexitsetextheadinglinesequence,\n setextHeadingText: onexitsetextheadingtext,\n strong: closer(),\n thematicBreak: closer()\n }\n }\n configure(config, (options || {}).mdastExtensions || [])\n\n /** @type {CompileData} */\n const data = {}\n return compile\n\n /**\n * Turn micromark events into an mdast tree.\n *\n * @param {Array} events\n * Events.\n * @returns {Root}\n * mdast tree.\n */\n function compile(events) {\n /** @type {Root} */\n let tree = {\n type: 'root',\n children: []\n }\n /** @type {Omit} */\n const context = {\n stack: [tree],\n tokenStack: [],\n config,\n enter,\n exit,\n buffer,\n resume,\n setData,\n getData\n }\n /** @type {Array} */\n const listStack = []\n let index = -1\n while (++index < events.length) {\n // We preprocess lists to add `listItem` tokens, and to infer whether\n // items the list itself are spread out.\n if (\n events[index][1].type === 'listOrdered' ||\n events[index][1].type === 'listUnordered'\n ) {\n if (events[index][0] === 'enter') {\n listStack.push(index)\n } else {\n const tail = listStack.pop()\n index = prepareList(events, tail, index)\n }\n }\n }\n index = -1\n while (++index < events.length) {\n const handler = config[events[index][0]]\n if (own.call(handler, events[index][1].type)) {\n handler[events[index][1].type].call(\n Object.assign(\n {\n sliceSerialize: events[index][2].sliceSerialize\n },\n context\n ),\n events[index][1]\n )\n }\n }\n\n // Handle tokens still being open.\n if (context.tokenStack.length > 0) {\n const tail = context.tokenStack[context.tokenStack.length - 1]\n const handler = tail[1] || defaultOnError\n handler.call(context, undefined, tail[0])\n }\n\n // Figure out `root` position.\n tree.position = {\n start: point(\n events.length > 0\n ? events[0][1].start\n : {\n line: 1,\n column: 1,\n offset: 0\n }\n ),\n end: point(\n events.length > 0\n ? events[events.length - 2][1].end\n : {\n line: 1,\n column: 1,\n offset: 0\n }\n )\n }\n\n // Call transforms.\n index = -1\n while (++index < config.transforms.length) {\n tree = config.transforms[index](tree) || tree\n }\n return tree\n }\n\n /**\n * @param {Array} events\n * @param {number} start\n * @param {number} length\n * @returns {number}\n */\n function prepareList(events, start, length) {\n let index = start - 1\n let containerBalance = -1\n let listSpread = false\n /** @type {Token | undefined} */\n let listItem\n /** @type {number | undefined} */\n let lineIndex\n /** @type {number | undefined} */\n let firstBlankLineIndex\n /** @type {boolean | undefined} */\n let atMarker\n while (++index <= length) {\n const event = events[index]\n if (\n event[1].type === 'listUnordered' ||\n event[1].type === 'listOrdered' ||\n event[1].type === 'blockQuote'\n ) {\n if (event[0] === 'enter') {\n containerBalance++\n } else {\n containerBalance--\n }\n atMarker = undefined\n } else if (event[1].type === 'lineEndingBlank') {\n if (event[0] === 'enter') {\n if (\n listItem &&\n !atMarker &&\n !containerBalance &&\n !firstBlankLineIndex\n ) {\n firstBlankLineIndex = index\n }\n atMarker = undefined\n }\n } else if (\n event[1].type === 'linePrefix' ||\n event[1].type === 'listItemValue' ||\n event[1].type === 'listItemMarker' ||\n event[1].type === 'listItemPrefix' ||\n event[1].type === 'listItemPrefixWhitespace'\n ) {\n // Empty.\n } else {\n atMarker = undefined\n }\n if (\n (!containerBalance &&\n event[0] === 'enter' &&\n event[1].type === 'listItemPrefix') ||\n (containerBalance === -1 &&\n event[0] === 'exit' &&\n (event[1].type === 'listUnordered' ||\n event[1].type === 'listOrdered'))\n ) {\n if (listItem) {\n let tailIndex = index\n lineIndex = undefined\n while (tailIndex--) {\n const tailEvent = events[tailIndex]\n if (\n tailEvent[1].type === 'lineEnding' ||\n tailEvent[1].type === 'lineEndingBlank'\n ) {\n if (tailEvent[0] === 'exit') continue\n if (lineIndex) {\n events[lineIndex][1].type = 'lineEndingBlank'\n listSpread = true\n }\n tailEvent[1].type = 'lineEnding'\n lineIndex = tailIndex\n } else if (\n tailEvent[1].type === 'linePrefix' ||\n tailEvent[1].type === 'blockQuotePrefix' ||\n tailEvent[1].type === 'blockQuotePrefixWhitespace' ||\n tailEvent[1].type === 'blockQuoteMarker' ||\n tailEvent[1].type === 'listItemIndent'\n ) {\n // Empty\n } else {\n break\n }\n }\n if (\n firstBlankLineIndex &&\n (!lineIndex || firstBlankLineIndex < lineIndex)\n ) {\n listItem._spread = true\n }\n\n // Fix position.\n listItem.end = Object.assign(\n {},\n lineIndex ? events[lineIndex][1].start : event[1].end\n )\n events.splice(lineIndex || index, 0, ['exit', listItem, event[2]])\n index++\n length++\n }\n\n // Create a new list item.\n if (event[1].type === 'listItemPrefix') {\n listItem = {\n type: 'listItem',\n _spread: false,\n start: Object.assign({}, event[1].start),\n // @ts-expect-error: we’ll add `end` in a second.\n end: undefined\n }\n // @ts-expect-error: `listItem` is most definitely defined, TS...\n events.splice(index, 0, ['enter', listItem, event[2]])\n index++\n length++\n firstBlankLineIndex = undefined\n atMarker = true\n }\n }\n }\n events[start][1]._spread = listSpread\n return length\n }\n\n /**\n * Set data.\n *\n * @template {keyof CompileData} Key\n * Field type.\n * @param {Key} key\n * Key of field.\n * @param {CompileData[Key]} [value]\n * New value.\n * @returns {void}\n * Nothing.\n */\n function setData(key, value) {\n data[key] = value\n }\n\n /**\n * Get data.\n *\n * @template {keyof CompileData} Key\n * Field type.\n * @param {Key} key\n * Key of field.\n * @returns {CompileData[Key]}\n * Value.\n */\n function getData(key) {\n return data[key]\n }\n\n /**\n * Create an opener handle.\n *\n * @param {(token: Token) => Node} create\n * Create a node.\n * @param {Handle} [and]\n * Optional function to also run.\n * @returns {Handle}\n * Handle.\n */\n function opener(create, and) {\n return open\n\n /**\n * @this {CompileContext}\n * @param {Token} token\n * @returns {void}\n */\n function open(token) {\n enter.call(this, create(token), token)\n if (and) and.call(this, token)\n }\n }\n\n /**\n * @this {CompileContext}\n * @returns {void}\n */\n function buffer() {\n this.stack.push({\n type: 'fragment',\n children: []\n })\n }\n\n /**\n * @template {Node} Kind\n * Node type.\n * @this {CompileContext}\n * Context.\n * @param {Kind} node\n * Node to enter.\n * @param {Token} token\n * Corresponding token.\n * @param {OnEnterError | undefined} [errorHandler]\n * Handle the case where this token is open, but it is closed by something else.\n * @returns {Kind}\n * The given node.\n */\n function enter(node, token, errorHandler) {\n const parent = this.stack[this.stack.length - 1]\n // @ts-expect-error: Assume `Node` can exist as a child of `parent`.\n parent.children.push(node)\n this.stack.push(node)\n this.tokenStack.push([token, errorHandler])\n // @ts-expect-error: `end` will be patched later.\n node.position = {\n start: point(token.start)\n }\n return node\n }\n\n /**\n * Create a closer handle.\n *\n * @param {Handle} [and]\n * Optional function to also run.\n * @returns {Handle}\n * Handle.\n */\n function closer(and) {\n return close\n\n /**\n * @this {CompileContext}\n * @param {Token} token\n * @returns {void}\n */\n function close(token) {\n if (and) and.call(this, token)\n exit.call(this, token)\n }\n }\n\n /**\n * @this {CompileContext}\n * Context.\n * @param {Token} token\n * Corresponding token.\n * @param {OnExitError | undefined} [onExitError]\n * Handle the case where another token is open.\n * @returns {Node}\n * The closed node.\n */\n function exit(token, onExitError) {\n const node = this.stack.pop()\n const open = this.tokenStack.pop()\n if (!open) {\n throw new Error(\n 'Cannot close `' +\n token.type +\n '` (' +\n stringifyPosition({\n start: token.start,\n end: token.end\n }) +\n '): it’s not open'\n )\n } else if (open[0].type !== token.type) {\n if (onExitError) {\n onExitError.call(this, token, open[0])\n } else {\n const handler = open[1] || defaultOnError\n handler.call(this, token, open[0])\n }\n }\n node.position.end = point(token.end)\n return node\n }\n\n /**\n * @this {CompileContext}\n * @returns {string}\n */\n function resume() {\n return toString(this.stack.pop())\n }\n\n //\n // Handlers.\n //\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onenterlistordered() {\n setData('expectingFirstListItemValue', true)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onenterlistitemvalue(token) {\n if (getData('expectingFirstListItemValue')) {\n const ancestor = this.stack[this.stack.length - 2]\n ancestor.start = Number.parseInt(this.sliceSerialize(token), 10)\n setData('expectingFirstListItemValue')\n }\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitcodefencedfenceinfo() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.lang = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitcodefencedfencemeta() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.meta = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitcodefencedfence() {\n // Exit if this is the closing fence.\n if (getData('flowCodeInside')) return\n this.buffer()\n setData('flowCodeInside', true)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitcodefenced() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.value = data.replace(/^(\\r?\\n|\\r)|(\\r?\\n|\\r)$/g, '')\n setData('flowCodeInside')\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitcodeindented() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.value = data.replace(/(\\r?\\n|\\r)$/g, '')\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitdefinitionlabelstring(token) {\n const label = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.label = label\n node.identifier = normalizeIdentifier(\n this.sliceSerialize(token)\n ).toLowerCase()\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitdefinitiontitlestring() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.title = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitdefinitiondestinationstring() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.url = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitatxheadingsequence(token) {\n const node = this.stack[this.stack.length - 1]\n if (!node.depth) {\n const depth = this.sliceSerialize(token).length\n node.depth = depth\n }\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitsetextheadingtext() {\n setData('setextHeadingSlurpLineEnding', true)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitsetextheadinglinesequence(token) {\n const node = this.stack[this.stack.length - 1]\n node.depth = this.sliceSerialize(token).charCodeAt(0) === 61 ? 1 : 2\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitsetextheading() {\n setData('setextHeadingSlurpLineEnding')\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onenterdata(token) {\n const node = this.stack[this.stack.length - 1]\n let tail = node.children[node.children.length - 1]\n if (!tail || tail.type !== 'text') {\n // Add a new text node.\n tail = text()\n // @ts-expect-error: we’ll add `end` later.\n tail.position = {\n start: point(token.start)\n }\n // @ts-expect-error: Assume `parent` accepts `text`.\n node.children.push(tail)\n }\n this.stack.push(tail)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitdata(token) {\n const tail = this.stack.pop()\n tail.value += this.sliceSerialize(token)\n tail.position.end = point(token.end)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitlineending(token) {\n const context = this.stack[this.stack.length - 1]\n // If we’re at a hard break, include the line ending in there.\n if (getData('atHardBreak')) {\n const tail = context.children[context.children.length - 1]\n tail.position.end = point(token.end)\n setData('atHardBreak')\n return\n }\n if (\n !getData('setextHeadingSlurpLineEnding') &&\n config.canContainEols.includes(context.type)\n ) {\n onenterdata.call(this, token)\n onexitdata.call(this, token)\n }\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexithardbreak() {\n setData('atHardBreak', true)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexithtmlflow() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.value = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexithtmltext() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.value = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitcodetext() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.value = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitlink() {\n const node = this.stack[this.stack.length - 1]\n // Note: there are also `identifier` and `label` fields on this link node!\n // These are used / cleaned here.\n // To do: clean.\n if (getData('inReference')) {\n /** @type {ReferenceType} */\n const referenceType = getData('referenceType') || 'shortcut'\n node.type += 'Reference'\n // @ts-expect-error: mutate.\n node.referenceType = referenceType\n // @ts-expect-error: mutate.\n delete node.url\n delete node.title\n } else {\n // @ts-expect-error: mutate.\n delete node.identifier\n // @ts-expect-error: mutate.\n delete node.label\n }\n setData('referenceType')\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitimage() {\n const node = this.stack[this.stack.length - 1]\n // Note: there are also `identifier` and `label` fields on this link node!\n // These are used / cleaned here.\n // To do: clean.\n if (getData('inReference')) {\n /** @type {ReferenceType} */\n const referenceType = getData('referenceType') || 'shortcut'\n node.type += 'Reference'\n // @ts-expect-error: mutate.\n node.referenceType = referenceType\n // @ts-expect-error: mutate.\n delete node.url\n delete node.title\n } else {\n // @ts-expect-error: mutate.\n delete node.identifier\n // @ts-expect-error: mutate.\n delete node.label\n }\n setData('referenceType')\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitlabeltext(token) {\n const string = this.sliceSerialize(token)\n const ancestor = this.stack[this.stack.length - 2]\n // @ts-expect-error: stash this on the node, as it might become a reference\n // later.\n ancestor.label = decodeString(string)\n // @ts-expect-error: same as above.\n ancestor.identifier = normalizeIdentifier(string).toLowerCase()\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitlabel() {\n const fragment = this.stack[this.stack.length - 1]\n const value = this.resume()\n const node = this.stack[this.stack.length - 1]\n // Assume a reference.\n setData('inReference', true)\n if (node.type === 'link') {\n /** @type {Array} */\n // @ts-expect-error: Assume static phrasing content.\n const children = fragment.children\n node.children = children\n } else {\n node.alt = value\n }\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitresourcedestinationstring() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.url = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitresourcetitlestring() {\n const data = this.resume()\n const node = this.stack[this.stack.length - 1]\n node.title = data\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitresource() {\n setData('inReference')\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onenterreference() {\n setData('referenceType', 'collapsed')\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitreferencestring(token) {\n const label = this.resume()\n const node = this.stack[this.stack.length - 1]\n // @ts-expect-error: stash this on the node, as it might become a reference\n // later.\n node.label = label\n // @ts-expect-error: same as above.\n node.identifier = normalizeIdentifier(\n this.sliceSerialize(token)\n ).toLowerCase()\n setData('referenceType', 'full')\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n\n function onexitcharacterreferencemarker(token) {\n setData('characterReferenceType', token.type)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitcharacterreferencevalue(token) {\n const data = this.sliceSerialize(token)\n const type = getData('characterReferenceType')\n /** @type {string} */\n let value\n if (type) {\n value = decodeNumericCharacterReference(\n data,\n type === 'characterReferenceMarkerNumeric' ? 10 : 16\n )\n setData('characterReferenceType')\n } else {\n const result = decodeNamedCharacterReference(data)\n value = result\n }\n const tail = this.stack.pop()\n tail.value += value\n tail.position.end = point(token.end)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitautolinkprotocol(token) {\n onexitdata.call(this, token)\n const node = this.stack[this.stack.length - 1]\n node.url = this.sliceSerialize(token)\n }\n\n /**\n * @this {CompileContext}\n * @type {Handle}\n */\n function onexitautolinkemail(token) {\n onexitdata.call(this, token)\n const node = this.stack[this.stack.length - 1]\n node.url = 'mailto:' + this.sliceSerialize(token)\n }\n\n //\n // Creaters.\n //\n\n /** @returns {Blockquote} */\n function blockQuote() {\n return {\n type: 'blockquote',\n children: []\n }\n }\n\n /** @returns {Code} */\n function codeFlow() {\n return {\n type: 'code',\n lang: null,\n meta: null,\n value: ''\n }\n }\n\n /** @returns {InlineCode} */\n function codeText() {\n return {\n type: 'inlineCode',\n value: ''\n }\n }\n\n /** @returns {Definition} */\n function definition() {\n return {\n type: 'definition',\n identifier: '',\n label: null,\n title: null,\n url: ''\n }\n }\n\n /** @returns {Emphasis} */\n function emphasis() {\n return {\n type: 'emphasis',\n children: []\n }\n }\n\n /** @returns {Heading} */\n function heading() {\n // @ts-expect-error `depth` will be set later.\n return {\n type: 'heading',\n depth: undefined,\n children: []\n }\n }\n\n /** @returns {Break} */\n function hardBreak() {\n return {\n type: 'break'\n }\n }\n\n /** @returns {HTML} */\n function html() {\n return {\n type: 'html',\n value: ''\n }\n }\n\n /** @returns {Image} */\n function image() {\n return {\n type: 'image',\n title: null,\n url: '',\n alt: null\n }\n }\n\n /** @returns {Link} */\n function link() {\n return {\n type: 'link',\n title: null,\n url: '',\n children: []\n }\n }\n\n /**\n * @param {Token} token\n * @returns {List}\n */\n function list(token) {\n return {\n type: 'list',\n ordered: token.type === 'listOrdered',\n start: null,\n spread: token._spread,\n children: []\n }\n }\n\n /**\n * @param {Token} token\n * @returns {ListItem}\n */\n function listItem(token) {\n return {\n type: 'listItem',\n spread: token._spread,\n checked: null,\n children: []\n }\n }\n\n /** @returns {Paragraph} */\n function paragraph() {\n return {\n type: 'paragraph',\n children: []\n }\n }\n\n /** @returns {Strong} */\n function strong() {\n return {\n type: 'strong',\n children: []\n }\n }\n\n /** @returns {Text} */\n function text() {\n return {\n type: 'text',\n value: ''\n }\n }\n\n /** @returns {ThematicBreak} */\n function thematicBreak() {\n return {\n type: 'thematicBreak'\n }\n }\n}\n\n/**\n * Copy a point-like value.\n *\n * @param {Point} d\n * Point-like value.\n * @returns {Point}\n * unist point.\n */\nfunction point(d) {\n return {\n line: d.line,\n column: d.column,\n offset: d.offset\n }\n}\n\n/**\n * @param {Config} combined\n * @param {Array>} extensions\n * @returns {void}\n */\nfunction configure(combined, extensions) {\n let index = -1\n while (++index < extensions.length) {\n const value = extensions[index]\n if (Array.isArray(value)) {\n configure(combined, value)\n } else {\n extension(combined, value)\n }\n }\n}\n\n/**\n * @param {Config} combined\n * @param {Extension} extension\n * @returns {void}\n */\nfunction extension(combined, extension) {\n /** @type {keyof Extension} */\n let key\n for (key in extension) {\n if (own.call(extension, key)) {\n if (key === 'canContainEols') {\n const right = extension[key]\n if (right) {\n combined[key].push(...right)\n }\n } else if (key === 'transforms') {\n const right = extension[key]\n if (right) {\n combined[key].push(...right)\n }\n } else if (key === 'enter' || key === 'exit') {\n const right = extension[key]\n if (right) {\n Object.assign(combined[key], right)\n }\n }\n }\n }\n}\n\n/** @type {OnEnterError} */\nfunction defaultOnError(left, right) {\n if (left) {\n throw new Error(\n 'Cannot close `' +\n left.type +\n '` (' +\n stringifyPosition({\n start: left.start,\n end: left.end\n }) +\n '): a different token (`' +\n right.type +\n '`, ' +\n stringifyPosition({\n start: right.start,\n end: right.end\n }) +\n ') is open'\n )\n } else {\n throw new Error(\n 'Cannot close document, a token (`' +\n right.type +\n '`, ' +\n stringifyPosition({\n start: right.start,\n end: right.end\n }) +\n ') is still open'\n )\n }\n}\n","/**\n * @typedef {import('mdast').Root} Root\n * @typedef {import('mdast-util-from-markdown').Options} Options\n */\n\nimport {fromMarkdown} from 'mdast-util-from-markdown'\n\n/**\n * @this {import('unified').Processor}\n * @type {import('unified').Plugin<[Options?] | void[], string, Root>}\n */\nexport default function remarkParse(options) {\n /** @type {import('unified').ParserFunction} */\n const parser = (doc) => {\n // Assume options.\n const settings = /** @type {Options} */ (this.data('settings'))\n\n return fromMarkdown(\n doc,\n Object.assign({}, settings, options, {\n // Note: these options are not in the readme.\n // The goal is for them to be set by plugins on `data` instead of being\n // passed by users.\n extensions: this.data('micromarkExtensions') || [],\n mdastExtensions: this.data('fromMarkdownExtensions') || []\n })\n )\n }\n\n Object.assign(this, {Parser: parser})\n}\n","/**\n * @typedef {import('unist').Node} Node\n */\n\n/**\n * @typedef {Array | string} ChildrenOrValue\n * List to use as `children` or value to use as `value`.\n *\n * @typedef {Record} Props\n * Other fields to add to the node.\n */\n\n/**\n * Build a node.\n *\n * @param type\n * Node type.\n * @param props\n * Fields assigned to node.\n * @param value\n * Children of node or value of `node` (cast to string).\n * @returns\n * Built node.\n */\nexport const u =\n /**\n * @type {(\n * ((type: T) => {type: T}) &\n * ((type: T, props: P) => {type: T} & P) &\n * ((type: T, value: string) => {type: T, value: string}) &\n * ((type: T, props: P, value: string) => {type: T, value: string} & P) &\n * (>(type: T, children: C) => {type: T, children: C}) &\n * (>(type: T, props: P, children: C) => {type: T, children: C} & P)\n * )}\n */\n (\n /**\n * @param {string} type\n * @param {Props | ChildrenOrValue | null | undefined} [props]\n * @param {ChildrenOrValue | null | undefined} [value]\n * @returns {Node}\n */\n function (type, props, value) {\n /** @type {Node} */\n const node = {type: String(type)}\n\n if (\n (value === undefined || value === null) &&\n (typeof props === 'string' || Array.isArray(props))\n ) {\n value = props\n } else {\n Object.assign(node, props)\n }\n\n if (Array.isArray(value)) {\n // @ts-expect-error: create a parent.\n node.children = value\n } else if (value !== undefined && value !== null) {\n // @ts-expect-error: create a literal.\n node.value = String(value)\n }\n\n return node\n }\n )\n","/**\n * @typedef {import('mdast').Root|import('mdast').Parent['children'][number]} MdastNode\n * @typedef {import('./index.js').H} H\n * @typedef {import('./index.js').Handler} Handler\n * @typedef {import('./index.js').Content} Content\n */\n\nimport {u} from 'unist-builder'\n\nconst own = {}.hasOwnProperty\n\n/**\n * Transform an unknown node.\n * @type {Handler}\n * @param {MdastNode} node\n */\nfunction unknown(h, node) {\n const data = node.data || {}\n\n if (\n 'value' in node &&\n !(\n own.call(data, 'hName') ||\n own.call(data, 'hProperties') ||\n own.call(data, 'hChildren')\n )\n ) {\n return h.augment(node, u('text', node.value))\n }\n\n return h(node, 'div', all(h, node))\n}\n\n/**\n * @type {Handler}\n * @param {MdastNode} node\n */\nexport function one(h, node, parent) {\n const type = node && node.type\n /** @type {Handler} */\n let fn\n\n // Fail on non-nodes.\n if (!type) {\n throw new Error('Expected node, got `' + node + '`')\n }\n\n if (own.call(h.handlers, type)) {\n fn = h.handlers[type]\n } else if (h.passThrough && h.passThrough.includes(type)) {\n fn = returnNode\n } else {\n fn = h.unknownHandler\n }\n\n return (typeof fn === 'function' ? fn : unknown)(h, node, parent)\n}\n\n/**\n * @type {Handler}\n * @param {MdastNode} node\n */\nfunction returnNode(h, node) {\n // @ts-expect-error: Pass through custom node.\n return 'children' in node ? {...node, children: all(h, node)} : node\n}\n\n/**\n * @param {H} h\n * @param {MdastNode} parent\n */\nexport function all(h, parent) {\n /** @type {Array.} */\n const values = []\n\n if ('children' in parent) {\n const nodes = parent.children\n let index = -1\n\n while (++index < nodes.length) {\n const result = one(h, nodes[index], parent)\n\n if (result) {\n if (index && nodes[index - 1].type === 'break') {\n if (!Array.isArray(result) && result.type === 'text') {\n result.value = result.value.replace(/^\\s+/, '')\n }\n\n if (!Array.isArray(result) && result.type === 'element') {\n const head = result.children[0]\n\n if (head && head.type === 'text') {\n head.value = head.value.replace(/^\\s+/, '')\n }\n }\n }\n\n if (Array.isArray(result)) {\n values.push(...result)\n } else {\n values.push(result)\n }\n }\n }\n }\n\n return values\n}\n","/**\n * @typedef {import('unist').Node} Node\n * @typedef {import('unist').Parent} Parent\n */\n\n/**\n * @typedef {Record} Props\n * @typedef {null | undefined | string | Props | TestFunctionAnything | Array} Test\n * Check for an arbitrary node, unaware of TypeScript inferral.\n *\n * @callback TestFunctionAnything\n * Check if a node passes a test, unaware of TypeScript inferral.\n * @param {unknown} this\n * The given context.\n * @param {Node} node\n * A node.\n * @param {number | null | undefined} [index]\n * The node’s position in its parent.\n * @param {Parent | null | undefined} [parent]\n * The node’s parent.\n * @returns {boolean | void}\n * Whether this node passes the test.\n */\n\n/**\n * @template {Node} Kind\n * Node type.\n * @typedef {Kind['type'] | Partial | TestFunctionPredicate | Array | TestFunctionPredicate>} PredicateTest\n * Check for a node that can be inferred by TypeScript.\n */\n\n/**\n * Check if a node passes a certain test.\n *\n * @template {Node} Kind\n * Node type.\n * @callback TestFunctionPredicate\n * Complex test function for a node that can be inferred by TypeScript.\n * @param {Node} node\n * A node.\n * @param {number | null | undefined} [index]\n * The node’s position in its parent.\n * @param {Parent | null | undefined} [parent]\n * The node’s parent.\n * @returns {node is Kind}\n * Whether this node passes the test.\n */\n\n/**\n * @callback AssertAnything\n * Check that an arbitrary value is a node, unaware of TypeScript inferral.\n * @param {unknown} [node]\n * Anything (typically a node).\n * @param {number | null | undefined} [index]\n * The node’s position in its parent.\n * @param {Parent | null | undefined} [parent]\n * The node’s parent.\n * @returns {boolean}\n * Whether this is a node and passes a test.\n */\n\n/**\n * Check if a node is a node and passes a certain node test.\n *\n * @template {Node} Kind\n * Node type.\n * @callback AssertPredicate\n * Check that an arbitrary value is a specific node, aware of TypeScript.\n * @param {unknown} [node]\n * Anything (typically a node).\n * @param {number | null | undefined} [index]\n * The node’s position in its parent.\n * @param {Parent | null | undefined} [parent]\n * The node’s parent.\n * @returns {node is Kind}\n * Whether this is a node and passes a test.\n */\n\n/**\n * Check if `node` is a `Node` and whether it passes the given test.\n *\n * @param node\n * Thing to check, typically `Node`.\n * @param test\n * A check for a specific node.\n * @param index\n * The node’s position in its parent.\n * @param parent\n * The node’s parent.\n * @returns\n * Whether `node` is a node and passes a test.\n */\nexport const is =\n /**\n * @type {(\n * (() => false) &\n * ((node: unknown, test: PredicateTest, index: number, parent: Parent, context?: unknown) => node is Kind) &\n * ((node: unknown, test: PredicateTest, index?: null | undefined, parent?: null | undefined, context?: unknown) => node is Kind) &\n * ((node: unknown, test: Test, index: number, parent: Parent, context?: unknown) => boolean) &\n * ((node: unknown, test?: Test, index?: null | undefined, parent?: null | undefined, context?: unknown) => boolean)\n * )}\n */\n (\n /**\n * @param {unknown} [node]\n * @param {Test} [test]\n * @param {number | null | undefined} [index]\n * @param {Parent | null | undefined} [parent]\n * @param {unknown} [context]\n * @returns {boolean}\n */\n // eslint-disable-next-line max-params\n function is(node, test, index, parent, context) {\n const check = convert(test)\n\n if (\n index !== undefined &&\n index !== null &&\n (typeof index !== 'number' ||\n index < 0 ||\n index === Number.POSITIVE_INFINITY)\n ) {\n throw new Error('Expected positive finite index')\n }\n\n if (\n parent !== undefined &&\n parent !== null &&\n (!is(parent) || !parent.children)\n ) {\n throw new Error('Expected parent node')\n }\n\n if (\n (parent === undefined || parent === null) !==\n (index === undefined || index === null)\n ) {\n throw new Error('Expected both parent and index')\n }\n\n // @ts-expect-error Looks like a node.\n return node && node.type && typeof node.type === 'string'\n ? Boolean(check.call(context, node, index, parent))\n : false\n }\n )\n\n/**\n * Generate an assertion from a test.\n *\n * Useful if you’re going to test many nodes, for example when creating a\n * utility where something else passes a compatible test.\n *\n * The created function is a bit faster because it expects valid input only:\n * a `node`, `index`, and `parent`.\n *\n * @param test\n * * when nullish, checks if `node` is a `Node`.\n * * when `string`, works like passing `(node) => node.type === test`.\n * * when `function` checks if function passed the node is true.\n * * when `object`, checks that all keys in test are in node, and that they have (strictly) equal values.\n * * when `array`, checks if any one of the subtests pass.\n * @returns\n * An assertion.\n */\nexport const convert =\n /**\n * @type {(\n * ((test: PredicateTest) => AssertPredicate) &\n * ((test?: Test) => AssertAnything)\n * )}\n */\n (\n /**\n * @param {Test} [test]\n * @returns {AssertAnything}\n */\n function (test) {\n if (test === undefined || test === null) {\n return ok\n }\n\n if (typeof test === 'string') {\n return typeFactory(test)\n }\n\n if (typeof test === 'object') {\n return Array.isArray(test) ? anyFactory(test) : propsFactory(test)\n }\n\n if (typeof test === 'function') {\n return castFactory(test)\n }\n\n throw new Error('Expected function, string, or object as test')\n }\n )\n\n/**\n * @param {Array} tests\n * @returns {AssertAnything}\n */\nfunction anyFactory(tests) {\n /** @type {Array} */\n const checks = []\n let index = -1\n\n while (++index < tests.length) {\n checks[index] = convert(tests[index])\n }\n\n return castFactory(any)\n\n /**\n * @this {unknown}\n * @param {Array} parameters\n * @returns {boolean}\n */\n function any(...parameters) {\n let index = -1\n\n while (++index < checks.length) {\n if (checks[index].call(this, ...parameters)) return true\n }\n\n return false\n }\n}\n\n/**\n * Turn an object into a test for a node with a certain fields.\n *\n * @param {Props} check\n * @returns {AssertAnything}\n */\nfunction propsFactory(check) {\n return castFactory(all)\n\n /**\n * @param {Node} node\n * @returns {boolean}\n */\n function all(node) {\n /** @type {string} */\n let key\n\n for (key in check) {\n // @ts-expect-error: hush, it sure works as an index.\n if (node[key] !== check[key]) return false\n }\n\n return true\n }\n}\n\n/**\n * Turn a string into a test for a node with a certain type.\n *\n * @param {string} check\n * @returns {AssertAnything}\n */\nfunction typeFactory(check) {\n return castFactory(type)\n\n /**\n * @param {Node} node\n */\n function type(node) {\n return node && node.type === check\n }\n}\n\n/**\n * Turn a custom test into a test for a node that passes that test.\n *\n * @param {TestFunctionAnything} check\n * @returns {AssertAnything}\n */\nfunction castFactory(check) {\n return assertion\n\n /**\n * @this {unknown}\n * @param {unknown} node\n * @param {Array} parameters\n * @returns {boolean}\n */\n function assertion(node, ...parameters) {\n return Boolean(\n node &&\n typeof node === 'object' &&\n 'type' in node &&\n // @ts-expect-error: fine.\n Boolean(check.call(this, node, ...parameters))\n )\n }\n}\n\nfunction ok() {\n return true\n}\n","/**\n * @typedef {import('unist').Node} Node\n * @typedef {import('unist').Parent} Parent\n * @typedef {import('unist-util-is').Test} Test\n */\n\n/**\n * @typedef {boolean | 'skip'} Action\n * Union of the action types.\n *\n * @typedef {number} Index\n * Move to the sibling at `index` next (after node itself is completely\n * traversed).\n *\n * Useful if mutating the tree, such as removing the node the visitor is\n * currently on, or any of its previous siblings.\n * Results less than 0 or greater than or equal to `children.length` stop\n * traversing the parent.\n *\n * @typedef {[(Action | null | undefined | void)?, (Index | null | undefined)?]} ActionTuple\n * List with one or two values, the first an action, the second an index.\n *\n * @typedef {Action | ActionTuple | Index | null | undefined | void} VisitorResult\n * Any value that can be returned from a visitor.\n */\n\n/**\n * @template {Node} [Visited=Node]\n * Visited node type.\n * @template {Parent} [Ancestor=Parent]\n * Ancestor type.\n * @callback Visitor\n * Handle a node (matching `test`, if given).\n *\n * Visitors are free to transform `node`.\n * They can also transform the parent of node (the last of `ancestors`).\n *\n * Replacing `node` itself, if `SKIP` is not returned, still causes its\n * descendants to be walked (which is a bug).\n *\n * When adding or removing previous siblings of `node` (or next siblings, in\n * case of reverse), the `Visitor` should return a new `Index` to specify the\n * sibling to traverse after `node` is traversed.\n * Adding or removing next siblings of `node` (or previous siblings, in case\n * of reverse) is handled as expected without needing to return a new `Index`.\n *\n * Removing the children property of an ancestor still results in them being\n * traversed.\n * @param {Visited} node\n * Found node.\n * @param {Array} ancestors\n * Ancestors of `node`.\n * @returns {VisitorResult}\n * What to do next.\n *\n * An `Index` is treated as a tuple of `[CONTINUE, Index]`.\n * An `Action` is treated as a tuple of `[Action]`.\n *\n * Passing a tuple back only makes sense if the `Action` is `SKIP`.\n * When the `Action` is `EXIT`, that action can be returned.\n * When the `Action` is `CONTINUE`, `Index` can be returned.\n */\n\n/**\n * @template {Node} [Tree=Node]\n * Tree type.\n * @template {Test} [Check=string]\n * Test type.\n * @typedef {Visitor, Check>, Extract, Parent>>} BuildVisitor\n * Build a typed `Visitor` function from a tree and a test.\n *\n * It will infer which values are passed as `node` and which as `parents`.\n */\n\nimport {convert} from 'unist-util-is'\nimport {color} from './color.js'\n\n/**\n * Continue traversing as normal.\n */\nexport const CONTINUE = true\n\n/**\n * Stop traversing immediately.\n */\nexport const EXIT = false\n\n/**\n * Do not traverse this node’s children.\n */\nexport const SKIP = 'skip'\n\n/**\n * Visit nodes, with ancestral information.\n *\n * This algorithm performs *depth-first* *tree traversal* in *preorder*\n * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**).\n *\n * You can choose for which nodes `visitor` is called by passing a `test`.\n * For complex tests, you should test yourself in `visitor`, as it will be\n * faster and will have improved type information.\n *\n * Walking the tree is an intensive task.\n * Make use of the return values of the visitor when possible.\n * Instead of walking a tree multiple times, walk it once, use `unist-util-is`\n * to check if a node matches, and then perform different operations.\n *\n * You can change the tree.\n * See `Visitor` for more info.\n *\n * @param tree\n * Tree to traverse.\n * @param test\n * `unist-util-is`-compatible test\n * @param visitor\n * Handle each node.\n * @param reverse\n * Traverse in reverse preorder (NRL) instead of the default preorder (NLR).\n * @returns\n * Nothing.\n */\nexport const visitParents =\n /**\n * @type {(\n * ((tree: Tree, test: Check, visitor: BuildVisitor, reverse?: boolean | null | undefined) => void) &\n * ((tree: Tree, visitor: BuildVisitor, reverse?: boolean | null | undefined) => void)\n * )}\n */\n (\n /**\n * @param {Node} tree\n * @param {Test} test\n * @param {Visitor} visitor\n * @param {boolean | null | undefined} [reverse]\n * @returns {void}\n */\n function (tree, test, visitor, reverse) {\n if (typeof test === 'function' && typeof visitor !== 'function') {\n reverse = visitor\n // @ts-expect-error no visitor given, so `visitor` is test.\n visitor = test\n test = null\n }\n\n const is = convert(test)\n const step = reverse ? -1 : 1\n\n factory(tree, undefined, [])()\n\n /**\n * @param {Node} node\n * @param {number | undefined} index\n * @param {Array} parents\n */\n function factory(node, index, parents) {\n /** @type {Record} */\n // @ts-expect-error: hush\n const value = node && typeof node === 'object' ? node : {}\n\n if (typeof value.type === 'string') {\n const name =\n // `hast`\n typeof value.tagName === 'string'\n ? value.tagName\n : // `xast`\n typeof value.name === 'string'\n ? value.name\n : undefined\n\n Object.defineProperty(visit, 'name', {\n value:\n 'node (' + color(node.type + (name ? '<' + name + '>' : '')) + ')'\n })\n }\n\n return visit\n\n function visit() {\n /** @type {ActionTuple} */\n let result = []\n /** @type {ActionTuple} */\n let subresult\n /** @type {number} */\n let offset\n /** @type {Array} */\n let grandparents\n\n if (!test || is(node, index, parents[parents.length - 1] || null)) {\n result = toResult(visitor(node, parents))\n\n if (result[0] === EXIT) {\n return result\n }\n }\n\n // @ts-expect-error looks like a parent.\n if (node.children && result[0] !== SKIP) {\n // @ts-expect-error looks like a parent.\n offset = (reverse ? node.children.length : -1) + step\n // @ts-expect-error looks like a parent.\n grandparents = parents.concat(node)\n\n // @ts-expect-error looks like a parent.\n while (offset > -1 && offset < node.children.length) {\n // @ts-expect-error looks like a parent.\n subresult = factory(node.children[offset], offset, grandparents)()\n\n if (subresult[0] === EXIT) {\n return subresult\n }\n\n offset =\n typeof subresult[1] === 'number' ? subresult[1] : offset + step\n }\n }\n\n return result\n }\n }\n }\n )\n\n/**\n * Turn a return value into a clean result.\n *\n * @param {VisitorResult} value\n * Valid return values from visitors.\n * @returns {ActionTuple}\n * Clean result.\n */\nfunction toResult(value) {\n if (Array.isArray(value)) {\n return value\n }\n\n if (typeof value === 'number') {\n return [CONTINUE, value]\n }\n\n return [value]\n}\n","/**\n * @typedef {import('unist').Node} Node\n * @typedef {import('unist').Parent} Parent\n * @typedef {import('unist-util-is').Test} Test\n * @typedef {import('unist-util-visit-parents').VisitorResult} VisitorResult\n */\n\n/**\n * Check if `Child` can be a child of `Ancestor`.\n *\n * Returns the ancestor when `Child` can be a child of `Ancestor`, or returns\n * `never`.\n *\n * @template {Node} Ancestor\n * Node type.\n * @template {Node} Child\n * Node type.\n * @typedef {(\n * Ancestor extends Parent\n * ? Child extends Ancestor['children'][number]\n * ? Ancestor\n * : never\n * : never\n * )} ParentsOf\n */\n\n/**\n * @template {Node} [Visited=Node]\n * Visited node type.\n * @template {Parent} [Ancestor=Parent]\n * Ancestor type.\n * @callback Visitor\n * Handle a node (matching `test`, if given).\n *\n * Visitors are free to transform `node`.\n * They can also transform `parent`.\n *\n * Replacing `node` itself, if `SKIP` is not returned, still causes its\n * descendants to be walked (which is a bug).\n *\n * When adding or removing previous siblings of `node` (or next siblings, in\n * case of reverse), the `Visitor` should return a new `Index` to specify the\n * sibling to traverse after `node` is traversed.\n * Adding or removing next siblings of `node` (or previous siblings, in case\n * of reverse) is handled as expected without needing to return a new `Index`.\n *\n * Removing the children property of `parent` still results in them being\n * traversed.\n * @param {Visited} node\n * Found node.\n * @param {Visited extends Node ? number | null : never} index\n * Index of `node` in `parent`.\n * @param {Ancestor extends Node ? Ancestor | null : never} parent\n * Parent of `node`.\n * @returns {VisitorResult}\n * What to do next.\n *\n * An `Index` is treated as a tuple of `[CONTINUE, Index]`.\n * An `Action` is treated as a tuple of `[Action]`.\n *\n * Passing a tuple back only makes sense if the `Action` is `SKIP`.\n * When the `Action` is `EXIT`, that action can be returned.\n * When the `Action` is `CONTINUE`, `Index` can be returned.\n */\n\n/**\n * Build a typed `Visitor` function from a node and all possible parents.\n *\n * It will infer which values are passed as `node` and which as `parent`.\n *\n * @template {Node} Visited\n * Node type.\n * @template {Parent} Ancestor\n * Parent type.\n * @typedef {Visitor>} BuildVisitorFromMatch\n */\n\n/**\n * Build a typed `Visitor` function from a list of descendants and a test.\n *\n * It will infer which values are passed as `node` and which as `parent`.\n *\n * @template {Node} Descendant\n * Node type.\n * @template {Test} Check\n * Test type.\n * @typedef {(\n * BuildVisitorFromMatch<\n * import('unist-util-visit-parents/complex-types.js').Matches,\n * Extract\n * >\n * )} BuildVisitorFromDescendants\n */\n\n/**\n * Build a typed `Visitor` function from a tree and a test.\n *\n * It will infer which values are passed as `node` and which as `parent`.\n *\n * @template {Node} [Tree=Node]\n * Node type.\n * @template {Test} [Check=string]\n * Test type.\n * @typedef {(\n * BuildVisitorFromDescendants<\n * import('unist-util-visit-parents/complex-types.js').InclusiveDescendant,\n * Check\n * >\n * )} BuildVisitor\n */\n\nimport {visitParents} from 'unist-util-visit-parents'\n\n/**\n * Visit nodes.\n *\n * This algorithm performs *depth-first* *tree traversal* in *preorder*\n * (**NLR**) or if `reverse` is given, in *reverse preorder* (**NRL**).\n *\n * You can choose for which nodes `visitor` is called by passing a `test`.\n * For complex tests, you should test yourself in `visitor`, as it will be\n * faster and will have improved type information.\n *\n * Walking the tree is an intensive task.\n * Make use of the return values of the visitor when possible.\n * Instead of walking a tree multiple times, walk it once, use `unist-util-is`\n * to check if a node matches, and then perform different operations.\n *\n * You can change the tree.\n * See `Visitor` for more info.\n *\n * @param tree\n * Tree to traverse.\n * @param test\n * `unist-util-is`-compatible test\n * @param visitor\n * Handle each node.\n * @param reverse\n * Traverse in reverse preorder (NRL) instead of the default preorder (NLR).\n * @returns\n * Nothing.\n */\nexport const visit =\n /**\n * @type {(\n * ((tree: Tree, test: Check, visitor: BuildVisitor, reverse?: boolean | null | undefined) => void) &\n * ((tree: Tree, visitor: BuildVisitor, reverse?: boolean | null | undefined) => void)\n * )}\n */\n (\n /**\n * @param {Node} tree\n * @param {Test} test\n * @param {Visitor} visitor\n * @param {boolean | null | undefined} [reverse]\n * @returns {void}\n */\n function (tree, test, visitor, reverse) {\n if (typeof test === 'function' && typeof visitor !== 'function') {\n reverse = visitor\n visitor = test\n test = null\n }\n\n visitParents(tree, test, overload, reverse)\n\n /**\n * @param {Node} node\n * @param {Array} parents\n */\n function overload(node, parents) {\n const parent = parents[parents.length - 1]\n return visitor(\n node,\n parent ? parent.children.indexOf(node) : null,\n parent\n )\n }\n }\n )\n\nexport {CONTINUE, EXIT, SKIP} from 'unist-util-visit-parents'\n","/**\n * @typedef {import('unist').Position} Position\n * @typedef {import('unist').Node} Node\n * @typedef {import('unist').Point} Point\n */\n\n/**\n * @typedef NodeLike\n * @property {string} type\n * @property {PositionLike | null | undefined} [position]\n *\n * @typedef PositionLike\n * @property {PointLike | null | undefined} [start]\n * @property {PointLike | null | undefined} [end]\n *\n * @typedef PointLike\n * @property {number | null | undefined} [line]\n * @property {number | null | undefined} [column]\n * @property {number | null | undefined} [offset]\n */\n\n/**\n * Get the starting point of `node`.\n *\n * @param node\n * Node.\n * @returns\n * Point.\n */\nexport const pointStart = point('start')\n\n/**\n * Get the ending point of `node`.\n *\n * @param node\n * Node.\n * @returns\n * Point.\n */\nexport const pointEnd = point('end')\n\n/**\n * Get the positional info of `node`.\n *\n * @param {NodeLike | Node | null | undefined} [node]\n * Node.\n * @returns {Position}\n * Position.\n */\nexport function position(node) {\n return {start: pointStart(node), end: pointEnd(node)}\n}\n\n/**\n * Get the positional info of `node`.\n *\n * @param {'start' | 'end'} type\n * Side.\n * @returns\n * Getter.\n */\nfunction point(type) {\n return point\n\n /**\n * Get the point info of `node` at a bound side.\n *\n * @param {NodeLike | Node | null | undefined} [node]\n * @returns {Point}\n */\n function point(node) {\n const point = (node && node.position && node.position[type]) || {}\n\n // To do: next major: don’t return points when invalid.\n return {\n // @ts-expect-error: in practice, null is allowed.\n line: point.line || null,\n // @ts-expect-error: in practice, null is allowed.\n column: point.column || null,\n // @ts-expect-error: in practice, null is allowed.\n offset: point.offset > -1 ? point.offset : null\n }\n }\n}\n","/**\n * @typedef PointLike\n * @property {number | null | undefined} [line]\n * @property {number | null | undefined} [column]\n * @property {number | null | undefined} [offset]\n *\n * @typedef PositionLike\n * @property {PointLike | null | undefined} [start]\n * @property {PointLike | null | undefined} [end]\n *\n * @typedef NodeLike\n * @property {PositionLike | null | undefined} [position]\n */\n\n/**\n * Check if `node` is generated.\n *\n * @param {NodeLike | null | undefined} [node]\n * Node to check.\n * @returns {boolean}\n * Whether `node` is generated (does not have positional info).\n */\nexport function generated(node) {\n return (\n !node ||\n !node.position ||\n !node.position.start ||\n !node.position.start.line ||\n !node.position.start.column ||\n !node.position.end ||\n !node.position.end.line ||\n !node.position.end.column\n )\n}\n","/**\n * @typedef {import('mdast').Root} Root\n * @typedef {import('mdast').Content} Content\n * @typedef {import('mdast').Definition} Definition\n */\n\n/**\n * @typedef {Root | Content} Node\n *\n * @callback GetDefinition\n * Get a definition by identifier.\n * @param {string | null | undefined} [identifier]\n * Identifier of definition.\n * @returns {Definition | null}\n * Definition corresponding to `identifier` or `null`.\n */\n\nimport {visit} from 'unist-util-visit'\n\nconst own = {}.hasOwnProperty\n\n/**\n * Find definitions in `tree`.\n *\n * Uses CommonMark precedence, which means that earlier definitions are\n * preferred over duplicate later definitions.\n *\n * @param {Node} tree\n * Tree to check.\n * @returns {GetDefinition}\n * Getter.\n */\nexport function definitions(tree) {\n /** @type {Record} */\n const cache = Object.create(null)\n\n if (!tree || !tree.type) {\n throw new Error('mdast-util-definitions expected node')\n }\n\n visit(tree, 'definition', (definition) => {\n const id = clean(definition.identifier)\n if (id && !own.call(cache, id)) {\n cache[id] = definition\n }\n })\n\n return definition\n\n /** @type {GetDefinition} */\n function definition(identifier) {\n const id = clean(identifier)\n // To do: next major: return `undefined` when not found.\n return id && own.call(cache, id) ? cache[id] : null\n }\n}\n\n/**\n * @param {string | null | undefined} [value]\n * @returns {string}\n */\nfunction clean(value) {\n return String(value || '').toUpperCase()\n}\n","/**\n * @typedef {import('mdast').ThematicBreak} ThematicBreak\n * @typedef {import('hast').Element} Element\n * @typedef {import('../index.js').Handler} Handler\n */\n\n/**\n * @type {Handler}\n * @param {ThematicBreak} [node]\n * @returns {Element}\n */\nexport function thematicBreak(h, node) {\n return h(node, 'hr')\n}\n","/**\n * @typedef {import('./index.js').Content} Content\n */\n\nimport {u} from 'unist-builder'\n\n/**\n * Wrap `nodes` with line feeds between each entry.\n * Optionally adds line feeds at the start and end.\n *\n * @param {Array.} nodes\n * @param {boolean} [loose=false]\n * @returns {Array.}\n */\nexport function wrap(nodes, loose) {\n /** @type {Array.} */\n const result = []\n let index = -1\n\n if (loose) {\n result.push(u('text', '\\n'))\n }\n\n while (++index < nodes.length) {\n if (index) result.push(u('text', '\\n'))\n result.push(nodes[index])\n }\n\n if (loose && nodes.length > 0) {\n result.push(u('text', '\\n'))\n }\n\n return result\n}\n","/**\n * @typedef {import('mdast').List} List\n * @typedef {import('hast').Element} Element\n * @typedef {import('hast').Properties} Properties\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {wrap} from '../wrap.js'\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {List} node\n * @returns {Element}\n */\nexport function list(h, node) {\n /** @type {Properties} */\n const props = {}\n const name = node.ordered ? 'ol' : 'ul'\n const items = all(h, node)\n let index = -1\n\n if (typeof node.start === 'number' && node.start !== 1) {\n props.start = node.start\n }\n\n // Like GitHub, add a class for custom styling.\n while (++index < items.length) {\n const item = items[index]\n\n if (\n item.type === 'element' &&\n item.tagName === 'li' &&\n item.properties &&\n Array.isArray(item.properties.className) &&\n item.properties.className.includes('task-list-item')\n ) {\n props.className = ['contains-task-list']\n break\n }\n }\n\n return h(node, name, props, wrap(items, true))\n}\n","/**\n * @typedef {import('mdast').BlockContent} BlockContent\n * @typedef {import('mdast').FootnoteDefinition} FootnoteDefinition\n * @typedef {import('mdast').Link} Link\n * @typedef {import('mdast').ListItem} ListItem\n * @typedef {import('mdast').Paragraph} Paragraph\n * @typedef {import('./index.js').H} H\n */\n\nimport {thematicBreak} from './handlers/thematic-break.js'\nimport {list} from './handlers/list.js'\nimport {wrap} from './wrap.js'\n\n/**\n * @param {H} h\n */\nexport function footer(h) {\n const footnoteById = h.footnoteById\n const footnoteOrder = h.footnoteOrder\n let index = -1\n /** @type {Array.} */\n const listItems = []\n\n while (++index < footnoteOrder.length) {\n const def = footnoteById[footnoteOrder[index].toUpperCase()]\n\n if (!def) {\n continue\n }\n\n const marker = String(index + 1)\n const content = [...def.children]\n /** @type {Link} */\n const backReference = {\n type: 'link',\n url: '#fnref' + marker,\n data: {hProperties: {className: ['footnote-back'], role: 'doc-backlink'}},\n children: [{type: 'text', value: '↩'}]\n }\n const tail = content[content.length - 1]\n\n if (tail && tail.type === 'paragraph') {\n tail.children.push(backReference)\n } else {\n // @ts-expect-error Indeed, link directly added in block content.\n // Which we do because that way at least the handlers will be called\n // for the other HTML we’re generating (as markdown).\n content.push(backReference)\n }\n\n listItems.push({\n type: 'listItem',\n data: {hProperties: {id: 'fn' + marker, role: 'doc-endnote'}},\n children: content,\n position: def.position\n })\n }\n\n if (listItems.length === 0) {\n return null\n }\n\n return h(\n null,\n 'section',\n {className: ['footnotes'], role: 'doc-endnotes'},\n wrap(\n [\n thematicBreak(h),\n list(h, {type: 'list', ordered: true, children: listItems})\n ],\n true\n )\n )\n}\n","/**\n * @typedef {import('mdast').Blockquote} Blockquote\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {wrap} from '../wrap.js'\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {Blockquote} node\n */\nexport function blockquote(h, node) {\n return h(node, 'blockquote', wrap(all(h, node), true))\n}\n","/**\n * @typedef {import('hast').Element} Element\n * @typedef {import('hast').Text} Text\n * @typedef {import('mdast').Break} Break\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {u} from 'unist-builder'\n\n/**\n * @type {Handler}\n * @param {Break} node\n * @returns {Array}\n */\nexport function hardBreak(h, node) {\n return [h(node, 'br'), u('text', '\\n')]\n}\n","/**\n * @typedef {import('mdast').Code} Code\n * @typedef {import('hast').Element} Element\n * @typedef {import('hast').Properties} Properties\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {u} from 'unist-builder'\n\n/**\n * @type {Handler}\n * @param {Code} node\n */\nexport function code(h, node) {\n const value = node.value ? node.value + '\\n' : ''\n // To do: next major, use `node.lang` w/o regex, the splitting’s been going\n // on for years in remark now.\n const lang = node.lang && node.lang.match(/^[^ \\t]+(?=[ \\t]|$)/)\n /** @type {Properties} */\n const props = {}\n\n if (lang) {\n props.className = ['language-' + lang]\n }\n\n const code = h(node, 'code', props, [u('text', value)])\n\n if (node.meta) {\n code.data = {meta: node.meta}\n }\n\n return h(node.position, 'pre', [code])\n}\n","/**\n * @typedef {import('mdast').Delete} Delete\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {Delete} node\n */\nexport function strikethrough(h, node) {\n return h(node, 'del', all(h, node))\n}\n","/**\n * @typedef {import('mdast').Emphasis} Emphasis\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {Emphasis} node\n */\nexport function emphasis(h, node) {\n return h(node, 'em', all(h, node))\n}\n","/**\n * @typedef {import('mdast').FootnoteReference} FootnoteReference\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {u} from 'unist-builder'\n\n/**\n * @type {Handler}\n * @param {FootnoteReference} node\n */\nexport function footnoteReference(h, node) {\n const footnoteOrder = h.footnoteOrder\n const identifier = String(node.identifier)\n const index = footnoteOrder.indexOf(identifier)\n const marker = String(\n index === -1 ? footnoteOrder.push(identifier) : index + 1\n )\n\n return h(\n node,\n 'a',\n {\n href: '#fn' + marker,\n className: ['footnote-ref'],\n id: 'fnref' + marker,\n role: 'doc-noteref'\n },\n [h(node.position, 'sup', [u('text', marker)])]\n )\n}\n","/**\n * @typedef {import('mdast').Footnote} Footnote\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {footnoteReference} from './footnote-reference.js'\n\n/**\n * @type {Handler}\n * @param {Footnote} node\n */\nexport function footnote(h, node) {\n const footnoteById = h.footnoteById\n const footnoteOrder = h.footnoteOrder\n let no = 1\n\n while (no in footnoteById) no++\n\n const identifier = String(no)\n\n // No need to check if `identifier` exists in `footnoteOrder`, it’s guaranteed\n // to not exist because we just generated it.\n footnoteOrder.push(identifier)\n\n footnoteById[identifier] = {\n type: 'footnoteDefinition',\n identifier,\n children: [{type: 'paragraph', children: node.children}],\n position: node.position\n }\n\n return footnoteReference(h, {\n type: 'footnoteReference',\n identifier,\n position: node.position\n })\n}\n","/**\n * @typedef {import('mdast').Heading} Heading\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {Heading} node\n */\nexport function heading(h, node) {\n return h(node, 'h' + node.depth, all(h, node))\n}\n","/**\n * @typedef {import('mdast').HTML} HTML\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {u} from 'unist-builder'\n\n/**\n * Return either a `raw` node in dangerous mode, otherwise nothing.\n *\n * @type {Handler}\n * @param {HTML} node\n */\nexport function html(h, node) {\n return h.dangerous ? h.augment(node, u('raw', node.value)) : null\n}\n","\n'use strict';\n\n\nvar encodeCache = {};\n\n\n// Create a lookup array where anything but characters in `chars` string\n// and alphanumeric chars is percent-encoded.\n//\nfunction getEncodeCache(exclude) {\n var i, ch, cache = encodeCache[exclude];\n if (cache) { return cache; }\n\n cache = encodeCache[exclude] = [];\n\n for (i = 0; i < 128; i++) {\n ch = String.fromCharCode(i);\n\n if (/^[0-9a-z]$/i.test(ch)) {\n // always allow unencoded alphanumeric characters\n cache.push(ch);\n } else {\n cache.push('%' + ('0' + i.toString(16).toUpperCase()).slice(-2));\n }\n }\n\n for (i = 0; i < exclude.length; i++) {\n cache[exclude.charCodeAt(i)] = exclude[i];\n }\n\n return cache;\n}\n\n\n// Encode unsafe characters with percent-encoding, skipping already\n// encoded sequences.\n//\n// - string - string to encode\n// - exclude - list of characters to ignore (in addition to a-zA-Z0-9)\n// - keepEscaped - don't encode '%' in a correct escape sequence (default: true)\n//\nfunction encode(string, exclude, keepEscaped) {\n var i, l, code, nextCode, cache,\n result = '';\n\n if (typeof exclude !== 'string') {\n // encode(string, keepEscaped)\n keepEscaped = exclude;\n exclude = encode.defaultChars;\n }\n\n if (typeof keepEscaped === 'undefined') {\n keepEscaped = true;\n }\n\n cache = getEncodeCache(exclude);\n\n for (i = 0, l = string.length; i < l; i++) {\n code = string.charCodeAt(i);\n\n if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) {\n if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) {\n result += string.slice(i, i + 3);\n i += 2;\n continue;\n }\n }\n\n if (code < 128) {\n result += cache[code];\n continue;\n }\n\n if (code >= 0xD800 && code <= 0xDFFF) {\n if (code >= 0xD800 && code <= 0xDBFF && i + 1 < l) {\n nextCode = string.charCodeAt(i + 1);\n if (nextCode >= 0xDC00 && nextCode <= 0xDFFF) {\n result += encodeURIComponent(string[i] + string[i + 1]);\n i++;\n continue;\n }\n }\n result += '%EF%BF%BD';\n continue;\n }\n\n result += encodeURIComponent(string[i]);\n }\n\n return result;\n}\n\nencode.defaultChars = \";/?:@&=+$,-_.!~*'()#\";\nencode.componentChars = \"-_.!~*'()\";\n\n\nmodule.exports = encode;\n","/**\n * @typedef {import('mdast').LinkReference} LinkReference\n * @typedef {import('mdast').ImageReference} ImageReference\n * @typedef {import('./index.js').Handler} Handler\n * @typedef {import('./index.js').Content} Content\n */\n\nimport {u} from 'unist-builder'\nimport {all} from './traverse.js'\n\n/**\n * Return the content of a reference without definition as plain text.\n *\n * @type {Handler}\n * @param {ImageReference|LinkReference} node\n * @returns {Content|Array.}\n */\nexport function revert(h, node) {\n const subtype = node.referenceType\n let suffix = ']'\n\n if (subtype === 'collapsed') {\n suffix += '[]'\n } else if (subtype === 'full') {\n suffix += '[' + (node.label || node.identifier) + ']'\n }\n\n if (node.type === 'imageReference') {\n return u('text', '![' + node.alt + suffix)\n }\n\n const contents = all(h, node)\n const head = contents[0]\n\n if (head && head.type === 'text') {\n head.value = '[' + head.value\n } else {\n contents.unshift(u('text', '['))\n }\n\n const tail = contents[contents.length - 1]\n\n if (tail && tail.type === 'text') {\n tail.value += suffix\n } else {\n contents.push(u('text', suffix))\n }\n\n return contents\n}\n","/**\n * @typedef {import('mdast').ImageReference} ImageReference\n * @typedef {import('hast').Properties} Properties\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport normalize from 'mdurl/encode.js'\nimport {revert} from '../revert.js'\n\n/**\n * @type {Handler}\n * @param {ImageReference} node\n */\nexport function imageReference(h, node) {\n const def = h.definition(node.identifier)\n\n if (!def) {\n return revert(h, node)\n }\n\n /** @type {Properties} */\n const props = {src: normalize(def.url || ''), alt: node.alt}\n\n if (def.title !== null && def.title !== undefined) {\n props.title = def.title\n }\n\n return h(node, 'img', props)\n}\n","/**\n * @typedef {import('mdast').Image} Image\n * @typedef {import('hast').Properties} Properties\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport normalize from 'mdurl/encode.js'\n\n/**\n * @type {Handler}\n * @param {Image} node\n */\nexport function image(h, node) {\n /** @type {Properties} */\n const props = {src: normalize(node.url), alt: node.alt}\n\n if (node.title !== null && node.title !== undefined) {\n props.title = node.title\n }\n\n return h(node, 'img', props)\n}\n","/**\n * @typedef {import('mdast').InlineCode} InlineCode\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {u} from 'unist-builder'\n\n/**\n * @type {Handler}\n * @param {InlineCode} node\n */\nexport function inlineCode(h, node) {\n return h(node, 'code', [u('text', node.value.replace(/\\r?\\n|\\r/g, ' '))])\n}\n","/**\n * @typedef {import('mdast').LinkReference} LinkReference\n * @typedef {import('hast').Properties} Properties\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport normalize from 'mdurl/encode.js'\nimport {revert} from '../revert.js'\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {LinkReference} node\n */\nexport function linkReference(h, node) {\n const def = h.definition(node.identifier)\n\n if (!def) {\n return revert(h, node)\n }\n\n /** @type {Properties} */\n const props = {href: normalize(def.url || '')}\n\n if (def.title !== null && def.title !== undefined) {\n props.title = def.title\n }\n\n return h(node, 'a', props, all(h, node))\n}\n","/**\n * @typedef {import('mdast').Link} Link\n * @typedef {import('hast').Properties} Properties\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport normalize from 'mdurl/encode.js'\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {Link} node\n */\nexport function link(h, node) {\n /** @type {Properties} */\n const props = {href: normalize(node.url)}\n\n if (node.title !== null && node.title !== undefined) {\n props.title = node.title\n }\n\n return h(node, 'a', props, all(h, node))\n}\n","/**\n * @typedef {import('mdast').ListItem} ListItem\n * @typedef {import('mdast').List} List\n * @typedef {import('hast').Properties} Properties\n * @typedef {import('hast').Element} Element\n * @typedef {import('../index.js').Handler} Handler\n * @typedef {import('../index.js').Content} Content\n */\n\nimport {u} from 'unist-builder'\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {ListItem} node\n * @param {List} parent\n */\nexport function listItem(h, node, parent) {\n const result = all(h, node)\n const loose = parent ? listLoose(parent) : listItemLoose(node)\n /** @type {Properties} */\n const props = {}\n /** @type {Array.} */\n const wrapped = []\n\n if (typeof node.checked === 'boolean') {\n /** @type {Element} */\n let paragraph\n\n if (\n result[0] &&\n result[0].type === 'element' &&\n result[0].tagName === 'p'\n ) {\n paragraph = result[0]\n } else {\n paragraph = h(null, 'p', [])\n result.unshift(paragraph)\n }\n\n if (paragraph.children.length > 0) {\n paragraph.children.unshift(u('text', ' '))\n }\n\n paragraph.children.unshift(\n h(null, 'input', {\n type: 'checkbox',\n checked: node.checked,\n disabled: true\n })\n )\n\n // According to github-markdown-css, this class hides bullet.\n // See: .\n props.className = ['task-list-item']\n }\n\n let index = -1\n\n while (++index < result.length) {\n const child = result[index]\n\n // Add eols before nodes, except if this is a loose, first paragraph.\n if (\n loose ||\n index !== 0 ||\n child.type !== 'element' ||\n child.tagName !== 'p'\n ) {\n wrapped.push(u('text', '\\n'))\n }\n\n if (child.type === 'element' && child.tagName === 'p' && !loose) {\n wrapped.push(...child.children)\n } else {\n wrapped.push(child)\n }\n }\n\n const tail = result[result.length - 1]\n\n // Add a final eol.\n if (tail && (loose || !('tagName' in tail) || tail.tagName !== 'p')) {\n wrapped.push(u('text', '\\n'))\n }\n\n return h(node, 'li', props, wrapped)\n}\n\n/**\n * @param {List} node\n * @return {Boolean}\n */\nfunction listLoose(node) {\n let loose = node.spread\n const children = node.children\n let index = -1\n\n while (!loose && ++index < children.length) {\n loose = listItemLoose(children[index])\n }\n\n return Boolean(loose)\n}\n\n/**\n * @param {ListItem} node\n * @return {Boolean}\n */\nfunction listItemLoose(node) {\n const spread = node.spread\n\n return spread === undefined || spread === null\n ? node.children.length > 1\n : spread\n}\n","/**\n * @typedef {import('mdast').Paragraph} Paragraph\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {Paragraph} node\n */\nexport function paragraph(h, node) {\n return h(node, 'p', all(h, node))\n}\n","/**\n * @typedef {import('mdast').Root} Root\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {u} from 'unist-builder'\nimport {all} from '../traverse.js'\nimport {wrap} from '../wrap.js'\n\n/**\n * @type {Handler}\n * @param {Root} node\n */\nexport function root(h, node) {\n // @ts-expect-error `root`s are also fine.\n return h.augment(node, u('root', wrap(all(h, node))))\n}\n","/**\n * @typedef {import('mdast').Strong} Strong\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {Strong} node\n */\nexport function strong(h, node) {\n return h(node, 'strong', all(h, node))\n}\n","/**\n * @typedef {import('mdast').Table} Table\n * @typedef {import('mdast').TableCell} TableCell\n * @typedef {import('hast').Element} Element\n * @typedef {import('../index.js').Handler} Handler\n * @typedef {import('../index.js').Content} Content\n */\n\nimport {pointStart, pointEnd} from 'unist-util-position'\nimport {wrap} from '../wrap.js'\nimport {all} from '../traverse.js'\n\n/**\n * @type {Handler}\n * @param {Table} node\n */\nexport function table(h, node) {\n const rows = node.children\n let index = -1\n const align = node.align || []\n /** @type {Array.} */\n const result = []\n\n while (++index < rows.length) {\n const row = rows[index].children\n const name = index === 0 ? 'th' : 'td'\n let pos = node.align ? align.length : row.length\n /** @type {Array.} */\n const out = []\n\n while (pos--) {\n const cell = row[pos]\n out[pos] = h(cell, name, {align: align[pos]}, cell ? all(h, cell) : [])\n }\n\n result[index] = h(rows[index], 'tr', wrap(out, true))\n }\n\n return h(\n node,\n 'table',\n wrap(\n [h(result[0].position, 'thead', wrap([result[0]], true))].concat(\n result[1]\n ? h(\n {\n start: pointStart(result[1]),\n end: pointEnd(result[result.length - 1])\n },\n 'tbody',\n wrap(result.slice(1), true)\n )\n : []\n ),\n true\n )\n )\n}\n","/**\n * @typedef {import('mdast').Text} Text\n * @typedef {import('../index.js').Handler} Handler\n */\n\nimport {u} from 'unist-builder'\n\n/**\n * @type {Handler}\n * @param {Text} node\n */\nexport function text(h, node) {\n return h.augment(\n node,\n u('text', String(node.value).replace(/[ \\t]*(\\r?\\n|\\r)[ \\t]*/g, '$1'))\n )\n}\n","import {blockquote} from './blockquote.js'\nimport {hardBreak} from './break.js'\nimport {code} from './code.js'\nimport {strikethrough} from './delete.js'\nimport {emphasis} from './emphasis.js'\nimport {footnoteReference} from './footnote-reference.js'\nimport {footnote} from './footnote.js'\nimport {heading} from './heading.js'\nimport {html} from './html.js'\nimport {imageReference} from './image-reference.js'\nimport {image} from './image.js'\nimport {inlineCode} from './inline-code.js'\nimport {linkReference} from './link-reference.js'\nimport {link} from './link.js'\nimport {listItem} from './list-item.js'\nimport {list} from './list.js'\nimport {paragraph} from './paragraph.js'\nimport {root} from './root.js'\nimport {strong} from './strong.js'\nimport {table} from './table.js'\nimport {text} from './text.js'\nimport {thematicBreak} from './thematic-break.js'\n\nexport const handlers = {\n blockquote,\n break: hardBreak,\n code,\n delete: strikethrough,\n emphasis,\n footnoteReference,\n footnote,\n heading,\n html,\n imageReference,\n image,\n inlineCode,\n linkReference,\n link,\n listItem,\n list,\n paragraph,\n root,\n strong,\n table,\n text,\n thematicBreak,\n toml: ignore,\n yaml: ignore,\n definition: ignore,\n footnoteDefinition: ignore\n}\n\n// Return nothing for nodes that are ignored.\nfunction ignore() {\n return null\n}\n","/**\n * @typedef {import('mdast').Root|import('mdast').Parent['children'][number]} MdastNode\n * @typedef {import('hast').Root|import('hast').Parent['children'][number]} HastNode\n * @typedef {import('mdast').Parent} Parent\n * @typedef {import('mdast').Definition} Definition\n * @typedef {import('mdast').FootnoteDefinition} FootnoteDefinition\n * @typedef {import('hast').Properties} Properties\n * @typedef {import('hast').Text} Text\n * @typedef {import('hast').Comment} Comment\n * @typedef {import('hast').Element} Element\n * @typedef {import('hast').Root} Root\n * @typedef {import('hast').ElementContent} Content\n * @typedef {import('unist-util-position').PositionLike} PositionLike\n *\n * @typedef EmbeddedHastFields\n * @property {string} [hName] Defines the tag name of an element\n * @property {Properties} [hProperties] Defines the properties of an element\n * @property {Array.} [hChildren] Defines the (hast) children of an element\n *\n * @typedef {Object. & EmbeddedHastFields} Data unist data with embedded hast fields\n *\n * @typedef {MdastNode & {data?: Data}} NodeWithData unist node with embedded hast data\n *\n * @callback Handler\n * @param {H} h Handle context\n * @param {any} node mdast node to handle\n * @param {Parent|null} parent Parent of `node`\n * @returns {Content|Array.|null|undefined} hast node\n *\n * @callback HFunctionProps\n * @param {MdastNode|PositionLike|null|undefined} node mdast node or unist position\n * @param {string} tagName HTML tag name\n * @param {Properties} props Properties\n * @param {Array.?} [children] hast content\n * @returns {Element}\n *\n * @callback HFunctionNoProps\n * @param {MdastNode|PositionLike|null|undefined} node mdast node or unist position\n * @param {string} tagName HTML tag name\n * @param {Array.?} [children] hast content\n * @returns {Element}\n *\n * @typedef HFields\n * @property {boolean} dangerous Whether HTML is allowed\n * @property {(identifier: string) => Definition|null} definition Definition cache\n * @property {Object.} footnoteById Footnote cache\n * @property {Array.} footnoteOrder Order in which footnotes occur\n * @property {Handlers} handlers Applied handlers\n * @property {Handler} unknownHandler Handler for any none not in `passThrough` or otherwise handled\n * @property {(left: NodeWithData|PositionLike|null|undefined, right: Content) => Content} augment Like `h` but lower-level and usable on non-elements.\n * @property {Array.} passThrough List of node types to pass through untouched (except for their children).\n *\n * @typedef Options\n * @property {boolean} [allowDangerousHtml=false] Whether to allow `html` nodes and inject them as `raw` HTML\n * @property {Handlers} [handlers] Object mapping mdast nodes to functions handling them\n * @property {Array.} [passThrough] List of custom mdast node types to pass through (keep) in hast\n * @property {Handler} [unknownHandler] Handler for all unknown nodes.\n *\n * @typedef {Record.} Handlers Map of node types to handlers\n * @typedef {HFunctionProps & HFunctionNoProps & HFields} H Handle context\n */\n\nimport {u} from 'unist-builder'\nimport {visit} from 'unist-util-visit'\nimport {pointStart, pointEnd} from 'unist-util-position'\nimport {generated} from 'unist-util-generated'\nimport {definitions} from 'mdast-util-definitions'\nimport {one} from './traverse.js'\nimport {footer} from './footer.js'\nimport {handlers} from './handlers/index.js'\n\nconst own = {}.hasOwnProperty\n\n/**\n * Factory to transform.\n * @param {MdastNode} tree mdast node\n * @param {Options} [options] Configuration\n * @returns {H} `h` function\n */\nfunction factory(tree, options) {\n const settings = options || {}\n const dangerous = settings.allowDangerousHtml || false\n /** @type {Object.} */\n const footnoteById = {}\n\n h.dangerous = dangerous\n h.definition = definitions(tree)\n h.footnoteById = footnoteById\n /** @type {Array.} */\n h.footnoteOrder = []\n h.augment = augment\n h.handlers = {...handlers, ...settings.handlers}\n h.unknownHandler = settings.unknownHandler\n h.passThrough = settings.passThrough\n\n visit(tree, 'footnoteDefinition', (definition) => {\n const id = String(definition.identifier).toUpperCase()\n\n // Mimick CM behavior of link definitions.\n // See: .\n if (!own.call(footnoteById, id)) {\n footnoteById[id] = definition\n }\n })\n\n // @ts-expect-error Hush, it’s fine!\n return h\n\n /**\n * Finalise the created `right`, a hast node, from `left`, an mdast node.\n * @param {(NodeWithData|PositionLike)?} left\n * @param {Content} right\n * @returns {Content}\n */\n function augment(left, right) {\n // Handle `data.hName`, `data.hProperties, `data.hChildren`.\n if (left && 'data' in left && left.data) {\n /** @type {Data} */\n const data = left.data\n\n if (data.hName) {\n if (right.type !== 'element') {\n right = {\n type: 'element',\n tagName: '',\n properties: {},\n children: []\n }\n }\n\n right.tagName = data.hName\n }\n\n if (right.type === 'element' && data.hProperties) {\n right.properties = {...right.properties, ...data.hProperties}\n }\n\n if ('children' in right && right.children && data.hChildren) {\n right.children = data.hChildren\n }\n }\n\n if (left) {\n const ctx = 'type' in left ? left : {position: left}\n\n if (!generated(ctx)) {\n right.position = {start: pointStart(ctx), end: pointEnd(ctx)}\n }\n }\n\n return right\n }\n\n /**\n * Create an element for `node`.\n *\n * @type {HFunctionProps}\n */\n function h(node, tagName, props, children) {\n if (Array.isArray(props)) {\n children = props\n props = {}\n }\n\n // @ts-expect-error augmenting an element yields an element.\n return augment(node, {\n type: 'element',\n tagName,\n properties: props || {},\n children: children || []\n })\n }\n}\n\n/**\n * Transform `tree` (an mdast node) to a hast node.\n *\n * @param {MdastNode} tree mdast node\n * @param {Options} [options] Configuration\n * @returns {HastNode|null|undefined} hast node\n */\nexport function toHast(tree, options) {\n const h = factory(tree, options)\n const node = one(h, tree, null)\n const foot = footer(h)\n\n if (foot) {\n // @ts-expect-error If there’s a footer, there were definitions, meaning block\n // content.\n // So assume `node` is a parent node.\n node.children.push(u('text', '\\n'), foot)\n }\n\n return Array.isArray(node) ? {type: 'root', children: node} : node\n}\n","/**\n * @typedef {import('unist').Node} Node\n * @typedef {import('hast').Root} HastRoot\n * @typedef {import('mdast').Root} MdastRoot\n * @typedef {import('mdast-util-to-hast').Options} Options\n * @typedef {import('unified').Processor} Processor\n *\n * @typedef {import('mdast-util-to-hast')} DoNotTouchAsThisImportIncludesRawInTree\n */\n\nimport {toHast} from 'mdast-util-to-hast'\n\n// Note: the `` overload doesn’t seem to work :'(\n\n/**\n * Plugin to bridge or mutate to rehype.\n *\n * If a destination is given, runs the destination with the new hast tree\n * (bridge-mode).\n * Without destination, returns the hast tree: further plugins run on that tree\n * (mutate-mode).\n *\n * @param destination\n * Optional unified processor.\n * @param options\n * Options passed to `mdast-util-to-hast`.\n */\nconst remarkRehype =\n /** @type {(import('unified').Plugin<[Processor, Options?]|[Options]|[], MdastRoot>)} */\n (\n function (destination, options) {\n return destination && 'run' in destination\n ? bridge(destination, options)\n : mutate(destination)\n }\n )\n\nexport default remarkRehype\n\n/**\n * Bridge-mode.\n * Runs the destination with the new hast tree.\n *\n * @type {import('unified').Plugin<[Processor, Options?], MdastRoot>}\n */\nfunction bridge(destination, options) {\n return (node, file, next) => {\n destination.run(toHast(node, options), file, (error) => {\n next(error)\n })\n }\n}\n\n/**\n * Mutate-mode.\n * Further transformers run on the nlcst tree.\n *\n * @type {import('unified').Plugin<[Options?]|void[], MdastRoot, HastRoot>}\n */\nfunction mutate(options) {\n // @ts-expect-error: assume a corresponding node is returned for `toHast`.\n return (node) => toHast(node, options)\n}\n","/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nvar ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';\n\nmodule.exports = ReactPropTypesSecret;\n","/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n'use strict';\n\nvar ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');\n\nfunction emptyFunction() {}\nfunction emptyFunctionWithReset() {}\nemptyFunctionWithReset.resetWarningCache = emptyFunction;\n\nmodule.exports = function() {\n function shim(props, propName, componentName, location, propFullName, secret) {\n if (secret === ReactPropTypesSecret) {\n // It is still safe when called from React.\n return;\n }\n var err = new Error(\n 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +\n 'Use PropTypes.checkPropTypes() to call them. ' +\n 'Read more at http://fb.me/use-check-prop-types'\n );\n err.name = 'Invariant Violation';\n throw err;\n };\n shim.isRequired = shim;\n function getShim() {\n return shim;\n };\n // Important!\n // Keep this list in sync with production version in `./factoryWithTypeCheckers.js`.\n var ReactPropTypes = {\n array: shim,\n bigint: shim,\n bool: shim,\n func: shim,\n number: shim,\n object: shim,\n string: shim,\n symbol: shim,\n\n any: shim,\n arrayOf: getShim,\n element: shim,\n elementType: shim,\n instanceOf: getShim,\n node: shim,\n objectOf: getShim,\n oneOf: getShim,\n oneOfType: getShim,\n shape: getShim,\n exact: getShim,\n\n checkPropTypes: emptyFunctionWithReset,\n resetWarningCache: emptyFunction\n };\n\n ReactPropTypes.PropTypes = ReactPropTypes;\n\n return ReactPropTypes;\n};\n","/**\n * Copyright (c) 2013-present, Facebook, Inc.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nif (process.env.NODE_ENV !== 'production') {\n var ReactIs = require('react-is');\n\n // By explicitly using `prop-types` you are opting into new development behavior.\n // http://fb.me/prop-types-in-prod\n var throwOnDirectAccess = true;\n module.exports = require('./factoryWithTypeCheckers')(ReactIs.isElement, throwOnDirectAccess);\n} else {\n // By explicitly using `prop-types` you are opting into new production behavior.\n // http://fb.me/prop-types-in-prod\n module.exports = require('./factoryWithThrowingShims')();\n}\n","/**\n * @typedef {import('./info.js').Info} Info\n * @typedef {Record} Properties\n * @typedef {Record} Normal\n */\n\nexport class Schema {\n /**\n * @constructor\n * @param {Properties} property\n * @param {Normal} normal\n * @param {string} [space]\n */\n constructor(property, normal, space) {\n this.property = property\n this.normal = normal\n if (space) {\n this.space = space\n }\n }\n}\n\n/** @type {Properties} */\nSchema.prototype.property = {}\n/** @type {Normal} */\nSchema.prototype.normal = {}\n/** @type {string|null} */\nSchema.prototype.space = null\n","/**\n * @typedef {import('./schema.js').Properties} Properties\n * @typedef {import('./schema.js').Normal} Normal\n */\n\nimport {Schema} from './schema.js'\n\n/**\n * @param {Schema[]} definitions\n * @param {string} [space]\n * @returns {Schema}\n */\nexport function merge(definitions, space) {\n /** @type {Properties} */\n const property = {}\n /** @type {Normal} */\n const normal = {}\n let index = -1\n\n while (++index < definitions.length) {\n Object.assign(property, definitions[index].property)\n Object.assign(normal, definitions[index].normal)\n }\n\n return new Schema(property, normal, space)\n}\n","/**\n * @param {string} value\n * @returns {string}\n */\nexport function normalize(value) {\n return value.toLowerCase()\n}\n","export class Info {\n /**\n * @constructor\n * @param {string} property\n * @param {string} attribute\n */\n constructor(property, attribute) {\n /** @type {string} */\n this.property = property\n /** @type {string} */\n this.attribute = attribute\n }\n}\n\n/** @type {string|null} */\nInfo.prototype.space = null\nInfo.prototype.boolean = false\nInfo.prototype.booleanish = false\nInfo.prototype.overloadedBoolean = false\nInfo.prototype.number = false\nInfo.prototype.commaSeparated = false\nInfo.prototype.spaceSeparated = false\nInfo.prototype.commaOrSpaceSeparated = false\nInfo.prototype.mustUseProperty = false\nInfo.prototype.defined = false\n","let powers = 0\n\nexport const boolean = increment()\nexport const booleanish = increment()\nexport const overloadedBoolean = increment()\nexport const number = increment()\nexport const spaceSeparated = increment()\nexport const commaSeparated = increment()\nexport const commaOrSpaceSeparated = increment()\n\nfunction increment() {\n return 2 ** ++powers\n}\n","import {Info} from './info.js'\nimport * as types from './types.js'\n\n/** @type {Array} */\n// @ts-expect-error: hush.\nconst checks = Object.keys(types)\n\nexport class DefinedInfo extends Info {\n /**\n * @constructor\n * @param {string} property\n * @param {string} attribute\n * @param {number|null} [mask]\n * @param {string} [space]\n */\n constructor(property, attribute, mask, space) {\n let index = -1\n\n super(property, attribute)\n\n mark(this, 'space', space)\n\n if (typeof mask === 'number') {\n while (++index < checks.length) {\n const check = checks[index]\n mark(this, checks[index], (mask & types[check]) === types[check])\n }\n }\n }\n}\n\nDefinedInfo.prototype.defined = true\n\n/**\n * @param {DefinedInfo} values\n * @param {string} key\n * @param {unknown} value\n */\nfunction mark(values, key, value) {\n if (value) {\n // @ts-expect-error: assume `value` matches the expected value of `key`.\n values[key] = value\n }\n}\n","/**\n * @typedef {import('./schema.js').Properties} Properties\n * @typedef {import('./schema.js').Normal} Normal\n *\n * @typedef {Record} Attributes\n *\n * @typedef {Object} Definition\n * @property {Record} properties\n * @property {(attributes: Attributes, property: string) => string} transform\n * @property {string} [space]\n * @property {Attributes} [attributes]\n * @property {Array} [mustUseProperty]\n */\n\nimport {normalize} from '../normalize.js'\nimport {Schema} from './schema.js'\nimport {DefinedInfo} from './defined-info.js'\n\nconst own = {}.hasOwnProperty\n\n/**\n * @param {Definition} definition\n * @returns {Schema}\n */\nexport function create(definition) {\n /** @type {Properties} */\n const property = {}\n /** @type {Normal} */\n const normal = {}\n /** @type {string} */\n let prop\n\n for (prop in definition.properties) {\n if (own.call(definition.properties, prop)) {\n const value = definition.properties[prop]\n const info = new DefinedInfo(\n prop,\n definition.transform(definition.attributes || {}, prop),\n value,\n definition.space\n )\n\n if (\n definition.mustUseProperty &&\n definition.mustUseProperty.includes(prop)\n ) {\n info.mustUseProperty = true\n }\n\n property[prop] = info\n\n normal[normalize(prop)] = prop\n normal[normalize(info.attribute)] = prop\n }\n }\n\n return new Schema(property, normal, definition.space)\n}\n","import {create} from './util/create.js'\n\nexport const xlink = create({\n space: 'xlink',\n transform(_, prop) {\n return 'xlink:' + prop.slice(5).toLowerCase()\n },\n properties: {\n xLinkActuate: null,\n xLinkArcRole: null,\n xLinkHref: null,\n xLinkRole: null,\n xLinkShow: null,\n xLinkTitle: null,\n xLinkType: null\n }\n})\n","import {create} from './util/create.js'\n\nexport const xml = create({\n space: 'xml',\n transform(_, prop) {\n return 'xml:' + prop.slice(3).toLowerCase()\n },\n properties: {xmlLang: null, xmlBase: null, xmlSpace: null}\n})\n","/**\n * @param {Record} attributes\n * @param {string} attribute\n * @returns {string}\n */\nexport function caseSensitiveTransform(attributes, attribute) {\n return attribute in attributes ? attributes[attribute] : attribute\n}\n","import {caseSensitiveTransform} from './case-sensitive-transform.js'\n\n/**\n * @param {Record} attributes\n * @param {string} property\n * @returns {string}\n */\nexport function caseInsensitiveTransform(attributes, property) {\n return caseSensitiveTransform(attributes, property.toLowerCase())\n}\n","import {create} from './util/create.js'\nimport {caseInsensitiveTransform} from './util/case-insensitive-transform.js'\n\nexport const xmlns = create({\n space: 'xmlns',\n attributes: {xmlnsxlink: 'xmlns:xlink'},\n transform: caseInsensitiveTransform,\n properties: {xmlns: null, xmlnsXLink: null}\n})\n","import {booleanish, number, spaceSeparated} from './util/types.js'\nimport {create} from './util/create.js'\n\nexport const aria = create({\n transform(_, prop) {\n return prop === 'role' ? prop : 'aria-' + prop.slice(4).toLowerCase()\n },\n properties: {\n ariaActiveDescendant: null,\n ariaAtomic: booleanish,\n ariaAutoComplete: null,\n ariaBusy: booleanish,\n ariaChecked: booleanish,\n ariaColCount: number,\n ariaColIndex: number,\n ariaColSpan: number,\n ariaControls: spaceSeparated,\n ariaCurrent: null,\n ariaDescribedBy: spaceSeparated,\n ariaDetails: null,\n ariaDisabled: booleanish,\n ariaDropEffect: spaceSeparated,\n ariaErrorMessage: null,\n ariaExpanded: booleanish,\n ariaFlowTo: spaceSeparated,\n ariaGrabbed: booleanish,\n ariaHasPopup: null,\n ariaHidden: booleanish,\n ariaInvalid: null,\n ariaKeyShortcuts: null,\n ariaLabel: null,\n ariaLabelledBy: spaceSeparated,\n ariaLevel: number,\n ariaLive: null,\n ariaModal: booleanish,\n ariaMultiLine: booleanish,\n ariaMultiSelectable: booleanish,\n ariaOrientation: null,\n ariaOwns: spaceSeparated,\n ariaPlaceholder: null,\n ariaPosInSet: number,\n ariaPressed: booleanish,\n ariaReadOnly: booleanish,\n ariaRelevant: null,\n ariaRequired: booleanish,\n ariaRoleDescription: spaceSeparated,\n ariaRowCount: number,\n ariaRowIndex: number,\n ariaRowSpan: number,\n ariaSelected: booleanish,\n ariaSetSize: number,\n ariaSort: null,\n ariaValueMax: number,\n ariaValueMin: number,\n ariaValueNow: number,\n ariaValueText: null,\n role: null\n }\n})\n","import {\n boolean,\n overloadedBoolean,\n booleanish,\n number,\n spaceSeparated,\n commaSeparated\n} from './util/types.js'\nimport {create} from './util/create.js'\nimport {caseInsensitiveTransform} from './util/case-insensitive-transform.js'\n\nexport const html = create({\n space: 'html',\n attributes: {\n acceptcharset: 'accept-charset',\n classname: 'class',\n htmlfor: 'for',\n httpequiv: 'http-equiv'\n },\n transform: caseInsensitiveTransform,\n mustUseProperty: ['checked', 'multiple', 'muted', 'selected'],\n properties: {\n // Standard Properties.\n abbr: null,\n accept: commaSeparated,\n acceptCharset: spaceSeparated,\n accessKey: spaceSeparated,\n action: null,\n allow: null,\n allowFullScreen: boolean,\n allowPaymentRequest: boolean,\n allowUserMedia: boolean,\n alt: null,\n as: null,\n async: boolean,\n autoCapitalize: null,\n autoComplete: spaceSeparated,\n autoFocus: boolean,\n autoPlay: boolean,\n capture: boolean,\n charSet: null,\n checked: boolean,\n cite: null,\n className: spaceSeparated,\n cols: number,\n colSpan: null,\n content: null,\n contentEditable: booleanish,\n controls: boolean,\n controlsList: spaceSeparated,\n coords: number | commaSeparated,\n crossOrigin: null,\n data: null,\n dateTime: null,\n decoding: null,\n default: boolean,\n defer: boolean,\n dir: null,\n dirName: null,\n disabled: boolean,\n download: overloadedBoolean,\n draggable: booleanish,\n encType: null,\n enterKeyHint: null,\n form: null,\n formAction: null,\n formEncType: null,\n formMethod: null,\n formNoValidate: boolean,\n formTarget: null,\n headers: spaceSeparated,\n height: number,\n hidden: boolean,\n high: number,\n href: null,\n hrefLang: null,\n htmlFor: spaceSeparated,\n httpEquiv: spaceSeparated,\n id: null,\n imageSizes: null,\n imageSrcSet: null,\n inputMode: null,\n integrity: null,\n is: null,\n isMap: boolean,\n itemId: null,\n itemProp: spaceSeparated,\n itemRef: spaceSeparated,\n itemScope: boolean,\n itemType: spaceSeparated,\n kind: null,\n label: null,\n lang: null,\n language: null,\n list: null,\n loading: null,\n loop: boolean,\n low: number,\n manifest: null,\n max: null,\n maxLength: number,\n media: null,\n method: null,\n min: null,\n minLength: number,\n multiple: boolean,\n muted: boolean,\n name: null,\n nonce: null,\n noModule: boolean,\n noValidate: boolean,\n onAbort: null,\n onAfterPrint: null,\n onAuxClick: null,\n onBeforeMatch: null,\n onBeforePrint: null,\n onBeforeUnload: null,\n onBlur: null,\n onCancel: null,\n onCanPlay: null,\n onCanPlayThrough: null,\n onChange: null,\n onClick: null,\n onClose: null,\n onContextLost: null,\n onContextMenu: null,\n onContextRestored: null,\n onCopy: null,\n onCueChange: null,\n onCut: null,\n onDblClick: null,\n onDrag: null,\n onDragEnd: null,\n onDragEnter: null,\n onDragExit: null,\n onDragLeave: null,\n onDragOver: null,\n onDragStart: null,\n onDrop: null,\n onDurationChange: null,\n onEmptied: null,\n onEnded: null,\n onError: null,\n onFocus: null,\n onFormData: null,\n onHashChange: null,\n onInput: null,\n onInvalid: null,\n onKeyDown: null,\n onKeyPress: null,\n onKeyUp: null,\n onLanguageChange: null,\n onLoad: null,\n onLoadedData: null,\n onLoadedMetadata: null,\n onLoadEnd: null,\n onLoadStart: null,\n onMessage: null,\n onMessageError: null,\n onMouseDown: null,\n onMouseEnter: null,\n onMouseLeave: null,\n onMouseMove: null,\n onMouseOut: null,\n onMouseOver: null,\n onMouseUp: null,\n onOffline: null,\n onOnline: null,\n onPageHide: null,\n onPageShow: null,\n onPaste: null,\n onPause: null,\n onPlay: null,\n onPlaying: null,\n onPopState: null,\n onProgress: null,\n onRateChange: null,\n onRejectionHandled: null,\n onReset: null,\n onResize: null,\n onScroll: null,\n onScrollEnd: null,\n onSecurityPolicyViolation: null,\n onSeeked: null,\n onSeeking: null,\n onSelect: null,\n onSlotChange: null,\n onStalled: null,\n onStorage: null,\n onSubmit: null,\n onSuspend: null,\n onTimeUpdate: null,\n onToggle: null,\n onUnhandledRejection: null,\n onUnload: null,\n onVolumeChange: null,\n onWaiting: null,\n onWheel: null,\n open: boolean,\n optimum: number,\n pattern: null,\n ping: spaceSeparated,\n placeholder: null,\n playsInline: boolean,\n poster: null,\n preload: null,\n readOnly: boolean,\n referrerPolicy: null,\n rel: spaceSeparated,\n required: boolean,\n reversed: boolean,\n rows: number,\n rowSpan: number,\n sandbox: spaceSeparated,\n scope: null,\n scoped: boolean,\n seamless: boolean,\n selected: boolean,\n shape: null,\n size: number,\n sizes: null,\n slot: null,\n span: number,\n spellCheck: booleanish,\n src: null,\n srcDoc: null,\n srcLang: null,\n srcSet: null,\n start: number,\n step: null,\n style: null,\n tabIndex: number,\n target: null,\n title: null,\n translate: null,\n type: null,\n typeMustMatch: boolean,\n useMap: null,\n value: booleanish,\n width: number,\n wrap: null,\n\n // Legacy.\n // See: https://html.spec.whatwg.org/#other-elements,-attributes-and-apis\n align: null, // Several. Use CSS `text-align` instead,\n aLink: null, // ``. Use CSS `a:active {color}` instead\n archive: spaceSeparated, // ``. List of URIs to archives\n axis: null, // `` and ``. Use `scope` on ``\n background: null, // ``. Use CSS `background-image` instead\n bgColor: null, // `` and table elements. Use CSS `background-color` instead\n border: number, // ``. Use CSS `border-width` instead,\n borderColor: null, // `
`. Use CSS `border-color` instead,\n bottomMargin: number, // ``\n cellPadding: null, // `
`\n cellSpacing: null, // `
`\n char: null, // Several table elements. When `align=char`, sets the character to align on\n charOff: null, // Several table elements. When `char`, offsets the alignment\n classId: null, // ``\n clear: null, // `
`. Use CSS `clear` instead\n code: null, // ``\n codeBase: null, // ``\n codeType: null, // ``\n color: null, // `` and `
`. Use CSS instead\n compact: boolean, // Lists. Use CSS to reduce space between items instead\n declare: boolean, // ``\n event: null, // ` + + + +
+ + + diff --git a/docs/LOCAL_DEPLOYMENT.md b/docs/LOCAL_DEPLOYMENT.md index 1a8d653bc..8dd019580 100644 --- a/docs/LOCAL_DEPLOYMENT.md +++ b/docs/LOCAL_DEPLOYMENT.md @@ -91,14 +91,13 @@ This step is included if you cannot use the Launch configuration in VSCode. Open ```shell cd code python -m pip install -r requirements.txt -cd app python -m flask --app ./app.py --debug run ``` #### Starting the Typescript React app in dev mode (optional) This step is included if you cannot use the Launch configuration in VSCode. Open a new separate terminal and enter the following commands: ```shell -cd code\app\frontend +cd code\frontend npm install npm run dev ``` @@ -138,7 +137,7 @@ If you want to develop and run the batch processing functions container locally, First, install [Azure Functions Core Tools](https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=windows%2Cportal%2Cv2%2Cbash&pivots=programming-language-python). ```shell -cd code\batch +cd code\backend\batch func start ```