From a978248d8f132fde3ce1ba3a91c87cea3cdac12d Mon Sep 17 00:00:00 2001 From: Davide Mauri Date: Tue, 12 Mar 2024 13:38:36 -0700 Subject: [PATCH 1/4] first commit --- .env.sample | 3 + .gitattributes | 13 + .gitignore | 6 + .nvmrc | 1 + .vscode/extensions.json | 6 + .vscode/launch.json | 25 + .vscode/settings.json | 7 + .vscode/tasks.json | 89 + README.md | 71 +- azure.yaml | 15 + client/.eslintrc.cjs | 20 + client/.gitignore | 24 + client/index.html | 17 + client/package-lock.json | 7054 +++++++++++++++++ client/package.json | 38 + client/src/Main.tsx | 43 + client/src/api/chat.ts | 57 + client/src/api/sessions.ts | 72 + client/src/components/FancyText.tsx | 36 + client/src/components/Header.tsx | 10 + client/src/components/Navigation.tsx | 33 + client/src/components/NoSessions.tsx | 9 + client/src/components/PrimaryButton.tsx | 21 + client/src/components/Session.tsx | 45 + client/src/components/SessionsList.tsx | 12 + client/src/models.ts | 14 + client/src/pages/About.tsx | 56 + client/src/pages/Chat.tsx | 137 + client/src/pages/Root.tsx | 35 + client/src/pages/Search.tsx | 102 + client/src/site.ts | 7 + client/src/user.ts | 8 + client/staticwebapp.config.json | 5 + client/tsconfig.json | 25 + client/tsconfig.node.json | 10 + client/vite.config.ts | 7 + .../Script.PostDeployment.sql | 7 + .../Script.PreDeployment.sql | 15 + .../SessionRecommender/Security/db_owner.sql | 1 + .../Security/session_recommender_app.sql | 2 + database/SessionRecommender/Security/web.sql | 4 + .../Sequences/global_id.sql | 6 + .../StoredProcedures/find_sessions.sql | 146 + .../StoredProcedures/get_sessions_count.sql | 5 + .../upsert_session_embeddings.sql | 23 + .../upsert_speaker_embeddings.sql | 23 + .../Tables/searched_text.sql | 11 + .../SessionRecommender/Tables/sessions.sql | 20 + .../Tables/sessions_embeddings.sql | 12 + .../Tables/sessions_speakers.sql | 14 + .../SessionRecommender/Tables/speakers.sql | 12 + .../Tables/speakers_embeddings.sql | 12 + .../Views/last_searches.sql | 15 + .../SessionRecommender/Views/search_stats.sql | 9 + .../session_recommender_v2.sqlproj | 50 + database/master.dacpac | Bin 0 -> 85666 bytes database/session_recommender_v2.dacpac | Bin 0 -> 7267 bytes database/setup-database.ps1 | 26 + func/.gitignore | 264 + func/.vscode/extensions.json | 6 + func/.vscode/launch.json | 11 + func/.vscode/settings.json | 7 + func/.vscode/tasks.json | 69 + func/ChatHandler.cs | 123 + func/Program.cs | 45 + func/RequestHandler.csproj | 29 + func/SessionProcessor.cs | 145 + func/host.json | 12 + func/local.settings copy.json | 12 + func/local.settings.json.sample | 12 + infra/abbreviations.json | 136 + infra/app/functions.bicep | 49 + infra/app/openai.bicep | 57 + infra/app/sqlserver.bicep | 114 + infra/app/staticwebapp.bicep | 39 + infra/core/host/appservice-appsettings.bicep | 17 + infra/core/host/appservice.bicep | 124 + infra/core/host/appserviceplan.bicep | 22 + infra/core/host/functions.bicep | 87 + .../applicationinsights-dashboard.bicep | 1236 +++ infra/core/monitor/applicationinsights.bicep | 30 + infra/core/monitor/loganalytics.bicep | 22 + infra/core/security/keyvault-access.bicep | 22 + infra/core/security/keyvault.bicep | 26 + infra/core/security/role.bicep | 21 + infra/core/storage/storage-account.bicep | 64 + infra/main.bicep | 224 + infra/main.parameters.json | 24 + swa-cli.config.json | 11 + .../staticwebapp.database.config.json | 131 + 90 files changed, 11692 insertions(+), 45 deletions(-) create mode 100644 .env.sample create mode 100644 .gitattributes create mode 100644 .nvmrc create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 azure.yaml create mode 100644 client/.eslintrc.cjs create mode 100644 client/.gitignore create mode 100644 client/index.html create mode 100644 client/package-lock.json create mode 100644 client/package.json create mode 100644 client/src/Main.tsx create mode 100644 client/src/api/chat.ts create mode 100644 client/src/api/sessions.ts create mode 100644 client/src/components/FancyText.tsx create mode 100644 client/src/components/Header.tsx create mode 100644 client/src/components/Navigation.tsx create mode 100644 client/src/components/NoSessions.tsx create mode 100644 client/src/components/PrimaryButton.tsx create mode 100644 client/src/components/Session.tsx create mode 100644 client/src/components/SessionsList.tsx create mode 100644 client/src/models.ts create mode 100644 client/src/pages/About.tsx create mode 100644 client/src/pages/Chat.tsx create mode 100644 client/src/pages/Root.tsx create mode 100644 client/src/pages/Search.tsx create mode 100644 client/src/site.ts create mode 100644 client/src/user.ts create mode 100644 client/staticwebapp.config.json create mode 100644 client/tsconfig.json create mode 100644 client/tsconfig.node.json create mode 100644 client/vite.config.ts create mode 100644 database/SessionRecommender/Script.PostDeployment.sql create mode 100644 database/SessionRecommender/Script.PreDeployment.sql create mode 100644 database/SessionRecommender/Security/db_owner.sql create mode 100644 database/SessionRecommender/Security/session_recommender_app.sql create mode 100644 database/SessionRecommender/Security/web.sql create mode 100644 database/SessionRecommender/Sequences/global_id.sql create mode 100644 database/SessionRecommender/StoredProcedures/find_sessions.sql create mode 100644 database/SessionRecommender/StoredProcedures/get_sessions_count.sql create mode 100644 database/SessionRecommender/StoredProcedures/upsert_session_embeddings.sql create mode 100644 database/SessionRecommender/StoredProcedures/upsert_speaker_embeddings.sql create mode 100644 database/SessionRecommender/Tables/searched_text.sql create mode 100644 database/SessionRecommender/Tables/sessions.sql create mode 100644 database/SessionRecommender/Tables/sessions_embeddings.sql create mode 100644 database/SessionRecommender/Tables/sessions_speakers.sql create mode 100644 database/SessionRecommender/Tables/speakers.sql create mode 100644 database/SessionRecommender/Tables/speakers_embeddings.sql create mode 100644 database/SessionRecommender/Views/last_searches.sql create mode 100644 database/SessionRecommender/Views/search_stats.sql create mode 100644 database/SessionRecommender/session_recommender_v2.sqlproj create mode 100644 database/master.dacpac create mode 100644 database/session_recommender_v2.dacpac create mode 100644 database/setup-database.ps1 create mode 100644 func/.gitignore create mode 100644 func/.vscode/extensions.json create mode 100644 func/.vscode/launch.json create mode 100644 func/.vscode/settings.json create mode 100644 func/.vscode/tasks.json create mode 100644 func/ChatHandler.cs create mode 100644 func/Program.cs create mode 100644 func/RequestHandler.csproj create mode 100644 func/SessionProcessor.cs create mode 100644 func/host.json create mode 100644 func/local.settings copy.json create mode 100644 func/local.settings.json.sample create mode 100644 infra/abbreviations.json create mode 100644 infra/app/functions.bicep create mode 100644 infra/app/openai.bicep create mode 100644 infra/app/sqlserver.bicep create mode 100644 infra/app/staticwebapp.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/security/keyvault-access.bicep create mode 100644 infra/core/security/keyvault.bicep create mode 100644 infra/core/security/role.bicep create mode 100644 infra/core/storage/storage-account.bicep create mode 100644 infra/main.bicep create mode 100644 infra/main.parameters.json create mode 100644 swa-cli.config.json create mode 100644 swa-db-connections/staticwebapp.database.config.json diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..209ce0b --- /dev/null +++ b/.env.sample @@ -0,0 +1,3 @@ +MSSQL='Server=.database.windows.net;Initial Catalog=;Persist Security Info=False;User ID=session_recommender_app;Password=unEno!h5!&*KP420xds&@P901afb$^M;MultipleActiveResultSets=False;Encrypt=True;Connection Timeout=30;' +OPENAI.URL='https://.openai.azure.com' +OPENAI.KEY='' diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..8efbe82 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,13 @@ +# Thanks to: https://rehansaeed.com/gitattributes-best-practices/ + +# Set default behavior to automatically normalize line endings. +* text=auto + +# Force batch scripts to always use CRLF line endings so that if a repo is accessed +# in Windows via a file share from Linux, the scripts will work. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use LF line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.sh text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8a30d25..e21ec49 100644 --- a/.gitignore +++ b/.gitignore @@ -396,3 +396,9 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml + +# Custom +.env +azuredeploy.parameters.json +*.zip +.azure/ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2edeafb --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..bb76300 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-azuretools.vscode-azurefunctions", + "ms-dotnettools.csharp" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a778a15 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to .NET Functions", + "type": "coreclr", + "request": "attach", + "processId": "${command:azureFunctions.pickProcess}" + }, + { + "name": "Run web app", + "type": "node", + "request": "launch", + "cwd": "${workspaceFolder}", + "runtimeExecutable": "swa", + "runtimeArgs": ["start"], + "presentation": { + "hidden": false, + "group": "Frontend", + "order": 1 + }, + "preLaunchTask": "npm: install" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4ff618a --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "azureFunctions.deploySubpath": "api/bin/Release/net8.0/publish", + "azureFunctions.projectLanguage": "C#", + "azureFunctions.projectRuntime": "~4", + "debug.internalConsoleOptions": "neverOpen", + "azureFunctions.preDeployTask": "publish (functions)" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..370f7e1 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,89 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "clean (functions)", + "command": "dotnet", + "args": [ + "clean", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/api" + } + }, + { + "label": "build (functions)", + "command": "dotnet", + "args": [ + "build", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean (functions)", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/api" + } + }, + { + "label": "clean release (functions)", + "command": "dotnet", + "args": [ + "clean", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/api" + } + }, + { + "label": "publish (functions)", + "command": "dotnet", + "args": [ + "publish", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean release (functions)", + "problemMatcher": "$msCompile", + "options": { + "cwd": "${workspaceFolder}/api" + } + }, + { + "type": "func", + "dependsOn": "build (functions)", + "options": { + "cwd": "${workspaceFolder}/api/bin/Debug/net8.0" + }, + "command": "host start", + "isBackground": true, + "problemMatcher": "$func-dotnet-watch" + }, + { + "type": "npm", + "options": { + "cwd": "${workspaceFolder}/client" + }, + "script": "install", + "label": "npm: install" + } + ] +} diff --git a/README.md b/README.md index 364f052..ab32415 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,38 @@ -# Project Name +Coming Soon! -(short, 1-3 sentenced, description of the project) +In the meantime take a look at v1 of this project: -## Features +- https://sessionfinder.dotnetconf.net/ +- https://github.com/Azure-Samples/azure-sql-db-session-recommender -This project framework provides the following features: +# Session Recommender V2 -* Feature 1 -* Feature 2 -* ... +Coming soon... -## Getting Started -### Prerequisites +``` -(ideally very short, if any) +insert into web.sessions + (title, abstract, external_id, start_time_PST, end_time_PST, require_embeddings_update) +values + ( + 'Building a session recommender using OpenAI and Azure SQL', + 'In this fun and demo-driven session you’ll learn how to integrate Azure SQL with OpenAI to generate text embeddings, store them in the database, index them and calculate cosine distance to build a session recommender. And once that is done, you’ll publish it as a REST and GraphQL API to be consumed by a modern JavaScript frontend. Sounds pretty cool, uh? Well, it is!', + 'S1', + '2024-03-10 10:00:00', + '2024-03-10 11:00:00', + 1 + ) -- OS -- Library version -- ... +``` -### Installation +``` +swa start ./client --api-location ./func --data-api-location ./swa-db-connections +``` -(ideally very short) +## Fluent UI -- npm install [package name] -- mvn install -- ... +The solution uses Fluent UI for the UI components. The Fluent UI is a collection of UX frameworks from Microsoft that provides a consistent design language for web, mobile, and desktop applications. More details about Fluent UI can be found at the following links: -### Quickstart -(Add steps to get up and running quickly) - -1. git clone [repository clone url] -2. cd [repository name] -3. ... - - -## Demo - -A demo app is included to show how to use the project. - -To run the demo, follow these steps: - -(Add steps to start up the demo) - -1. -2. -3. - -## Resources - -(Any additional resources or related projects) - -- Link to supporting information -- Link to similar sample -- ... +- https://github.com/microsoft/fluentui +- https://react.fluentui.dev/ \ No newline at end of file diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 0000000..b03c92c --- /dev/null +++ b/azure.yaml @@ -0,0 +1,15 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json + +name: azure-sql-db-session-recommender-v2 +metadata: + template: azure-sql-db-session-recommender-v2 +services: + web: + project: ./client + language: js + host: staticwebapp + dist: dist + functionapp: + project: ./func + language: dotnet + host: function \ No newline at end of file diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs new file mode 100644 index 0000000..4dcb439 --- /dev/null +++ b/client/.eslintrc.cjs @@ -0,0 +1,20 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..f0fa470 --- /dev/null +++ b/client/index.html @@ -0,0 +1,17 @@ + + + + + + Conference AI Assistant + + + + + + + +
+ + + diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..6dfe1fc --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,7054 @@ +{ + "name": "client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "client", + "version": "0.0.0", + "dependencies": { + "@fluentui/react": "^8.115.5", + "@fluentui/react-components": "^9.38.0", + "dayjs": "^1.11.10", + "localforage": "^1.10.0", + "localstorage-slim": "^2.7.0", + "match-sorter": "^6.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-markdown": "^9.0.1", + "react-router-dom": "^6.16.0", + "sort-by": "^1.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.10.0", + "@typescript-eslint/parser": "^6.10.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "typescript": "^5.2.2", + "vite": "^4.4.5" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz", + "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.0.tgz", + "integrity": "sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.0", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.0", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", + "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.1.tgz", + "integrity": "sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.22.5.tgz", + "integrity": "sha512-nTh2ogNUtxbiSbxaT4Ds6aXnXEipHweN9YRgOX/oNXdf0cCrGn/+2LozFa3lnPV5D90MkjhgckCPBrsoSc1a7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.22.5.tgz", + "integrity": "sha512-yIiRO6yobeEIaI0RTbIr8iAK9FcBHLtZq0S89ZPjDLQXBA4xvghaKqI0etp/tF3htTM0sazJKKLz9oEiGRtu7w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.23.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.1.tgz", + "integrity": "sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", + "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", + "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", + "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", + "dependencies": { + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "dependencies": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" + }, + "node_modules/@fluentui/date-time-utilities": { + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/@fluentui/date-time-utilities/-/date-time-utilities-8.5.16.tgz", + "integrity": "sha512-l+mLfJ2VhdHjBpELLLPDaWgT7GMLynm2aqR7SttbEb6Jh7hc/7ck1MWm93RTb3gYVHYai8SENqimNcvIxHt/zg==", + "dependencies": { + "@fluentui/set-version": "^8.2.14", + "tslib": "^2.1.0" + } + }, + "node_modules/@fluentui/dom-utilities": { + "version": "2.2.14", + "resolved": "https://registry.npmjs.org/@fluentui/dom-utilities/-/dom-utilities-2.2.14.tgz", + "integrity": "sha512-+4DVm5sNfJh+l8fM+7ylpOkGNZkNr4X1z1uKQPzRJ1PRhlnvc6vLpWNNicGwpjTbgufSrVtGKXwP5sf++r81lg==", + "dependencies": { + "@fluentui/set-version": "^8.2.14", + "tslib": "^2.1.0" + } + }, + "node_modules/@fluentui/font-icons-mdl2": { + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/@fluentui/font-icons-mdl2/-/font-icons-mdl2-8.5.32.tgz", + "integrity": "sha512-PCZMijJlDQ5Zy8oNb80vUD6I4ORiR03qFgDT8o08mAGu+KzQO96q4jm0rzPRQuI9CO7pDD/6naOo8UVrmhZ2Aw==", + "dependencies": { + "@fluentui/set-version": "^8.2.14", + "@fluentui/style-utilities": "^8.10.3", + "@fluentui/utilities": "^8.13.24", + "tslib": "^2.1.0" + } + }, + "node_modules/@fluentui/foundation-legacy": { + "version": "8.2.52", + "resolved": "https://registry.npmjs.org/@fluentui/foundation-legacy/-/foundation-legacy-8.2.52.tgz", + "integrity": "sha512-tHCD0m58Zja7wN1FTsvj4Gaj0B22xOhRTpyDzyvxRfjFGYPpR2Jgx/y/KRB3JTOX5EfJHAVzInyWZBeN5IfsVA==", + "dependencies": { + "@fluentui/merge-styles": "^8.5.15", + "@fluentui/set-version": "^8.2.14", + "@fluentui/style-utilities": "^8.10.3", + "@fluentui/utilities": "^8.13.24", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/keyboard-key": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@fluentui/keyboard-key/-/keyboard-key-0.4.14.tgz", + "integrity": "sha512-XzZHcyFEM20H23h3i15UpkHi2AhRBriXPGAHq0Jm98TKFppXehedjjEFuUsh+CyU5JKBhDalWp8TAQ1ArpNzow==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@fluentui/keyboard-keys": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@fluentui/keyboard-keys/-/keyboard-keys-9.0.7.tgz", + "integrity": "sha512-vaQ+lOveQTdoXJYqDQXWb30udSfTVcIuKk1rV0X0eGAgcHeSDeP1HxMy+OgHOQZH3OiBH4ZYeWxb+tmfiDiygQ==", + "dependencies": { + "@swc/helpers": "^0.5.1" + } + }, + "node_modules/@fluentui/merge-styles": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/@fluentui/merge-styles/-/merge-styles-8.5.15.tgz", + "integrity": "sha512-4CdKwo4k1Un2QLulpSVIz/KMgLNBMgin4NPyapmKDMVuO1OOxJUqfocubRGNO5x9mKgAMMYwBKGO9i0uxMMpJw==", + "dependencies": { + "@fluentui/set-version": "^8.2.14", + "tslib": "^2.1.0" + } + }, + "node_modules/@fluentui/priority-overflow": { + "version": "9.1.9", + "resolved": "https://registry.npmjs.org/@fluentui/priority-overflow/-/priority-overflow-9.1.9.tgz", + "integrity": "sha512-+YlmuQ2E2le/FsJHKQPgs/vx2q4nDFXTN1yDLht2RUQl8/wsl7AHorHyMU/9ETPoxVWP+fFMnj3rTV+e5vWirg==", + "dependencies": { + "@swc/helpers": "^0.5.1" + } + }, + "node_modules/@fluentui/react": { + "version": "8.115.5", + "resolved": "https://registry.npmjs.org/@fluentui/react/-/react-8.115.5.tgz", + "integrity": "sha512-/Dmtf52r1pd2tbJ6NqbC65Zs+V/lOharui2uTtc0GxaMr3QREUWUkojtxKFwLVHqCrO1NzcIZm6siqR4z6ucRw==", + "dependencies": { + "@fluentui/date-time-utilities": "^8.5.16", + "@fluentui/font-icons-mdl2": "^8.5.32", + "@fluentui/foundation-legacy": "^8.2.52", + "@fluentui/merge-styles": "^8.5.15", + "@fluentui/react-focus": "^8.8.40", + "@fluentui/react-hooks": "^8.6.36", + "@fluentui/react-portal-compat-context": "^9.0.11", + "@fluentui/react-window-provider": "^2.2.18", + "@fluentui/set-version": "^8.2.14", + "@fluentui/style-utilities": "^8.10.3", + "@fluentui/theme": "^2.6.41", + "@fluentui/utilities": "^8.13.24", + "@microsoft/load-themed-styles": "^1.10.26", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "@types/react-dom": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0", + "react-dom": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-accordion": { + "version": "9.3.27", + "resolved": "https://registry.npmjs.org/@fluentui/react-accordion/-/react-accordion-9.3.27.tgz", + "integrity": "sha512-JBG5werMVMqpZJrQeGqrVyF7AOALkFrzQH4MZ0Pcf9zo3qnZ62QUz7YEj+Zl/In7jdfQhRTJ8grf47c4b6Nsiw==", + "dependencies": { + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-alert": { + "version": "9.0.0-beta.91", + "resolved": "https://registry.npmjs.org/@fluentui/react-alert/-/react-alert-9.0.0-beta.91.tgz", + "integrity": "sha512-cH7I64f2uq7x7Ly4oaapZZY9EU+xbqmvgJAgi7Px8LwvC3zohI1RZtlpaTKitjR3Aq51YYd9tP3VU8+1W58PwA==", + "dependencies": { + "@fluentui/react-avatar": "^9.5.45", + "@fluentui/react-button": "^9.3.54", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-aria": { + "version": "9.3.44", + "resolved": "https://registry.npmjs.org/@fluentui/react-aria/-/react-aria-9.3.44.tgz", + "integrity": "sha512-D3pvGgYObBzO00lhwhuuMWbPOZccy5EaYLXXI3OvzYdTs6ias6xXsOCgdnvrQ8BsIKixNUbtfjjxWCCIcLr4cg==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-utilities": "^9.15.2", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-avatar": { + "version": "9.5.45", + "resolved": "https://registry.npmjs.org/@fluentui/react-avatar/-/react-avatar-9.5.45.tgz", + "integrity": "sha512-JEpjDO9O06IERUJQa0i19QLvA36vK81a3xuMln6rjDEbk53Lw5IohhzIAr2ozBGk61x4woJPiyP9hX0uol0ACA==", + "dependencies": { + "@fluentui/react-badge": "^9.2.13", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-popover": "^9.8.20", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-tooltip": "^9.3.21", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-badge": { + "version": "9.2.13", + "resolved": "https://registry.npmjs.org/@fluentui/react-badge/-/react-badge-9.2.13.tgz", + "integrity": "sha512-oK8U+VnaFHL0EM9t3f/G+IoVqRNEFILvTibLPufH8voDJbSQDZarXjNEUonovAUqGp4Y3TsCL6y27rRMi9TK/Q==", + "dependencies": { + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-button": { + "version": "9.3.54", + "resolved": "https://registry.npmjs.org/@fluentui/react-button/-/react-button-9.3.54.tgz", + "integrity": "sha512-hjwpwdSJgYcBVZhonM/h+q++69hNa4OoCkL+SXWyHjKc6UZn9QyUVbdUvyHkhA/K5MhKfzrv5Hw2i4PLJb2S6w==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-card": { + "version": "9.0.53", + "resolved": "https://registry.npmjs.org/@fluentui/react-card/-/react-card-9.0.53.tgz", + "integrity": "sha512-fNKBzLnuU6zVU/YFdsuR63Y0LjjzMg6ptRztGqY92bWilmv8xQVLHhJmUqtw9WTnGu0KM7FmdZZLrCm5gEJM1w==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-checkbox": { + "version": "9.1.55", + "resolved": "https://registry.npmjs.org/@fluentui/react-checkbox/-/react-checkbox-9.1.55.tgz", + "integrity": "sha512-1XcTP/6SnHJrk41Qr1A/ntpF9sIVPZkG9YLUxXo1Cz3QeO1yik22GkEugupBlmeyOx4zs+Cwiasw0vZKBRUqew==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-label": "^9.1.49", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-combobox": { + "version": "9.5.29", + "resolved": "https://registry.npmjs.org/@fluentui/react-combobox/-/react-combobox-9.5.29.tgz", + "integrity": "sha512-IrZGFNL3J+AnN92nN3bD27HPWx++6hcfH/jgpxZGUBdG8F6fhixlItCkx2k4DxONWNNMlZQ/SGB5TXNO5lCP4g==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-portal": "^9.4.0", + "@fluentui/react-positioning": "^9.10.0", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-components": { + "version": "9.38.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-components/-/react-components-9.38.0.tgz", + "integrity": "sha512-lDmoYefKSeXjORmO8cvKlDQOkWDfKEuOrMSDci6orxgWnGrIONh6mDmHg7C+2H8DeIWBiK7Bigd75BAWt6TNQA==", + "dependencies": { + "@fluentui/react-accordion": "^9.3.27", + "@fluentui/react-alert": "9.0.0-beta.91", + "@fluentui/react-avatar": "^9.5.45", + "@fluentui/react-badge": "^9.2.13", + "@fluentui/react-button": "^9.3.54", + "@fluentui/react-card": "^9.0.53", + "@fluentui/react-checkbox": "^9.1.55", + "@fluentui/react-combobox": "^9.5.29", + "@fluentui/react-dialog": "^9.8.4", + "@fluentui/react-divider": "^9.2.49", + "@fluentui/react-drawer": "9.0.0", + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-image": "^9.1.46", + "@fluentui/react-infobutton": "9.0.0-beta.75", + "@fluentui/react-infolabel": "^9.0.3", + "@fluentui/react-input": "^9.4.51", + "@fluentui/react-label": "^9.1.49", + "@fluentui/react-link": "^9.1.33", + "@fluentui/react-menu": "^9.12.31", + "@fluentui/react-message-bar": "^9.0.5", + "@fluentui/react-overflow": "^9.0.43", + "@fluentui/react-persona": "^9.2.55", + "@fluentui/react-popover": "^9.8.20", + "@fluentui/react-portal": "^9.4.0", + "@fluentui/react-positioning": "^9.10.0", + "@fluentui/react-progress": "^9.1.51", + "@fluentui/react-provider": "^9.12.0", + "@fluentui/react-radio": "^9.1.55", + "@fluentui/react-select": "^9.1.51", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-skeleton": "^9.0.39", + "@fluentui/react-slider": "^9.1.55", + "@fluentui/react-spinbutton": "^9.2.51", + "@fluentui/react-spinner": "^9.3.29", + "@fluentui/react-switch": "^9.1.55", + "@fluentui/react-table": "^9.10.10", + "@fluentui/react-tabs": "^9.3.56", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-tags": "^9.0.9", + "@fluentui/react-text": "^9.3.46", + "@fluentui/react-textarea": "^9.3.51", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-toast": "^9.3.16", + "@fluentui/react-toolbar": "^9.1.55", + "@fluentui/react-tooltip": "^9.3.21", + "@fluentui/react-tree": "^9.4.12", + "@fluentui/react-utilities": "^9.15.2", + "@fluentui/react-virtualizer": "9.0.0-alpha.56", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-context-selector": { + "version": "9.1.42", + "resolved": "https://registry.npmjs.org/@fluentui/react-context-selector/-/react-context-selector-9.1.42.tgz", + "integrity": "sha512-Xq9JcPBCRLkCnrUd83qSFgEYZc1BYyxFXLamtev5Ok1SSF53XI4yqN7Y34A13fSu/Q2wGeZibHcCTHJIXad2sQ==", + "dependencies": { + "@fluentui/react-utilities": "^9.15.2", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-dialog": { + "version": "9.8.4", + "resolved": "https://registry.npmjs.org/@fluentui/react-dialog/-/react-dialog-9.8.4.tgz", + "integrity": "sha512-H8PNiYnsycpK5+I7rO/IhRn5hZImeAmdc7A9t9lNMGTIDELSTRS/aOOV7D8dENY0fegD6oNi4gLBM+k6WOAc6g==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-portal": "^9.4.0", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1", + "react-transition-group": "^4.4.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-divider": { + "version": "9.2.49", + "resolved": "https://registry.npmjs.org/@fluentui/react-divider/-/react-divider-9.2.49.tgz", + "integrity": "sha512-XS0QWsbU0YHhZCtssYT3hT6825im8COcnSJ96KAj7rpffhlkOmpCdewNIQr/TyFrcLgfYg8jdKWO9QSmZpl1qg==", + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-drawer": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-drawer/-/react-drawer-9.0.0.tgz", + "integrity": "sha512-HWXtBwFQsDklhDiS3O5h2OokJ3B/kKMOXPHE8/vjELK4TsUDaV9gkr2IEHIlhx1OR7oo+N5wKmsbp+B7FNFspA==", + "dependencies": { + "@fluentui/react-dialog": "^9.8.4", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-motion-preview": "^0.5.1", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-field": { + "version": "9.1.41", + "resolved": "https://registry.npmjs.org/@fluentui/react-field/-/react-field-9.1.41.tgz", + "integrity": "sha512-0HmXM7HMAkabLuPDhlXKJjVg9hY5fGtgtrT1sVW3GgqiqYU+P5RYD67X4R28MSzrPvUVAc1tv9a3+UIQFF59QQ==", + "dependencies": { + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-label": "^9.1.49", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-focus": { + "version": "8.8.40", + "resolved": "https://registry.npmjs.org/@fluentui/react-focus/-/react-focus-8.8.40.tgz", + "integrity": "sha512-ha0CbLv5EIbjYCtQky6LVZObxOeMfhixrgrzfXm3Ta2eGs1NyZRDm1VeM6acOolWB/8QiN/CbdGckjALli8L2g==", + "dependencies": { + "@fluentui/keyboard-key": "^0.4.14", + "@fluentui/merge-styles": "^8.5.15", + "@fluentui/set-version": "^8.2.14", + "@fluentui/style-utilities": "^8.10.3", + "@fluentui/utilities": "^8.13.24", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-hooks": { + "version": "8.6.36", + "resolved": "https://registry.npmjs.org/@fluentui/react-hooks/-/react-hooks-8.6.36.tgz", + "integrity": "sha512-kI0Z4Q4xHUs4SOmmI5n5OH5fPckqMSCovTRpiuxzCO2TNzLmfC861+nqf4Ygw/ChqNm2gWNZZfUADfnNAEsq+Q==", + "dependencies": { + "@fluentui/react-window-provider": "^2.2.18", + "@fluentui/set-version": "^8.2.14", + "@fluentui/utilities": "^8.13.24", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-icons": { + "version": "2.0.222", + "resolved": "https://registry.npmjs.org/@fluentui/react-icons/-/react-icons-2.0.222.tgz", + "integrity": "sha512-3Qy9GPww9rj51mJ6iEGCqSBEDZ8qBK+FK0BdtcVF4LFxpnPbB45hEf2dZ6LBQbfuKgH8NB3QHRSky75DjrjfdA==", + "dependencies": { + "@griffel/react": "^1.0.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-image": { + "version": "9.1.46", + "resolved": "https://registry.npmjs.org/@fluentui/react-image/-/react-image-9.1.46.tgz", + "integrity": "sha512-J7fOUyF+xNvdfBBCthmW2VV3LMCdD4kDa4TzjAbMT9a/gafeXWaY2+gcEPtuBQ5gLAKv5HDVlEtxTmK3Bj+OAA==", + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-infobutton": { + "version": "9.0.0-beta.75", + "resolved": "https://registry.npmjs.org/@fluentui/react-infobutton/-/react-infobutton-9.0.0-beta.75.tgz", + "integrity": "sha512-axkJNnQGHpiOrrXOdJ9qrR9RNM626fYc4G/uUv9lgBp9NSD0ov15lFnzzjFWx34zTTfbmXZUVDzUEyzXd9g0xA==", + "dependencies": { + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-label": "^9.1.49", + "@fluentui/react-popover": "^9.8.20", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-infolabel": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@fluentui/react-infolabel/-/react-infolabel-9.0.3.tgz", + "integrity": "sha512-lK0rV2tB4RAsqeHc0UuFnsPFfr/gcA0TfbcdWyBCtGsRIB5RLsIGM1o3t/p0SfT1s1N0AmYi9abh5Yfhgfs7sw==", + "dependencies": { + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-label": "^9.1.49", + "@fluentui/react-popover": "^9.8.20", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "@types/react-dom": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0", + "react-dom": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-input": { + "version": "9.4.51", + "resolved": "https://registry.npmjs.org/@fluentui/react-input/-/react-input-9.4.51.tgz", + "integrity": "sha512-fdjU49z+qUNiOOpCMbV/sGyFJK2A5v53iIX26Ukxw/JLxXAaGnhCsKxkZgvx4PNyYRxWuYVwfkV0kUR2jjiN9Q==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-jsx-runtime": { + "version": "9.0.19", + "resolved": "https://registry.npmjs.org/@fluentui/react-jsx-runtime/-/react-jsx-runtime-9.0.19.tgz", + "integrity": "sha512-aSz/H86hsJQTQ7CkxfH4BIiwDRzIuQs9XKNCiVHNzk6AX2cEeA12SM4NSeT5VmksM+D7vL6J0EBmUGDyX6bY5A==", + "dependencies": { + "@fluentui/react-utilities": "^9.15.2", + "@swc/helpers": "^0.5.1", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-jsx-runtime/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + }, + "node_modules/@fluentui/react-label": { + "version": "9.1.49", + "resolved": "https://registry.npmjs.org/@fluentui/react-label/-/react-label-9.1.49.tgz", + "integrity": "sha512-N8Wlork7ZfJIqiKxginAPy9TrX/ZbVU3DljWyw3cD9Y9u6HAaFeYBM7heRhLgJNByN/NxcArw54/l99hBka2bg==", + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-link": { + "version": "9.1.33", + "resolved": "https://registry.npmjs.org/@fluentui/react-link/-/react-link-9.1.33.tgz", + "integrity": "sha512-MXXeyIrrawilx1+R25BzQV4zrWJ5PVIXrpPsFrmj9drkBvYBFceFhwjrzUxV1O51Q59Rpsh0I2Jz7rUwoGg8Qg==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-menu": { + "version": "9.12.31", + "resolved": "https://registry.npmjs.org/@fluentui/react-menu/-/react-menu-9.12.31.tgz", + "integrity": "sha512-PiWj4w9KnUU6nD+XAqDZR5a+6VquxbyL/Z6vEH+3MfU1769dR2mj68dKfLBZWmQKteSWJvqx4ZBR1KZwsYcccA==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-portal": "^9.4.0", + "@fluentui/react-positioning": "^9.10.0", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-message-bar": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@fluentui/react-message-bar/-/react-message-bar-9.0.5.tgz", + "integrity": "sha512-0q+ROGpIhguGKC4hbopSB0QSA8oEZKgYom/GsqX57J+OZi3sbpp7zdihS+j9pvvuHT22kKh6ASZG24rNTFtrVg==", + "dependencies": { + "@fluentui/react-button": "^9.3.54", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1", + "react-transition-group": "^4.4.1" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "@types/react-dom": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0", + "react-dom": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-motion-preview": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@fluentui/react-motion-preview/-/react-motion-preview-0.5.1.tgz", + "integrity": "sha512-tg7q73/mXR0ZhlZPn5nATIDmefDMN02DEnfhAjcio2by0tma15OV/RNlbEsoqUR2xDu9BSjieSS8TIZTgjq3SQ==", + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-overflow": { + "version": "9.0.43", + "resolved": "https://registry.npmjs.org/@fluentui/react-overflow/-/react-overflow-9.0.43.tgz", + "integrity": "sha512-YQDar2pw3WKpgK+gYg16l6Lsfz7HF4YLckOFlT4AujhrpdN6vEwDfw9Afj8/kSvJy5iGs0rqw8s4KokRWZArxA==", + "dependencies": { + "@fluentui/priority-overflow": "^9.1.9", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-persona": { + "version": "9.2.55", + "resolved": "https://registry.npmjs.org/@fluentui/react-persona/-/react-persona-9.2.55.tgz", + "integrity": "sha512-/CeY60wMXyZY67OPFuhHtli+ToDq2dWHPYGUIVqCWfAuLwvvWqbi8jPTlVOflGOWJJuomVeK/7iRb0gUJPobHg==", + "dependencies": { + "@fluentui/react-avatar": "^9.5.45", + "@fluentui/react-badge": "^9.2.13", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-popover": { + "version": "9.8.20", + "resolved": "https://registry.npmjs.org/@fluentui/react-popover/-/react-popover-9.8.20.tgz", + "integrity": "sha512-Pi69IIFSTIsEre//bQaHrPKGpN64E326MpCJ7b3QS4oyS0w/z3mv0ym893bLY4ktHbw6EZSWf+WchVJpfGYybA==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-portal": "^9.4.0", + "@fluentui/react-positioning": "^9.10.0", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-portal": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-portal/-/react-portal-9.4.0.tgz", + "integrity": "sha512-fiL8eFC7g9yurWEbfrpoFej9dv/cmIYWZcmNmvZL9jy6HsTnFS92mE31cKmFJErpqbsIxNFWK9TMs6zT8a4fTQ==", + "dependencies": { + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1", + "use-disposable": "^1.0.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-portal-compat-context": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/@fluentui/react-portal-compat-context/-/react-portal-compat-context-9.0.11.tgz", + "integrity": "sha512-ubvW/ej0O+Pago9GH3mPaxzUgsNnBoqvghNamWjyKvZIViyaXUG6+sgcAl721R+qGAFac+A20akI5qDJz/xtdg==", + "dependencies": { + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-positioning": { + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-positioning/-/react-positioning-9.10.0.tgz", + "integrity": "sha512-2k6e9AUi2yB2GPvgPSCQ42rG+yDkpAs2IfQpaO5ht4L/UxXyxEQUltcPJOQDiPE271WOfPI4rX201CLluHuNSQ==", + "dependencies": { + "@floating-ui/dom": "^1.2.0", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-progress": { + "version": "9.1.51", + "resolved": "https://registry.npmjs.org/@fluentui/react-progress/-/react-progress-9.1.51.tgz", + "integrity": "sha512-z2evxOKqgppbwrUajG4h0LbXax0sJ15t30gKtCzfY7TuvWqtmpImod6jOFyidWh9PLnNlNZeyJj2zQZ3VNPC6g==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-provider": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-provider/-/react-provider-9.12.0.tgz", + "integrity": "sha512-ocS0247BX7x9rUIozs0mMNGfDkjNIcFRq20cwPzpEUUHgKj/Dw0Ae1sXUORRUWG/z+/nalQx49ccqf6FcqmoYw==", + "dependencies": { + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/core": "^1.14.1", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-radio": { + "version": "9.1.55", + "resolved": "https://registry.npmjs.org/@fluentui/react-radio/-/react-radio-9.1.55.tgz", + "integrity": "sha512-WxAtrpKwLjhSVQ796DeayJ3aAe4zVYXlGgWMyN9/zjNruve7jNrybMMUw6XPBwjw+Ycfk6P3r/rCasG2SphzOg==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-label": "^9.1.49", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-select": { + "version": "9.1.51", + "resolved": "https://registry.npmjs.org/@fluentui/react-select/-/react-select-9.1.51.tgz", + "integrity": "sha512-sUpfOM9Aj+N9cjPkW+LcmZW+FFk1asPMt8GiMP68szFwxcLwXkOb3rvG26v6C9fexGKpk4SA9giZ0iuaTnlsJA==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-shared-contexts": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@fluentui/react-shared-contexts/-/react-shared-contexts-9.12.0.tgz", + "integrity": "sha512-1HSFgrLB9631E4XXLemotfqTSxnKEZlcx+h7kDn2Q+GtK6dvpjaDtsnEPvC0aoCbXz7UTyCsl6OIPU+yjcfhyw==", + "dependencies": { + "@fluentui/react-theme": "^9.1.16", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-skeleton": { + "version": "9.0.39", + "resolved": "https://registry.npmjs.org/@fluentui/react-skeleton/-/react-skeleton-9.0.39.tgz", + "integrity": "sha512-O5oRgbEsR8mBck39rJJKPHDCUqYsko6mOpqMrcB196BBp2vcI6usrU1i5lJ3rN48KmiT7OMaeRotwwW32ep4/A==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-slider": { + "version": "9.1.55", + "resolved": "https://registry.npmjs.org/@fluentui/react-slider/-/react-slider-9.1.55.tgz", + "integrity": "sha512-wgaOkVGlvEd/d5j9mDSI93zhmoYdnkgQy7S96CKPoPbA0N0MbOIApWz5uzQBu6X0mUfmI66jaafYrQ+u1vWwgA==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-spinbutton": { + "version": "9.2.51", + "resolved": "https://registry.npmjs.org/@fluentui/react-spinbutton/-/react-spinbutton-9.2.51.tgz", + "integrity": "sha512-adlILeKAobCtLVaQpJY+PvJPsMaZVM9EIIMyi2s083XzgXUij6Xwa9c/89vyUO5f5P5HkYm+V12KqnOOoXzhBQ==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-spinner": { + "version": "9.3.29", + "resolved": "https://registry.npmjs.org/@fluentui/react-spinner/-/react-spinner-9.3.29.tgz", + "integrity": "sha512-oF4HsUrtyZxMXManIH0Q0+QtRf39aPq8bFc5umItsqi2uT+hom8fSc5yPPOgFAma3E+ioUxfFbM+dC3Zapzy2Q==", + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-label": "^9.1.49", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-switch": { + "version": "9.1.55", + "resolved": "https://registry.npmjs.org/@fluentui/react-switch/-/react-switch-9.1.55.tgz", + "integrity": "sha512-gMi9Yv4DmVYsF+DJEjdoI5hEHH8nPpdUV8l2m0hjbV3IuBpMXD2RVAGeo/T6GMHtafk9v2PFsFEy2xBjcBOiDA==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-label": "^9.1.49", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-table": { + "version": "9.10.10", + "resolved": "https://registry.npmjs.org/@fluentui/react-table/-/react-table-9.10.10.tgz", + "integrity": "sha512-spnXKUJyPx8AC+IBSIVY+4T18Cbrk0nnRu38HcBaTnnaFv2z6lr1AiYbQ0Tyul42YiQBipSpl0mUML/kk0B0+A==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-avatar": "^9.5.45", + "@fluentui/react-checkbox": "^9.1.55", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-radio": "^9.1.55", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-tabs": { + "version": "9.3.56", + "resolved": "https://registry.npmjs.org/@fluentui/react-tabs/-/react-tabs-9.3.56.tgz", + "integrity": "sha512-5uaHdXV6pNSPnfpEhar6UjkoSaUNNJZEDxe5cBECC8IkvbC9lBkq6LdO5EB+Bdnul002dy4D0Aj0SGNI7DuqvQ==", + "dependencies": { + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0", + "scheduler": "^0.19.0 || ^0.20.0" + } + }, + "node_modules/@fluentui/react-tabster": { + "version": "9.14.4", + "resolved": "https://registry.npmjs.org/@fluentui/react-tabster/-/react-tabster-9.14.4.tgz", + "integrity": "sha512-TVsYcVoonGuoO3naHwurrWx4WcQIAefGljb8VxzRnZb5MuGY6vLk3jlsuQWYWyO24o4YH2fk7n5oSW6ugA1MAA==", + "dependencies": { + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1", + "keyborg": "^2.1.0", + "tabster": "^4.8.0" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-tags": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/@fluentui/react-tags/-/react-tags-9.0.9.tgz", + "integrity": "sha512-zKO8+q3hjMr7b+c934tYy5sKebiSviMwPjKcRZc9cb9XuuyQwgDzul7WvuSBfZnuR9u/gk+6o7TRkQxjcQcAew==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-avatar": "^9.5.45", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-text": { + "version": "9.3.46", + "resolved": "https://registry.npmjs.org/@fluentui/react-text/-/react-text-9.3.46.tgz", + "integrity": "sha512-YrRxA3SCYn/NQj2BIesilXhjD8tFedYvJUjZUQMZD1Lm9ccFYnF/sI2WNz71Ta563rl0L9Ia2KMmNwD/XMZJoA==", + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-textarea": { + "version": "9.3.51", + "resolved": "https://registry.npmjs.org/@fluentui/react-textarea/-/react-textarea-9.3.51.tgz", + "integrity": "sha512-FmPzQxwC4GLpke3uRdgNVnKHJRcrKpHLrmV2yyvcVwMEHn3OnH0QxMCMZhaDdedbAGAWO6Z2+kWfsdfTnrG99A==", + "dependencies": { + "@fluentui/react-field": "^9.1.41", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-theme": { + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-theme/-/react-theme-9.1.16.tgz", + "integrity": "sha512-QK2dGE5aQXN1UGdiEmGKpYGP3tHXIchLvFf8DEEOWnF4XBc9SiEPNFYkvLMJjHxZmDz4D670rsOPe0r5jFDEKQ==", + "dependencies": { + "@fluentui/tokens": "1.0.0-alpha.13", + "@swc/helpers": "^0.5.1" + } + }, + "node_modules/@fluentui/react-toast": { + "version": "9.3.16", + "resolved": "https://registry.npmjs.org/@fluentui/react-toast/-/react-toast-9.3.16.tgz", + "integrity": "sha512-NGU2oz4JgmX7MaglyKnYRY2pw7uKjQbPzQj0pxPFk1hMi7l30w46xJ24m74z01kqQYCCxl5hAjfICVIGIX07MQ==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-portal": "^9.4.0", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1", + "react-transition-group": "^4.4.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-toolbar": { + "version": "9.1.55", + "resolved": "https://registry.npmjs.org/@fluentui/react-toolbar/-/react-toolbar-9.1.55.tgz", + "integrity": "sha512-Z4MDjZ4pH8P/5ttR5UlZ++aC1MzGcIHNWVqdO4DUpPHEaneBxuRzAYlMQlZW/IZLDw7JEfFwQLIP0GscfTtWUw==", + "dependencies": { + "@fluentui/react-button": "^9.3.54", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-divider": "^9.2.49", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-radio": "^9.1.55", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-tooltip": { + "version": "9.3.21", + "resolved": "https://registry.npmjs.org/@fluentui/react-tooltip/-/react-tooltip-9.3.21.tgz", + "integrity": "sha512-9y77imd0lBSO/xA2fzbBHA0FRf5MMda+p1B1xQKk7cRhhz6TzVX83y7bdf6ewcWBVuZxqDCFc3SELI1gOZxEKQ==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-portal": "^9.4.0", + "@fluentui/react-positioning": "^9.10.0", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-tree": { + "version": "9.4.12", + "resolved": "https://registry.npmjs.org/@fluentui/react-tree/-/react-tree-9.4.12.tgz", + "integrity": "sha512-01hBhQIVOpUHudAU/RvMk0i0yWYLnnF5rFPy6KWUgIs068gU+MEMuUn9+nCM9au5UGpGcgqr2840dmibSCGi1Q==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@fluentui/react-aria": "^9.3.44", + "@fluentui/react-avatar": "^9.5.45", + "@fluentui/react-button": "^9.3.54", + "@fluentui/react-checkbox": "^9.1.55", + "@fluentui/react-context-selector": "^9.1.42", + "@fluentui/react-icons": "^2.0.217", + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-radio": "^9.1.55", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-tabster": "^9.14.4", + "@fluentui/react-theme": "^9.1.16", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-utilities": { + "version": "9.15.2", + "resolved": "https://registry.npmjs.org/@fluentui/react-utilities/-/react-utilities-9.15.2.tgz", + "integrity": "sha512-Oq016/dHu7PXW5x/2RtLts1ULiyd7JctXFdvi9IacLs/J1nLfg2KSBzzLqKxtdyVvgbZ9Mlu6kPITbFtF9dsIA==", + "dependencies": { + "@fluentui/keyboard-keys": "^9.0.7", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-virtualizer": { + "version": "9.0.0-alpha.56", + "resolved": "https://registry.npmjs.org/@fluentui/react-virtualizer/-/react-virtualizer-9.0.0-alpha.56.tgz", + "integrity": "sha512-IrJKFrpPO/uLA9dVUuzUEQ5LPHtlOHhykOKiGWWWUXsjJUSekTU17PL45fylaJmwsn4bDtWIFSNBW0FSQQrkLg==", + "dependencies": { + "@fluentui/react-jsx-runtime": "^9.0.19", + "@fluentui/react-shared-contexts": "^9.12.0", + "@fluentui/react-utilities": "^9.15.2", + "@griffel/react": "^1.5.14", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@types/react": ">=16.14.0 <19.0.0", + "@types/react-dom": ">=16.14.0 <19.0.0", + "react": ">=16.14.0 <19.0.0", + "react-dom": ">=16.14.0 <19.0.0" + } + }, + "node_modules/@fluentui/react-window-provider": { + "version": "2.2.18", + "resolved": "https://registry.npmjs.org/@fluentui/react-window-provider/-/react-window-provider-2.2.18.tgz", + "integrity": "sha512-nBKqxd0P8NmIR0qzFvka1urE2LVbUm6cse1I1T7TcOVNYa5jDf5BrO06+JRZfwbn00IJqOnIVoP0qONqceypWQ==", + "dependencies": { + "@fluentui/set-version": "^8.2.14", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/set-version": { + "version": "8.2.14", + "resolved": "https://registry.npmjs.org/@fluentui/set-version/-/set-version-8.2.14.tgz", + "integrity": "sha512-f/QWJnSeyfAjGAqq57yjMb6a5ejPlwfzdExPmzFBuEOuupi8hHbV8Yno12XJcTW4I0KXEQGw+PUaM1aOf/j7jw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@fluentui/style-utilities": { + "version": "8.10.3", + "resolved": "https://registry.npmjs.org/@fluentui/style-utilities/-/style-utilities-8.10.3.tgz", + "integrity": "sha512-pyO9BGkwIxXaIMVT6ma98GIZAgTjGc0LZ5iUai9GLIrFLQWnIKnS//hgUx8qG4AecUeqZ26Wb0e+Ale9NyPQCQ==", + "dependencies": { + "@fluentui/merge-styles": "^8.5.15", + "@fluentui/set-version": "^8.2.14", + "@fluentui/theme": "^2.6.41", + "@fluentui/utilities": "^8.13.24", + "@microsoft/load-themed-styles": "^1.10.26", + "tslib": "^2.1.0" + } + }, + "node_modules/@fluentui/theme": { + "version": "2.6.41", + "resolved": "https://registry.npmjs.org/@fluentui/theme/-/theme-2.6.41.tgz", + "integrity": "sha512-h9RguEzqzJ0+59ys5Kkp7JtsjhDUxBLmQunu5rpHp5Mp788OtEjI/n1a9FIcOAL/priPSQwXN7RbuDpeP7+aSw==", + "dependencies": { + "@fluentui/merge-styles": "^8.5.15", + "@fluentui/set-version": "^8.2.14", + "@fluentui/utilities": "^8.13.24", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@fluentui/tokens": { + "version": "1.0.0-alpha.13", + "resolved": "https://registry.npmjs.org/@fluentui/tokens/-/tokens-1.0.0-alpha.13.tgz", + "integrity": "sha512-IzYysTTBkAH7tQZxYKpzhxYnTJkvwXhjhTOpmERgnqTFifHTP8/vaQjJAAm7dI/9zlDx1oN+y/I+KzL9bDLHZQ==", + "dependencies": { + "@swc/helpers": "^0.5.1" + } + }, + "node_modules/@fluentui/utilities": { + "version": "8.13.24", + "resolved": "https://registry.npmjs.org/@fluentui/utilities/-/utilities-8.13.24.tgz", + "integrity": "sha512-/jo6hWCzTGCx06l2baAMwsjjBZ/dyMouls53uNaQLUGUUhUwXh/DcDDXMqLRJB3MaH9zvgfvRw61iKmm2s9fIA==", + "dependencies": { + "@fluentui/dom-utilities": "^2.2.14", + "@fluentui/merge-styles": "^8.5.15", + "@fluentui/set-version": "^8.2.14", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@griffel/core": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@griffel/core/-/core-1.15.0.tgz", + "integrity": "sha512-+2Li2x6zqQdVBSMbvGSJRxbMbOrXhCEEzX0BK6OMfjdMPJLoR2aaHuAwHL3J9dOpHzFrjp9MMEo4Jzwfo4l6Xw==", + "dependencies": { + "@emotion/hash": "^0.9.0", + "@griffel/style-types": "^1.0.2", + "csstype": "^3.1.2", + "rtl-css-js": "^1.16.1", + "stylis": "^4.2.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@griffel/react": { + "version": "1.5.18", + "resolved": "https://registry.npmjs.org/@griffel/react/-/react-1.5.18.tgz", + "integrity": "sha512-Y5L2zvfE+quMPSQPtViMmuDXNCIyJaeeQc5m30VMELgXYN0uk4nbFqwKYXG0FmnHkEHy5MhiGy7q4zCR2+ubTg==", + "dependencies": { + "@griffel/core": "^1.15.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": ">=16.8.0 <19.0.0" + } + }, + "node_modules/@griffel/style-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@griffel/style-types/-/style-types-1.0.2.tgz", + "integrity": "sha512-ka/Tpl1WU8js88LObwB/4EvpgXzx/EEJfbHhAr4ZNt29hrQKgL93X1zSY6M/FRhMhWrGIawauWkZP6/y6w/WiQ==", + "dependencies": { + "csstype": "^3.1.2" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", + "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@microsoft/load-themed-styles": { + "version": "1.10.295", + "resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.295.tgz", + "integrity": "sha512-W+IzEBw8a6LOOfRJM02dTT7BDZijxm+Z7lhtOAz1+y9vQm1Kdz9jlAO+qCEKsfxtUOmKilW8DIRqFw2aUgKeGg==" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@remix-run/router": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.9.0.tgz", + "integrity": "sha512-bV63itrKBC0zdT27qYm6SDZHlkXwFL1xMBuhkn+X7l0+IIhNaH5wuuvZKp6eKhCD4KFhujhfhCT1YxXW6esUIA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", + "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.2.tgz", + "integrity": "sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.5", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", + "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", + "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", + "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.3.tgz", + "integrity": "sha512-pvQ+TKeRHeiUGRhvYwRrQ/ISnohKkSJR14fT2yqyZ4e9K5vqc7hrtY2Y1Dw0ZwAzQ6DQsxsaCUuSIIi8v0Cq6w==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.3.tgz", + "integrity": "sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, + "node_modules/@types/prop-types": { + "version": "15.7.8", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.8.tgz", + "integrity": "sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==" + }, + "node_modules/@types/react": { + "version": "18.2.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.27.tgz", + "integrity": "sha512-Wfv7B7FZiR2r3MIqbAlXoY1+tXm4bOqfz4oRr+nyXdBqapDBZ0l/IGcSlAfvxIHEEJjkPU0MYAc/BlFPOcrgLw==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.12", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.12.tgz", + "integrity": "sha512-QWZuiA/7J/hPIGocXreCRbx7wyoeet9ooxfbSA+zbIWqyQEE7GMtRn4A37BdYyksnN+/NDnWgfxZH9UVGDw1hg==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.4", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.4.tgz", + "integrity": "sha512-2L9ifAGl7wmXwP4v3pN4p2FLhD0O1qsJpvKmNin5VA8+UvNVb447UDaAEV6UdrkA+m/Xs58U1RFps44x6TFsVQ==" + }, + "node_modules/@types/semver": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", + "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", + "dev": true + }, + "node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", + "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/type-utils": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", + "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", + "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", + "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", + "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", + "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", + "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", + "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.10.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.0.tgz", + "integrity": "sha512-rM0SqazU9iqPUraQ2JlIvReeaxOoRj6n+PzB1C0cBzIbd8qP336nC39/R9yPi3wVcah7E7j/kdU1uCUqMEU4OQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.22.20", + "@babel/plugin-transform-react-jsx-self": "^7.22.5", + "@babel/plugin-transform-react-jsx-source": "^7.22.5", + "@types/babel__core": "^7.20.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001547", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz", + "integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz", + "integrity": "sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.548", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.548.tgz", + "integrity": "sha512-R77KD6mXv37DOyKLN/eW1rGS61N6yHOfapNSX9w+y9DdPG83l9Gkuv7qkCFZ4Ta4JPhrjgQfYbv4Y3TnM1Hi2Q==", + "dev": true + }, + "node_modules/es-abstract": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz", + "integrity": "sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.1", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "dev": true, + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.1", + "es-set-tostringtag": "^2.0.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", + "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "8.51.0", + "@humanwhocodes/config-array": "^0.11.11", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.3.tgz", + "integrity": "sha512-Hh0wv8bUNY877+sI0BlCUlsS0TYYQqvzEwJsJJPM2WF4RnTStSnSR3zdJYa2nPOJgg3UghXi54lVyMSmpCalzA==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", + "integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/inline-style-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" + }, + "node_modules/internal-slot": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", + "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyborg": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/keyborg/-/keyborg-2.1.0.tgz", + "integrity": "sha512-0+v3/GIYG6gClwBOXrHet31n1UJW49xbKgVPUu6we41aq9tAmMosVXEdctZxsQdebyxtrcxVTkvHjBD1XPOHwg==" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", + "dependencies": { + "lie": "3.1.1" + } + }, + "node_modules/localstorage-slim": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/localstorage-slim/-/localstorage-slim-2.7.0.tgz", + "integrity": "sha512-velxCZhb0mODaD9noe4pu3+OYnMKC66itqDqHMRsVpXKAJYU2+cUX09KRxoZ/nlVwitVgjlG2BZSEaP6RPJmgw==" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/match-sorter": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz", + "integrity": "sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "remove-accents": "0.4.2" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", + "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.0.0.tgz", + "integrity": "sha512-XZuPPzQNBPAlaqsTTgRrcJnyFbSOBovSadFgbFu8SnuNgm+6Bdx1K+IWoitsmj6Lq6MNtI+ytOqwN70n//NaBA==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^5.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz", + "integrity": "sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.0.2.tgz", + "integrity": "sha512-U5I+500EOOw9e3ZrclN3Is3fRpw8c19SMyNZlZ2IS+7vLsNzb2Om11VpIVOR+/0137GhZsFEF6YiKD5+0Hr2Og==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.0.1.tgz", + "integrity": "sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-path": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.6.0.tgz", + "integrity": "sha512-fxrwsCFi3/p+LeLOAwo/wyRMODZxdGBtUlWRzsEpsUVrisZbEfZ21arxLGfaWfcnqb8oHPNihIb4XPE8CQPN5A==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/property-information": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.0.tgz", + "integrity": "sha512-9t5qARVofg2xQqKtytzt+lZ4d1Qvj8t5B8fEwXK6qOfgRLgH/b13QlgEyDh033NOS31nXeFbYv7CLUDG1CeifQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-dom/node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-markdown": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", + "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.16.0.tgz", + "integrity": "sha512-VT4Mmc4jj5YyjpOi5jOf0I+TYzGpvzERy4ckNSvSh2RArv8LLoCxlsZ2D+tc7zgjxcY34oTz2hZaeX5RVprKqA==", + "dependencies": { + "@remix-run/router": "1.9.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.16.0.tgz", + "integrity": "sha512-aTfBLv3mk/gaKLxgRDUPbPw+s4Y/O+ma3rEN1u8EgEpLpPe6gNjIsWt9rxushMHHMb7mSwxRGdGlGdvmFsyPIg==", + "dependencies": { + "@remix-run/router": "1.9.0", + "react-router": "6.16.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.0.0.tgz", + "integrity": "sha512-vx8x2MDMcxuE4lBmQ46zYUDfcFMmvg80WYX+UNLeG6ixjdCCLcw1lrgAukwBTuOFsS78eoAedHGn9sNM0w7TPw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remove-accents": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz", + "integrity": "sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==" + }, + "node_modules/resolve": { + "version": "2.0.0-next.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", + "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/rtl-css-js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", + "integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==", + "dependencies": { + "@babel/runtime": "^7.1.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", + "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sort-by": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/sort-by/-/sort-by-1.2.0.tgz", + "integrity": "sha512-aRyW65r3xMnf4nxJRluCg0H/woJpksU1dQxRtXYzau30sNBOmf5HACpDd9MZDhKh7ALQ5FgSOfMPwZEtUmMqcg==", + "dependencies": { + "object-path": "0.6.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", + "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-to-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", + "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", + "dependencies": { + "inline-style-parser": "0.2.2" + } + }, + "node_modules/stylis": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", + "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tabster": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/tabster/-/tabster-4.9.0.tgz", + "integrity": "sha512-wcXpMQxLA7OFcL4ci6Spsv0C1S26HtCD4904XTW78mQ2iPsKyo+SdjBUZ4SHhNMpa1y4P1cByc6dPJ0n+9yVzA==", + "dependencies": { + "keyborg": "^2.0.0", + "tslib": "^2.3.1" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", + "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unified": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", + "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-disposable": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/use-disposable/-/use-disposable-1.0.2.tgz", + "integrity": "sha512-UMaXVlV77dWOu4GqAFNjRzHzowYKUKbJBQfCexvahrYeIz4OkUYUjna4Tjjdf92NH8Nm8J7wEfFRgTIwYjO5jg==", + "peerDependencies": { + "@types/react": ">=16.8.0 <19.0.0", + "@types/react-dom": ">=16.8.0 <19.0.0", + "react": ">=16.8.0 <19.0.0", + "react-dom": ">=16.8.0 <19.0.0" + } + }, + "node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vite": { + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz", + "integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz", + "integrity": "sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..a6ef7a9 --- /dev/null +++ b/client/package.json @@ -0,0 +1,38 @@ +{ + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@fluentui/react": "^8.115.5", + "@fluentui/react-components": "^9.38.0", + "dayjs": "^1.11.10", + "localforage": "^1.10.0", + "localstorage-slim": "^2.7.0", + "match-sorter": "^6.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-markdown": "^9.0.1", + "react-router-dom": "^6.16.0", + "sort-by": "^1.2.0" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@typescript-eslint/eslint-plugin": "^6.10.0", + "@typescript-eslint/parser": "^6.10.0", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "typescript": "^5.2.2", + "vite": "^4.4.5" + } +} diff --git a/client/src/Main.tsx b/client/src/Main.tsx new file mode 100644 index 0000000..a314e35 --- /dev/null +++ b/client/src/Main.tsx @@ -0,0 +1,43 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom/client"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; +import { FluentProvider, webLightTheme } from "@fluentui/react-components"; + +import Root from "./pages/Root"; +import SessionSearch, { loader as sessionsListLoader } from "./pages/Search"; +import { Chat, action as chatAction } from "./pages/Chat"; +import { About, loader as aboutLoader } from "./pages/About"; + +const router = createBrowserRouter([ + { + path: "/", + element: , + children: [ + { + index: true, + element: , + action: chatAction, + }, + { + index: false, + element: , + path: "/search", + loader: sessionsListLoader, + }, + { + index: false, + element: , + path: "/about", + loader: aboutLoader, + }, + ], + }, +]); + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + + + +); diff --git a/client/src/api/chat.ts b/client/src/api/chat.ts new file mode 100644 index 0000000..540bb75 --- /dev/null +++ b/client/src/api/chat.ts @@ -0,0 +1,57 @@ +import { AskResponse } from "../models"; + +type ChatTurn = { + userPrompt: string; + responseMessage?: string; +}; + +type UserQuestion = { + question: string; + askedOn: Date; +}; + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +let questionAndAnswers: Record = {}; + +export const ask = async (prompt: string) => { + const history: ChatTurn[] = []; + const currentMessageId = Date.now(); + const currentQuestion = { + question: prompt, + askedOn: new Date(), + }; + questionAndAnswers[currentMessageId] = [currentQuestion, undefined]; + + for (let id in questionAndAnswers) { + const [question, answer] = questionAndAnswers[id]; + history.push({ + userPrompt: question.question, + responseMessage: answer?.answer, + }); + } + + const response = await fetch("/api/ask", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(history), + }); + + const askResponse: AskResponse = await response.json(); + + questionAndAnswers[currentMessageId] = [ + currentQuestion, + { + answer: askResponse.answer, + thoughts: askResponse.thoughts, + dataPoints: askResponse.dataPoints, + citationBaseUrl: askResponse.citationBaseUrl, + }, + ]; + + return await Promise.resolve(questionAndAnswers); +}; diff --git a/client/src/api/sessions.ts b/client/src/api/sessions.ts new file mode 100644 index 0000000..49791ab --- /dev/null +++ b/client/src/api/sessions.ts @@ -0,0 +1,72 @@ +export type ErrorInfo = { + errorSource?: string; + errorCode?: number; + errorMessage: string; +}; + +export type SessionInfo = { + id: string; + external_id: string; + title: string; + abstract: string; + start_time_PST: string; + end_time_PST: string; + cosine_similarity: number; + speakers: string; +}; + +export type SessionsResponse = { + sessions: SessionInfo[]; + errorInfo?: ErrorInfo; +}; + +export async function getSessions(content: string): Promise { + const settings = { + method: "post", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + text: content, + }), + }; + + const response = await fetch("/data-api/rest/find", settings); + if (!response.ok) { + return { + sessions: [], + errorInfo: { + errorSource: "Server", + errorCode: response.status, + errorMessage: response.statusText, + }, + }; + } + + var sessions = []; + var errorInfo = undefined; + const data = await response.json(); + + if (data.value.length > 0) { + if (data.value[0].error_code) { + errorInfo = { + errorSource: data.value[0].error_source as string, + errorCode: data.value[0].error_code as number, + errorMessage: data.value[0].error_message as string, + }; + } else { + sessions = data.value; + } + } + + return { sessions: sessions, errorInfo: errorInfo }; +} + +export async function getSessionsCount(): Promise { + const response = await fetch("/data-api/rest/sessions-count"); + if (!response.ok) return "n/a"; + const data = await response.json(); + const totalCount = data ? data.value[0].total_sessions : "n/a"; + return totalCount; +} diff --git a/client/src/components/FancyText.tsx b/client/src/components/FancyText.tsx new file mode 100644 index 0000000..25bab70 --- /dev/null +++ b/client/src/components/FancyText.tsx @@ -0,0 +1,36 @@ +import { makeStyles, Text, TextProps } from "@fluentui/react-components"; + +const useStyles = makeStyles({ + fancy: { + fontSize: "1.125rem", + fontFamily: "var(--base-font-family)", + fontWeight: 600, + fontStyle: "normal", + lineHeight: "1.688rem", + marginTop: "-0.1rem", + textDecorationColor: "none", + textDecorationLine: "none", + textTransform: "none", + color: "var(--color-title-font)", + }, +}); + +export const FancyText = ({ + children, + className, + block, + as, + ...rest +}: TextProps) => { + const classes = useStyles(); + return ( + + {children} + + ); +}; diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx new file mode 100644 index 0000000..b59952d --- /dev/null +++ b/client/src/components/Header.tsx @@ -0,0 +1,10 @@ +import { Title1 } from "@fluentui/react-components"; +import siteConfig from "../site"; + +export const Header = () => { + return ( + + 🤖 {siteConfig.name} - Conference AI Assistant + + ); +}; diff --git a/client/src/components/Navigation.tsx b/client/src/components/Navigation.tsx new file mode 100644 index 0000000..e1f0785 --- /dev/null +++ b/client/src/components/Navigation.tsx @@ -0,0 +1,33 @@ +import { Divider, Tab, TabList } from "@fluentui/react-components"; +import { SearchRegular, ChatRegular, InfoRegular } from "@fluentui/react-icons"; +import { useNavigate } from "react-router-dom"; + +export const Navigation = () => { + const navigate = useNavigate(); + + return ( + <> + { + navigate(data.value === "chat" ? "/" : `/${data.value}`); + }} + selectedValue={ + window.location.pathname === "/" ? "chat" : window.location.pathname.substring(1) + } + > + }> + Ask + + }> + Search + + }> + About + + +
+ +
+ + ); +}; diff --git a/client/src/components/NoSessions.tsx b/client/src/components/NoSessions.tsx new file mode 100644 index 0000000..5b06ea0 --- /dev/null +++ b/client/src/components/NoSessions.tsx @@ -0,0 +1,9 @@ +export function NoSessions() { + return ( +
+

+ No session found +

+
+ ); +} diff --git a/client/src/components/PrimaryButton.tsx b/client/src/components/PrimaryButton.tsx new file mode 100644 index 0000000..d9391f0 --- /dev/null +++ b/client/src/components/PrimaryButton.tsx @@ -0,0 +1,21 @@ +import { Button, ButtonProps, makeStyles } from "@fluentui/react-components"; + +const useStyles = makeStyles({ + button: { + boxShadow: "0 0 1px #0009, 0 1px 2px #0003", + }, +}); + +export const PrimaryButton = ({ children, ...rest }: ButtonProps) => { + const classes = useStyles(); + return ( + + ); +}; diff --git a/client/src/components/Session.tsx b/client/src/components/Session.tsx new file mode 100644 index 0000000..cbdf7d0 --- /dev/null +++ b/client/src/components/Session.tsx @@ -0,0 +1,45 @@ +import { SessionInfo } from "../api/sessions"; +import { Text, Title2 } from "@fluentui/react-components"; +import { FancyText } from "./FancyText"; +import dayjs from "dayjs"; +import siteConfig from "../site"; + +function formatSubtitle(session: SessionInfo) { + const speakers = JSON.parse(session.speakers).join(", "); + + const startTime = dayjs(session.start_time_PST); + const endTime = dayjs(session.end_time_PST); + + const day = startTime.format("dddd") + const start = startTime.format("hh:mm A"); + const end = endTime.format("hh:mm A"); + + return `${speakers} | ${day}, ${start}-${end} | Similarity: ${session.cosine_similarity.toFixed(6)}`; +} + +function formatSessionLink(session: SessionInfo) { + const startTime = dayjs(session.start_time_PST); + + const day = startTime.format("D") + const url = new URL(`session-list.aspx?EventDay=${day}`, siteConfig.sessionUrl); + + return url; +} + +export const Session = ({ session }: { session: SessionInfo }) => { + return ( +
+ + + {session.title} + + + + {formatSubtitle(session)} + + + {session.abstract} + +
+ ); +}; diff --git a/client/src/components/SessionsList.tsx b/client/src/components/SessionsList.tsx new file mode 100644 index 0000000..fa05cc1 --- /dev/null +++ b/client/src/components/SessionsList.tsx @@ -0,0 +1,12 @@ +import { SessionInfo } from "../api/sessions"; +import { Session } from "./Session"; + +export const SessionList = ({ sessions }: { sessions: SessionInfo[] }) => { + return ( +
+ {sessions.map((session) => ( + + ))} +
+ ); +}; diff --git a/client/src/models.ts b/client/src/models.ts new file mode 100644 index 0000000..aaeabb8 --- /dev/null +++ b/client/src/models.ts @@ -0,0 +1,14 @@ +export type SupportingContentRecord = { + title: string; + content: string; + url: string; + similarity: number; +}; + +export type AskResponse = { + answer: string; + thoughts?: string; + dataPoints: SupportingContentRecord[]; + citationBaseUrl: string; + error?: string | null; +}; diff --git a/client/src/pages/About.tsx b/client/src/pages/About.tsx new file mode 100644 index 0000000..2214408 --- /dev/null +++ b/client/src/pages/About.tsx @@ -0,0 +1,56 @@ +import { Suspense } from "react"; +import { Await, LoaderFunction, defer, useLoaderData } from "react-router-dom"; +import ls from "localstorage-slim"; +import { getSessionsCount } from "../api/sessions"; +import { FancyText } from "../components/FancyText"; +import siteConfig from "../site"; + +function showSessionCount( + sessionsCount: string | undefined | null = undefined +) { + var sc = sessionsCount; + if (sc === undefined) { + sc = ls.get("sessionsCount"); + console.log("sessionsCount", sc); + } else { + ls.set("sessionsCount", sc, { ttl: 60 * 60 * 24 * 7 }); + } + if (sc == null) { + return Loading session count...; + } + return ( + + There are {sc} sessions indexed so far. + + ); +} + +export const loader: LoaderFunction = async () => { + const sessionsCount = getSessionsCount(); + return defer({ sessionsCount }); +}; + +export const About = () => { + const { sessionsCount } = useLoaderData() as { + sessionsCount: string | number; + }; + + return ( + <> + + Source code and and related articles are available on GitHub.{" "} + The AI model used generate embeddings is the text-embedding-ada-002 and the AI model used to process and generate natural language content is gpt-4 turbo. + + + Unable to load session count 😥... + } + > + {(sessionsCount) => showSessionCount(sessionsCount)} + + + + ); +}; diff --git a/client/src/pages/Chat.tsx b/client/src/pages/Chat.tsx new file mode 100644 index 0000000..5782ccc --- /dev/null +++ b/client/src/pages/Chat.tsx @@ -0,0 +1,137 @@ +import { + Body1, + Card, + CardHeader, + Textarea, + TextareaProps, + Text, + makeStyles, + CardFooter, + Spinner, + Title2 +} from "@fluentui/react-components"; +import { SendRegular } from "@fluentui/react-icons"; +import { useState } from "react"; +import { ActionFunctionArgs, useFetcher } from "react-router-dom"; +import { ask } from "../api/chat"; +import { FancyText } from "../components/FancyText"; +import { PrimaryButton } from "../components/PrimaryButton"; +import ReactMarkdown from "react-markdown"; + +const useClasses = makeStyles({ + container: {}, + chatArea: {}, + card: {}, + rm: { marginBottom: "-1em", marginTop: "-1em"}, + answersArea: { marginTop: "1em"}, + textarea: { width: "100%", marginBottom: "1rem" }, +}); + +export async function action({ request }: ActionFunctionArgs) { + let formData = await request.formData(); + const prompt = formData.get("prompt"); + if (!prompt) { + return null; + } + + const data = await ask(prompt.toString()); + return data; +} + +const Answers = ({ data }: { data: Awaited> }) => { + if (!data) { + return null; + } + const components = []; + const classes = useClasses(); + + var cid:number = 0 + for (const id in data) { cid = Number(id) } + const [question, answer] = data[cid]; + + components.push( + + Your question + + {question.question} + + My answer + + {answer?.answer} + + My thoughts + + {answer?.thoughts} + + + ); + + return <>{components}; +}; + +export const Chat = () => { + const fetcher = useFetcher>>(); + const classes = useClasses(); + + const submitting = fetcher.state !== "idle"; + const data = fetcher.data; + + const [prompt, setPrompt] = useState(""); + + const onChange: TextareaProps["onChange"] = (_, data) => + setPrompt(() => data.value); + + const onKeyDown: TextareaProps["onKeyDown"] = (e) => { + if (!prompt) { + return; + } + + if (e.key === "Enter" && !e.shiftKey) { + const formData = new FormData(); + formData.append("prompt", prompt); + fetcher.submit(formData, { method: "POST" }); + } + }; + + return ( +
+
+ + <> + Ask questions to the AI model in natural language and get meaningful answers + to help you navigate the conferences sessions and find the best ones for you. + Thanks to Prompt Engineering and Retrieval Augmented Generation (RAG) finding + details and recommendations on what session to attend is easier than ever. Please note that the AI model will remember your questions and answers to improve the quality of the answers and get more context on your requests. + If you want to start from scratch, click here or refresh the page. + + +
+
+ + + } + disabled={submitting || !prompt} + > + Ask + + {submitting && } + +
+
+ {!submitting && data && } +
+
+ ); +}; diff --git a/client/src/pages/Root.tsx b/client/src/pages/Root.tsx new file mode 100644 index 0000000..2a0c472 --- /dev/null +++ b/client/src/pages/Root.tsx @@ -0,0 +1,35 @@ +import { Outlet } from "react-router-dom"; +import { makeStyles, shorthands } from "@fluentui/react-components"; +import { Header } from "../components/Header"; +import { Navigation } from "../components/Navigation"; + +const margin = shorthands.margin("1rem", "3rem", "1rem"); +const useStyles = makeStyles({ + root: { + display: "grid", + gridTemplateRows: "auto 1fr", + gridTemplateAreas: ` + "header" + "main" + `, + height: `calc(100vh - ${margin.marginTop} - ${margin.marginBottom})`, + ...margin, + }, +}); + +export default function Root() { + const classes = useStyles(); + return ( + <> +
+
+
+ +
+
+ +
+
+ + ); +} diff --git a/client/src/pages/Search.tsx b/client/src/pages/Search.tsx new file mode 100644 index 0000000..6e7aefb --- /dev/null +++ b/client/src/pages/Search.tsx @@ -0,0 +1,102 @@ +import { Input, Spinner } from "@fluentui/react-components"; +import { Search24Regular } from "@fluentui/react-icons"; +import { + Form, + LoaderFunction, + useLoaderData, + useNavigation, +} from "react-router-dom"; +import { NoSessions } from "../components/NoSessions"; +import { SessionList } from "../components/SessionsList"; +import type { ErrorInfo, SessionInfo } from "../api/sessions"; +import { getSessions } from "../api/sessions"; +import { FancyText } from "../components/FancyText"; +import { PrimaryButton } from "../components/PrimaryButton"; + +type LoaderData = { + sessions: SessionInfo[]; + searchQuery: string; + isSearch: boolean; + errorInfo: ErrorInfo | null; +}; + +const SEARCH_INPUT_ID = "q"; + +export const loader: LoaderFunction = async ({ request }) => { + const url = new URL(request.url); + const searchQuery = url.searchParams.get(SEARCH_INPUT_ID) ?? ""; + const isSearch = searchQuery !== ""; + + if (!isSearch) { + return { sessions: [] }; + } + + let { sessions, errorInfo } = await getSessions(searchQuery); + if (!Array.isArray(sessions)) { + errorInfo = { errorMessage: "Error: sessions is not an array" }; + sessions = []; + } + return { sessions, searchQuery, isSearch, errorInfo }; +}; + +export default function SessionSearch() { + const { sessions, searchQuery, isSearch, errorInfo } = useLoaderData() as LoaderData; + const navigation = useNavigation(); + + const searching = + navigation.location && + new URLSearchParams(navigation.location.search).has(SEARCH_INPUT_ID); + + return ( + <> + + <> + Use OpenAI to search for interesting sessions. Write the topic you're + interested in, and (up to) the top ten most interesting and related + session will be returned. The search is done using text embeddings and + then using cosine similarity to find the most similar sessions. + + + +
+ {!errorInfo ? ( + "" + ) : ( +

+ {"Error" + errorInfo.errorMessage} +

+ )} + {sessions.length > 0 && } + {sessions.length === 0 && isSearch && } +
+ + ); +} + diff --git a/client/src/site.ts b/client/src/site.ts new file mode 100644 index 0000000..d521ed9 --- /dev/null +++ b/client/src/site.ts @@ -0,0 +1,7 @@ +const siteConfig = { + name: 'VSLive! Las Vegas 2024', + website: 'https://vslive.com/events/las-vegas-2024', + sessionUrl: 'https://vslive.com/Events/Las-Vegas-2024/Sessions/', +} + +export default siteConfig; \ No newline at end of file diff --git a/client/src/user.ts b/client/src/user.ts new file mode 100644 index 0000000..d153ebf --- /dev/null +++ b/client/src/user.ts @@ -0,0 +1,8 @@ +export async function getUserInfo() +{ + const response = await fetch('/.auth/me'); + const payload = await response.json(); + const { clientPrincipal } = payload; + return clientPrincipal; +} + diff --git a/client/staticwebapp.config.json b/client/staticwebapp.config.json new file mode 100644 index 0000000..1e812ac --- /dev/null +++ b/client/staticwebapp.config.json @@ -0,0 +1,5 @@ +{ + "navigationFallback": { + "rewrite": "/" + } +} \ No newline at end of file diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..a7fc6fb --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/client/tsconfig.node.json b/client/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/client/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/client/vite.config.ts b/client/vite.config.ts new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/client/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/database/SessionRecommender/Script.PostDeployment.sql b/database/SessionRecommender/Script.PostDeployment.sql new file mode 100644 index 0000000..2157524 --- /dev/null +++ b/database/SessionRecommender/Script.PostDeployment.sql @@ -0,0 +1,7 @@ +-- This file contains SQL statements that will be executed after the build script. + +alter table [web].[sessions] enable change_tracking with (track_columns_updated = off); +go + +alter table [web].[speakers] enable change_tracking with (track_columns_updated = off); +go diff --git a/database/SessionRecommender/Script.PreDeployment.sql b/database/SessionRecommender/Script.PreDeployment.sql new file mode 100644 index 0000000..ac90e05 --- /dev/null +++ b/database/SessionRecommender/Script.PreDeployment.sql @@ -0,0 +1,15 @@ +if not exists(select * from sys.symmetric_keys where [name] = '##MS_DatabaseMasterKey##') +begin + create master key encryption by password = N'V3RYStr0NGP@ssw0rd!'; +end +go + +if exists(select * from sys.[database_scoped_credentials] where name = '$(OPEN_AI_ENDPOINT)') +begin + drop database scoped credential [$(OPEN_AI_ENDPOINT)]; +end +go + +create database scoped credential [$(OPEN_AI_ENDPOINT)] +with identity = 'HTTPEndpointHeaders', secret = '{"api-key":"$(OPEN_AI_KEY)"}'; +go diff --git a/database/SessionRecommender/Security/db_owner.sql b/database/SessionRecommender/Security/db_owner.sql new file mode 100644 index 0000000..4113d8c --- /dev/null +++ b/database/SessionRecommender/Security/db_owner.sql @@ -0,0 +1 @@ +alter role [db_owner] add member [session_recommender_app]; \ No newline at end of file diff --git a/database/SessionRecommender/Security/session_recommender_app.sql b/database/SessionRecommender/Security/session_recommender_app.sql new file mode 100644 index 0000000..beef9d7 --- /dev/null +++ b/database/SessionRecommender/Security/session_recommender_app.sql @@ -0,0 +1,2 @@ +create user [session_recommender_app] with password = '$(APP_USER_PASSWORD)'; +go diff --git a/database/SessionRecommender/Security/web.sql b/database/SessionRecommender/Security/web.sql new file mode 100644 index 0000000..c9ff839 --- /dev/null +++ b/database/SessionRecommender/Security/web.sql @@ -0,0 +1,4 @@ +CREATE SCHEMA [web] + AUTHORIZATION [dbo]; +GO + diff --git a/database/SessionRecommender/Sequences/global_id.sql b/database/SessionRecommender/Sequences/global_id.sql new file mode 100644 index 0000000..85ee211 --- /dev/null +++ b/database/SessionRecommender/Sequences/global_id.sql @@ -0,0 +1,6 @@ +CREATE SEQUENCE [web].[global_id] + AS INT + START WITH 1 + INCREMENT BY 1; +GO + diff --git a/database/SessionRecommender/StoredProcedures/find_sessions.sql b/database/SessionRecommender/StoredProcedures/find_sessions.sql new file mode 100644 index 0000000..b43cbb9 --- /dev/null +++ b/database/SessionRecommender/StoredProcedures/find_sessions.sql @@ -0,0 +1,146 @@ +create procedure [web].[find_sessions] +@text nvarchar(max), +@top int = 10, +@min_similarity decimal(19,16) = 0.75 +as +if (@text is null) return; + +declare @sid as int = -1; +if (@text like N'***%') begin + set @text = trim(cast(substring(@text, 4, len(@text) - 3) as nvarchar(max))); +end else begin + insert into web.searched_text (searched_text) values (@text); + set @sid = scope_identity(); +end; + +declare @startTime as datetime2(7) = sysdatetime() + +declare @retval int, @response nvarchar(max); +declare @payload nvarchar(max); +set @payload = json_object('input': @text); + +begin try + exec @retval = sp_invoke_external_rest_endpoint + @url = '$(OPEN_AI_ENDPOINT)/openai/deployments/$(OPEN_AI_DEPLOYMENT)/embeddings?api-version=2023-03-15-preview', + @method = 'POST', + @credential = [$(OPEN_AI_ENDPOINT)], + @payload = @payload, + @response = @response output; +end try +begin catch + select + 'SQL' as error_source, + error_number() as error_code, + error_message() as error_message + return; +end catch + +if (@retval != 0) begin + select + 'OPENAI' as error_source, + json_value(@response, '$.result.error.code') as error_code, + json_value(@response, '$.result.error.message') as error_message, + @response as error_response + return; +end; + +declare @endTime1 as datetime2(7) = sysdatetime(); +update [web].[searched_text] set ms_rest_call = datediff(ms, @startTime, @endTime1) where id = @sid; + +with cteVector as +( + select + cast([key] as int) as [vector_value_id], + cast([value] as float) as [vector_value] + from + openjson(json_query(@response, '$.result.data[0].embedding')) +), +cteSimilarSpeakers as +( + select + v2.id as speaker_id, + -- Optimized as per https://platform.openai.com/docs/guides/embeddings/which-distance-function-should-i-use + sum(v1.[vector_value] * v2.[vector_value]) as cosine_similarity + from + cteVector v1 + inner join + web.speakers_embeddings v2 on v1.vector_value_id = v2.vector_value_id + group by + v2.id + +), +cteSimilar as +( + select + v2.id as session_id, + -- Optimized as per https://platform.openai.com/docs/guides/embeddings/which-distance-function-should-i-use + sum(v1.[vector_value] * v2.[vector_value]) as cosine_similarity + from + cteVector v1 + inner join + web.sessions_embeddings v2 on v1.vector_value_id = v2.vector_value_id + group by + v2.id + + union all + + select + ss.session_id, + s.cosine_similarity + from + web.sessions_speakers ss + inner join + cteSimilarSpeakers s on s.speaker_id = ss.speaker_id +), +cteSimilar2 as ( + select + *, + rn = row_number() over (partition by session_id order by cosine_similarity desc) + from + cteSimilar +), +cteSpeakers as +( + select + session_id, + json_query('["' + string_agg(string_escape(full_name, 'json'), '","') + '"]') as speakers + from + web.sessions_speakers ss + inner join + web.speakers sp on sp.id = ss.speaker_id + group by + session_id +) +select top(@top) + a.id, + a.title, + a.abstract, + a.external_id, + a.start_time_PST, + a.end_time_PST, + a.recording_url, + isnull((select top (1) speakers from cteSpeakers where session_id = a.id), '[]') as speakers, + r.cosine_similarity +from + cteSimilar2 r +inner join + web.sessions a on r.session_id = a.id +where + r.cosine_similarity > @min_similarity +and + rn = 1 +order by + r.cosine_similarity desc, a.title asc; + +declare @rc int = @@rowcount; + +declare @endTime2 as datetime2(7) = sysdatetime() +update + [web].[searched_text] +set + ms_vector_search = datediff(ms, @endTime1, @endTime2), + found_sessions = @rc +where + id = @sid +GO + diff --git a/database/SessionRecommender/StoredProcedures/get_sessions_count.sql b/database/SessionRecommender/StoredProcedures/get_sessions_count.sql new file mode 100644 index 0000000..1590d12 --- /dev/null +++ b/database/SessionRecommender/StoredProcedures/get_sessions_count.sql @@ -0,0 +1,5 @@ +create procedure [web].[get_sessions_count] +as +select count(*) as total_sessions from [web].[sessions]; +GO + diff --git a/database/SessionRecommender/StoredProcedures/upsert_session_embeddings.sql b/database/SessionRecommender/StoredProcedures/upsert_session_embeddings.sql new file mode 100644 index 0000000..8d83de5 --- /dev/null +++ b/database/SessionRecommender/StoredProcedures/upsert_session_embeddings.sql @@ -0,0 +1,23 @@ + +create procedure [web].[upsert_session_embeddings] +@id int, +@embeddings nvarchar(max) +as + +set xact_abort on +set transaction isolation level serializable + +begin transaction + + delete from web.sessions_embeddings + where id = @id + + insert into web.sessions_embeddings + select @id, cast([key] as int), cast([value] as float) + from openjson(@embeddings) + + update web.sessions set require_embeddings_update = 0 where id = @id + +commit +GO + diff --git a/database/SessionRecommender/StoredProcedures/upsert_speaker_embeddings.sql b/database/SessionRecommender/StoredProcedures/upsert_speaker_embeddings.sql new file mode 100644 index 0000000..a641df5 --- /dev/null +++ b/database/SessionRecommender/StoredProcedures/upsert_speaker_embeddings.sql @@ -0,0 +1,23 @@ + +create procedure [web].[upsert_speaker_embeddings] +@id int, +@embeddings nvarchar(max) +as + +set xact_abort on +set transaction isolation level serializable + +begin transaction + + delete from web.speakers_embeddings + where id = @id + + insert into web.speakers_embeddings + select @id, cast([key] as int), cast([value] as float) + from openjson(@embeddings) + + update web.speakers set require_embeddings_update = 0 where id = @id + +commit +GO + diff --git a/database/SessionRecommender/Tables/searched_text.sql b/database/SessionRecommender/Tables/searched_text.sql new file mode 100644 index 0000000..980ca8d --- /dev/null +++ b/database/SessionRecommender/Tables/searched_text.sql @@ -0,0 +1,11 @@ +CREATE TABLE [web].[searched_text] ( + [id] INT IDENTITY (1, 1) NOT NULL, + [searched_text] NVARCHAR (MAX) NOT NULL, + [search_datetime] DATETIME2 (7) DEFAULT (sysdatetime()) NOT NULL, + [ms_rest_call] INT NULL, + [ms_vector_search] INT NULL, + [found_sessions] INT NULL, + PRIMARY KEY CLUSTERED ([id] ASC) +); +GO + diff --git a/database/SessionRecommender/Tables/sessions.sql b/database/SessionRecommender/Tables/sessions.sql new file mode 100644 index 0000000..991adac --- /dev/null +++ b/database/SessionRecommender/Tables/sessions.sql @@ -0,0 +1,20 @@ +CREATE TABLE [web].[sessions] ( + [id] INT DEFAULT (NEXT VALUE FOR [web].[global_id]) NOT NULL, + [title] NVARCHAR (200) NOT NULL, + [abstract] NVARCHAR (MAX) NOT NULL, + [external_id] VARCHAR (100) COLLATE Latin1_General_100_BIN2 NULL, + [last_fetched] DATETIME2 (7) NULL, + [start_time_PST] DATETIME2 (0) NULL, + [end_time_PST] DATETIME2 (0) NULL, + [tags] NVARCHAR (MAX) NULL, + [recording_url] VARCHAR (1000) NULL, + [require_embeddings_update] BIT DEFAULT ((0)) NOT NULL, + [embeddings] NVARCHAR (MAX) NULL, + PRIMARY KEY CLUSTERED ([id] ASC), + CHECK (isjson([tags])=(1)), + UNIQUE NONCLUSTERED ([title] ASC) +); +GO + + + diff --git a/database/SessionRecommender/Tables/sessions_embeddings.sql b/database/SessionRecommender/Tables/sessions_embeddings.sql new file mode 100644 index 0000000..4473505 --- /dev/null +++ b/database/SessionRecommender/Tables/sessions_embeddings.sql @@ -0,0 +1,12 @@ +CREATE TABLE [web].[sessions_embeddings] ( + [id] INT NOT NULL, + [vector_value_id] INT NOT NULL, + [vector_value] DECIMAL (19, 16) NOT NULL, + CONSTRAINT fk__sessions_embeddings__sessions FOREIGN KEY ([id]) REFERENCES [web].[sessions] ([id]) +); +GO + +CREATE CLUSTERED COLUMNSTORE INDEX [IXCC] + ON [web].[sessions_embeddings]; +GO + diff --git a/database/SessionRecommender/Tables/sessions_speakers.sql b/database/SessionRecommender/Tables/sessions_speakers.sql new file mode 100644 index 0000000..2fcbb66 --- /dev/null +++ b/database/SessionRecommender/Tables/sessions_speakers.sql @@ -0,0 +1,14 @@ +CREATE TABLE [web].[sessions_speakers] ( + [session_id] INT NOT NULL, + [speaker_id] INT NOT NULL, + PRIMARY KEY CLUSTERED ([session_id] ASC, [speaker_id] ASC), + CONSTRAINT fk__sessions_speakers__sessions FOREIGN KEY ([session_id]) REFERENCES [web].[sessions] ([id]), + CONSTRAINT fk__sessions_speakers__speakers FOREIGN KEY ([speaker_id]) REFERENCES [web].[speakers] ([id]) +); +GO + + +CREATE NONCLUSTERED INDEX [ix2] + ON [web].[sessions_speakers]([speaker_id] ASC); +GO + diff --git a/database/SessionRecommender/Tables/speakers.sql b/database/SessionRecommender/Tables/speakers.sql new file mode 100644 index 0000000..de21003 --- /dev/null +++ b/database/SessionRecommender/Tables/speakers.sql @@ -0,0 +1,12 @@ +CREATE TABLE [web].[speakers] ( + [id] INT DEFAULT (NEXT VALUE FOR [web].[global_id]) NOT NULL, + [external_id] VARCHAR (100) COLLATE Latin1_General_100_BIN2 NULL, + [full_name] NVARCHAR (100) NOT NULL, + [require_embeddings_update] BIT DEFAULT ((0)) NOT NULL, + [embeddings] NVARCHAR (MAX) NULL, + + PRIMARY KEY CLUSTERED ([id] ASC), + UNIQUE NONCLUSTERED ([full_name] ASC), + CHECK (isjson([embeddings])=(1)) +); +GO diff --git a/database/SessionRecommender/Tables/speakers_embeddings.sql b/database/SessionRecommender/Tables/speakers_embeddings.sql new file mode 100644 index 0000000..6818ccd --- /dev/null +++ b/database/SessionRecommender/Tables/speakers_embeddings.sql @@ -0,0 +1,12 @@ +CREATE TABLE [web].[speakers_embeddings] ( + [id] INT NOT NULL, + [vector_value_id] INT NOT NULL, + [vector_value] DECIMAL (19, 16) NOT NULL, + CONSTRAINT fk__speakers_embeddings__speakers FOREIGN KEY ([id]) REFERENCES [web].[speakers] ([id]) +); +GO + +CREATE CLUSTERED COLUMNSTORE INDEX [IXCC] + ON [web].[speakers_embeddings]; +GO + diff --git a/database/SessionRecommender/Views/last_searches.sql b/database/SessionRecommender/Views/last_searches.sql new file mode 100644 index 0000000..f5449bc --- /dev/null +++ b/database/SessionRecommender/Views/last_searches.sql @@ -0,0 +1,15 @@ +create view [web].last_searches +as +select top (100) + id, + searched_text, + switchoffset(search_datetime, '-08:00') as search_datetime_pst, + ms_rest_call, + ms_vector_search, + found_sessions +from + [web].searched_text +order by + id desc +GO + diff --git a/database/SessionRecommender/Views/search_stats.sql b/database/SessionRecommender/Views/search_stats.sql new file mode 100644 index 0000000..33e5b16 --- /dev/null +++ b/database/SessionRecommender/Views/search_stats.sql @@ -0,0 +1,9 @@ +create view [web].search_stats +as +select + count(*) as total_searches, + avg(ms_vector_search) as avg_ms_vector_search +from + [web].searched_text +GO + diff --git a/database/SessionRecommender/session_recommender_v2.sqlproj b/database/SessionRecommender/session_recommender_v2.sqlproj new file mode 100644 index 0000000..598afd5 --- /dev/null +++ b/database/SessionRecommender/session_recommender_v2.sqlproj @@ -0,0 +1,50 @@ + + + + + session_recommender_v2 + {A3BB1F3B-AAAF-4735-A73F-46B26F8B66C0} + Microsoft.Data.Tools.Schema.Sql.SqlAzureV12DatabaseSchemaProvider + 1033, CI + True + + + + 160.0.0 + True + master + + + + + $(SqlCmdVar__4) + APP_USER_PASSWORD + + + $(SqlCmdVar__1) + https://dm-open-ai-3.openai.azure.com/ + + + $(SqlCmdVar__2) + embeddings + + + $(SqlCmdVar__3) + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/database/master.dacpac b/database/master.dacpac new file mode 100644 index 0000000000000000000000000000000000000000..bff78664ab2f57b8ccebb63dfba805ca18db4bc7 GIT binary patch literal 85666 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 literal 0 HcmV?d00001 diff --git a/database/session_recommender_v2.dacpac b/database/session_recommender_v2.dacpac new file mode 100644 index 0000000000000000000000000000000000000000..7edc6bc3675aa3fe5ded904f122ab3ef2a4e9d03 GIT binary patch literal 7267 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 P>#%+;Py4USU|{|SFt29j literal 0 HcmV?d00001 diff --git a/database/setup-database.ps1 b/database/setup-database.ps1 new file mode 100644 index 0000000..7395c98 --- /dev/null +++ b/database/setup-database.ps1 @@ -0,0 +1,26 @@ +Invoke-WebRequest -Uri https://aka.ms/sqlpackage-linux -OutFile sqlpackage.zip + +Expand-Archive ./sqlpackage.zip sqlpackage/ + +chmod a+x ./sqlpackage/sqlpackage + +./sqlpackage/sqlpackage /version + +Write-Host "Downloading dacpacs" + +Invoke-WebRequest -Uri https://github.com/yorek/session_recommender_v2/raw/main/database/session_recommender_v2.dacpac -OutFile session_recommender_v2.dacpac +Invoke-WebRequest -Uri https://github.com/yorek/session_recommender_v2/raw/main/database/master.dacpac -OutFile master.dacpac + +Write-Host "Deploying database to $Env:DBSERVER with name $Env:DBNAME" + +./sqlpackage/sqlpackage ` + /Action:Publish ` + /SourceFile:"session_recommender_v2.dacpac" ` + /TargetDatabaseName:"$Env:DBNAME" ` + /TargetServerName:"$Env:DBSERVER" ` + /TargetUser:"$Env:SQLADMIN" ` + /TargetPassword:"$Env:SQLCMDPASSWORD" ` + /v:OPEN_AI_ENDPOINT="$Env:OPEN_AI_ENDPOINT" ` + /v:OPEN_AI_DEPLOYMENT="$Env:OPEN_AI_DEPLOYMENT" ` + /v:OPEN_AI_KEY="$Env:OPEN_AI_KEY" ` + /v:APP_USER_PASSWORD="$Env:APP_USER_PASSWORD" diff --git a/func/.gitignore b/func/.gitignore new file mode 100644 index 0000000..ff5b00c --- /dev/null +++ b/func/.gitignore @@ -0,0 +1,264 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# Azure Functions localsettings file +local.settings.json + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc \ No newline at end of file diff --git a/func/.vscode/extensions.json b/func/.vscode/extensions.json new file mode 100644 index 0000000..bb76300 --- /dev/null +++ b/func/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "ms-azuretools.vscode-azurefunctions", + "ms-dotnettools.csharp" + ] +} \ No newline at end of file diff --git a/func/.vscode/launch.json b/func/.vscode/launch.json new file mode 100644 index 0000000..894cbe6 --- /dev/null +++ b/func/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to .NET Functions", + "type": "coreclr", + "request": "attach", + "processId": "${command:azureFunctions.pickProcess}" + } + ] +} \ No newline at end of file diff --git a/func/.vscode/settings.json b/func/.vscode/settings.json new file mode 100644 index 0000000..eed4725 --- /dev/null +++ b/func/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "azureFunctions.deploySubpath": "bin/Release/net8.0/publish", + "azureFunctions.projectLanguage": "C#", + "azureFunctions.projectRuntime": "~4", + "debug.internalConsoleOptions": "neverOpen", + "azureFunctions.preDeployTask": "publish (functions)" +} \ No newline at end of file diff --git a/func/.vscode/tasks.json b/func/.vscode/tasks.json new file mode 100644 index 0000000..5199259 --- /dev/null +++ b/func/.vscode/tasks.json @@ -0,0 +1,69 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "clean (functions)", + "command": "dotnet", + "args": [ + "clean", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile" + }, + { + "label": "build (functions)", + "command": "dotnet", + "args": [ + "build", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean (functions)", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": "$msCompile" + }, + { + "label": "clean release (functions)", + "command": "dotnet", + "args": [ + "clean", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "problemMatcher": "$msCompile" + }, + { + "label": "publish (functions)", + "command": "dotnet", + "args": [ + "publish", + "--configuration", + "Release", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "type": "process", + "dependsOn": "clean release (functions)", + "problemMatcher": "$msCompile" + }, + { + "type": "func", + "dependsOn": "build (functions)", + "options": { + "cwd": "${workspaceFolder}/bin/Debug/net8.0" + }, + "command": "host start", + "isBackground": true, + "problemMatcher": "$func-dotnet-watch" + } + ] +} \ No newline at end of file diff --git a/func/ChatHandler.cs b/func/ChatHandler.cs new file mode 100644 index 0000000..6b868cb --- /dev/null +++ b/func/ChatHandler.cs @@ -0,0 +1,123 @@ +using System; +using System.Data; +using System.Text.Json; +using Azure; +using Azure.AI.OpenAI; +using Dapper; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.Logging; +using FromBodyAttribute = Microsoft.Azure.Functions.Worker.Http.FromBodyAttribute; + +namespace SessionRecommender.RequestHandler; + +public record ChatTurn(string userPrompt, string? responseMessage); + +public record FoundSession( + int Id, + string Title, + string Abstract, + double Similarity, + //string RecordingUrl, + string Speakers, + string ExternalId, + DateTimeOffset Start, + DateTimeOffset End +); + +public class ChatHandler(OpenAIClient openAIClient, SqlConnection conn, ILogger logger) +{ + private readonly string _openAIDeploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_GPT_DEPLOYMENT_NAME") ?? "gpt-4"; + + private const string SystemMessage = """ +You are a system assistant who helps users find the right session to watch from the conference, based off the sessions that are provided to you. + +Sessions will be provided in an assistant message in the format of `title|abstract|speakers|start-time|end-time`. You can use this information to help you answer the user's question. +"""; + + [Function("ChatHandler")] + public async Task AskAsync( + [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "ask")] HttpRequest req, + [FromBody] ChatTurn[] history) + { + logger.LogInformation("Retrieving similar sessions..."); + + DynamicParameters p = new(); + p.Add("@text", history.Last().userPrompt); + p.Add("@top", 25); + p.Add("@min_similarity", 0.70); + + using IDataReader foundSessions = await conn.ExecuteReaderAsync("[web].[find_sessions]", commandType: CommandType.StoredProcedure, param: p); + + List sessions = []; + while (foundSessions.Read()) + { + sessions.Add(new( + Id: foundSessions.GetInt32(0), + Title: foundSessions.GetString(1), + Abstract: foundSessions.GetString(2), + ExternalId: foundSessions.GetString(3), + Start: foundSessions.GetDateTime(4), + End: foundSessions.GetDateTime(5), + //RecordingUrl: foundSessions.GetString(6), + Speakers: foundSessions.GetString(7), + Similarity: foundSessions.GetDouble(8) + )); + } + + logger.LogInformation("Calling GPT..."); + + string sessionDescriptions = string.Join("\r", sessions.Select(s => $"{s.Title}|{s.Abstract}|{s.Speakers}|{s.Start}|{s.End}")); + + List messages = [new ChatRequestSystemMessage(SystemMessage)]; + + foreach (ChatTurn turn in history) + { + messages.Add(new ChatRequestUserMessage(turn.userPrompt)); + if (turn.responseMessage is not null) + { + messages.Add(new ChatRequestAssistantMessage(turn.responseMessage)); + } + } + + messages.Add(new ChatRequestUserMessage($@"## Source ## +{sessionDescriptions} +## End ## + +You answer needs to divided in two sections: in the first section you'll add the answer to the question. +In the second section, that must be named exactly '###thoughts###', and you must use the section name as typed, without any changes, you'll write brief thoughts on how you came up with the answer, e.g. what sources you used, what you thought about, etc. +}}")); + + ChatCompletionsOptions options = new(_openAIDeploymentName, messages); + + try + { + var answerPayload = await openAIClient.GetChatCompletionsAsync(options); + var answerContent = answerPayload.Value.Choices[0].Message.Content; + + //logger.LogInformation(answerContent); + + var answerPieces = answerContent + .Replace("###Thoughts###", "###thoughts###", StringComparison.InvariantCultureIgnoreCase) + .Replace("### Thoughts ###", "###thoughts###", StringComparison.InvariantCultureIgnoreCase) + .Split("###thoughts###", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var answer = answerPieces[0]; + var thoughts = answerPieces.Length == 2 ? answerPieces[1] : "No thoughts provided."; + + logger.LogInformation("Done."); + + return new OkObjectResult(new + { + answer, + thoughts + }); + } + catch (Exception e) + { + logger.LogError(e, "Failed to get answer from OpenAI."); + return new BadRequestObjectResult(e.Message); + } + } +} \ No newline at end of file diff --git a/func/Program.cs b/func/Program.cs new file mode 100644 index 0000000..f17786c --- /dev/null +++ b/func/Program.cs @@ -0,0 +1,45 @@ +using Azure; +using Azure.AI.OpenAI; +using Azure.Identity; +using Azure.Security.KeyVault.Secrets; +using Microsoft.Data.SqlClient; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +var host = new HostBuilder() + +.ConfigureServices(services => +{ + Uri openaiEndPoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT") is string value && + Uri.TryCreate(value, UriKind.Absolute, out Uri? uri) && + uri is not null + ? uri + : throw new ArgumentException( + $"Unable to parse endpoint URI"); + + string? apiKey; + var keyVaultEndpoint = Environment.GetEnvironmentVariable("AZURE_KEY_VAULT_ENDPOINT"); + if (!string.IsNullOrEmpty(keyVaultEndpoint)) + { + var openAIKeyName = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); + var keyVaultClient = new SecretClient(vaultUri: new Uri(keyVaultEndpoint), credential: new DefaultAzureCredential()); + apiKey = keyVaultClient.GetSecret(openAIKeyName).Value.Value; + } + else + { + apiKey = Environment.GetEnvironmentVariable("AZURE_OPENAI_KEY"); + } + + OpenAIClient openAIClient = apiKey != null ? + new(openaiEndPoint, new AzureKeyCredential(apiKey)) : + new(openaiEndPoint, new DefaultAzureCredential()); + + services.AddSingleton(openAIClient); + + services.AddTransient((_) => new SqlConnection(Environment.GetEnvironmentVariable("AZURE_SQL_CONNECTION_STRING"))); + +}) +.ConfigureFunctionsWebApplication() +.Build(); + +host.Run(); \ No newline at end of file diff --git a/func/RequestHandler.csproj b/func/RequestHandler.csproj new file mode 100644 index 0000000..2a08e4a --- /dev/null +++ b/func/RequestHandler.csproj @@ -0,0 +1,29 @@ + + + net8.0 + v4 + Exe + enable + enable + preview + f9d76b6e-3000-45fa-8f99-dec6e7819a55 + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + Never + + + diff --git a/func/SessionProcessor.cs b/func/SessionProcessor.cs new file mode 100644 index 0000000..fe69fad --- /dev/null +++ b/func/SessionProcessor.cs @@ -0,0 +1,145 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Data.SqlClient; +using System.Data; +using Dapper; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Extensions.Sql; +using Azure.AI.OpenAI; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace SessionRecommender.RequestHandler; + +public class Item +{ + public required int Id { get; set; } + + [JsonPropertyName("require_embeddings_update")] + public bool RequireEmbeddingsUpdate { get; set; } + + public override bool Equals(object obj) + { + if (obj is not Item that) return false; + return Id == that.Id; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } + + public override string ToString() + { + return Id.ToString(); + } +} + +public class Session: Item +{ + public string? Title { get; set; } + + public string? Abstract { get; set; } +} + +public class Speaker: Item +{ + [JsonPropertyName("full_name")] + public string? FullName { get; set; } +} + +public class ChangedItem: Item +{ + public SqlChangeOperation Operation { get; set; } + public required string Payload { get; set; } +} + +public class SessionProcessor(OpenAIClient openAIClient, SqlConnection conn, ILogger logger) +{ + private readonly string _openAIDeploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME") ?? "embeddings"; + + [Function(nameof(SessionTrigger))] + public async Task SessionTrigger( + [SqlTrigger("[web].[sessions]", "AZURE_SQL_CONNECTION_STRING")] + IReadOnlyList> changes + ) + { + var ci = from c in changes + where c.Operation != SqlChangeOperation.Delete + where c.Item.RequireEmbeddingsUpdate == true + select new ChangedItem() { + Id = c.Item.Id, + Operation = c.Operation, + Payload = c.Item.Title + ':' + c.Item.Abstract + }; + + await ProcessChanges(ci, "web.sessions", "web.upsert_session_embeddings", logger); + } + + [Function(nameof(SpeakerTrigger))] + public async Task SpeakerTrigger( + [SqlTrigger("[web].[speakers]", "AZURE_SQL_CONNECTION_STRING")] + IReadOnlyList> changes + ) + { + var ci = from c in changes + where c.Operation != SqlChangeOperation.Delete + where c.Item.RequireEmbeddingsUpdate == true + select new ChangedItem() { + Id = c.Item.Id, + Operation = c.Operation, + Payload = c.Item.FullName ?? "", + RequireEmbeddingsUpdate = c.Item.RequireEmbeddingsUpdate + }; + + await ProcessChanges(ci, "web.speakers", "web.upsert_speaker_embeddings", logger); + } + + private async Task ProcessChanges(IEnumerable changes, string referenceTable, string upsertStoredProcedure, ILogger logger) + { + var ct = changes.Count(); + if (ct == 0) { + logger.LogInformation($"No useful changes detected on {referenceTable} table."); + return; + } + + logger.LogInformation($"There are {ct} changes that requires processing on table {referenceTable}."); + + foreach (var change in changes) + { + logger.LogInformation($"[{referenceTable}:{change.Id}] Processing change for operation: " + change.Operation.ToString()); + + var attempts = 0; + var embeddingsReceived = false; + while (attempts < 3) + { + attempts++; + + logger.LogInformation($"[{referenceTable}:{change.Id}] Attempt {attempts}/3 to get embeddings."); + + var response = await openAIClient.GetEmbeddingsAsync( + new EmbeddingsOptions(_openAIDeploymentName, [change.Payload]) + ); + + var e = response.Value.Data[0].Embedding; + await conn.ExecuteAsync( + upsertStoredProcedure, + commandType: CommandType.StoredProcedure, + param: new + { + @id = change.Id, + @embeddings = JsonSerializer.Serialize(e) + }); + embeddingsReceived = true; + + logger.LogInformation($"[{referenceTable}:{change.Id}] Done."); + + break; + } + if (!embeddingsReceived) + { + logger.LogInformation($"[{referenceTable}:{change.Id}] Failed to get embeddings."); + } + } + } +} + diff --git a/func/host.json b/func/host.json new file mode 100644 index 0000000..f93b3ee --- /dev/null +++ b/func/host.json @@ -0,0 +1,12 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + }, + "enableLiveMetricsFilters": true + } + } +} \ No newline at end of file diff --git a/func/local.settings copy.json b/func/local.settings copy.json new file mode 100644 index 0000000..7adeae7 --- /dev/null +++ b/func/local.settings copy.json @@ -0,0 +1,12 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "AZURE_SQL_CONNECTION_STRING": "Server=dmmssqlsrv9.database.windows.net;Initial Catalog=SessionRecommender_VSLive2024_LAS;Persist Security Info=False;User ID=session_recommender_app;Password=unEno!h5!&*KP420xds&@P901afb$^M;MultipleActiveResultSets=False;Encrypt=True;Connection Timeout=30;", + "AZURE_OPENAI_ENDPOINT": "https://dm-open-ai-3.openai.azure.com/", + "AZURE_OPENAI_KEY": "a03f26dd4cbf42c5a6ed11c72daf8bdf", + "AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME": "embeddings", + "AZURE_OPENAI_GPT_DEPLOYMENT_NAME": "gpt-4" + } +} \ No newline at end of file diff --git a/func/local.settings.json.sample b/func/local.settings.json.sample new file mode 100644 index 0000000..adc93a0 --- /dev/null +++ b/func/local.settings.json.sample @@ -0,0 +1,12 @@ +{ + "IsEncrypted": false, + "Values": { + "AzureWebJobsStorage": "UseDevelopmentStorage=true", + "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", + "AZURE_SQL_CONNECTION_STRING": "Server=.database.windows.net;Initial Catalog=;Persist Security Info=False;User ID=session_recommender_app;Password=unEno!h5!&*KP420xds&@P901afb$^M;MultipleActiveResultSets=False;Encrypt=True;Connection Timeout=30;", + "AZURE_OPENAI_ENDPOINT": "https://.openai.azure.com/", + "AZURE_OPENAI_KEY": "", + "AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME": "embeddings", + "AZURE_OPENAI_GPT_DEPLOYMENT_NAME": "gpt" + } +} \ No newline at end of file diff --git a/infra/abbreviations.json b/infra/abbreviations.json new file mode 100644 index 0000000..0126e6d --- /dev/null +++ b/infra/abbreviations.json @@ -0,0 +1,136 @@ +{ + "analysisServicesServers": "as", + "apiManagementService": "apim-", + "appConfigurationConfigurationStores": "appcs-", + "appManagedEnvironments": "cae-", + "appContainerApps": "ca-", + "authorizationPolicyDefinitions": "policy-", + "automationAutomationAccounts": "aa-", + "blueprintBlueprints": "bp-", + "blueprintBlueprintsArtifacts": "bpa-", + "cacheRedis": "redis-", + "cdnProfiles": "cdnp-", + "cdnProfilesEndpoints": "cdne-", + "cognitiveServicesAccounts": "cog-", + "cognitiveServicesFormRecognizer": "cog-fr-", + "cognitiveServicesTextAnalytics": "cog-ta-", + "computeAvailabilitySets": "avail-", + "computeCloudServices": "cld-", + "computeDiskEncryptionSets": "des", + "computeDisks": "disk", + "computeDisksOs": "osdisk", + "computeGalleries": "gal", + "computeSnapshots": "snap-", + "computeVirtualMachines": "vm", + "computeVirtualMachineScaleSets": "vmss-", + "containerInstanceContainerGroups": "ci", + "containerRegistryRegistries": "cr", + "containerServiceManagedClusters": "aks-", + "databricksWorkspaces": "dbw-", + "dataFactoryFactories": "adf-", + "dataLakeAnalyticsAccounts": "dla", + "dataLakeStoreAccounts": "dls", + "dataMigrationServices": "dms-", + "dBforMySQLServers": "mysql-", + "dBforPostgreSQLServers": "psql-", + "devicesIotHubs": "iot-", + "devicesProvisioningServices": "provs-", + "devicesProvisioningServicesCertificates": "pcert-", + "documentDBDatabaseAccounts": "cosmos-", + "eventGridDomains": "evgd-", + "eventGridDomainsTopics": "evgt-", + "eventGridEventSubscriptions": "evgs-", + "eventHubNamespaces": "evhns-", + "eventHubNamespacesEventHubs": "evh-", + "hdInsightClustersHadoop": "hadoop-", + "hdInsightClustersHbase": "hbase-", + "hdInsightClustersKafka": "kafka-", + "hdInsightClustersMl": "mls-", + "hdInsightClustersSpark": "spark-", + "hdInsightClustersStorm": "storm-", + "hybridComputeMachines": "arcs-", + "insightsActionGroups": "ag-", + "insightsComponents": "appi-", + "keyVaultVaults": "kv-", + "kubernetesConnectedClusters": "arck", + "kustoClusters": "dec", + "kustoClustersDatabases": "dedb", + "logicIntegrationAccounts": "ia-", + "logicWorkflows": "logic-", + "machineLearningServicesWorkspaces": "mlw-", + "managedIdentityUserAssignedIdentities": "id-", + "managementManagementGroups": "mg-", + "migrateAssessmentProjects": "migr-", + "networkApplicationGateways": "agw-", + "networkApplicationSecurityGroups": "asg-", + "networkAzureFirewalls": "afw-", + "networkBastionHosts": "bas-", + "networkConnections": "con-", + "networkDnsZones": "dnsz-", + "networkExpressRouteCircuits": "erc-", + "networkFirewallPolicies": "afwp-", + "networkFirewallPoliciesWebApplication": "waf", + "networkFirewallPoliciesRuleGroups": "wafrg", + "networkFrontDoors": "fd-", + "networkFrontdoorWebApplicationFirewallPolicies": "fdfp-", + "networkLoadBalancersExternal": "lbe-", + "networkLoadBalancersInternal": "lbi-", + "networkLoadBalancersInboundNatRules": "rule-", + "networkLocalNetworkGateways": "lgw-", + "networkNatGateways": "ng-", + "networkNetworkInterfaces": "nic-", + "networkNetworkSecurityGroups": "nsg-", + "networkNetworkSecurityGroupsSecurityRules": "nsgsr-", + "networkNetworkWatchers": "nw-", + "networkPrivateDnsZones": "pdnsz-", + "networkPrivateLinkServices": "pl-", + "networkPublicIPAddresses": "pip-", + "networkPublicIPPrefixes": "ippre-", + "networkRouteFilters": "rf-", + "networkRouteTables": "rt-", + "networkRouteTablesRoutes": "udr-", + "networkTrafficManagerProfiles": "traf-", + "networkVirtualNetworkGateways": "vgw-", + "networkVirtualNetworks": "vnet-", + "networkVirtualNetworksSubnets": "snet-", + "networkVirtualNetworksVirtualNetworkPeerings": "peer-", + "networkVirtualWans": "vwan-", + "networkVpnGateways": "vpng-", + "networkVpnGatewaysVpnConnections": "vcn-", + "networkVpnGatewaysVpnSites": "vst-", + "notificationHubsNamespaces": "ntfns-", + "notificationHubsNamespacesNotificationHubs": "ntf-", + "operationalInsightsWorkspaces": "log-", + "portalDashboards": "dash-", + "powerBIDedicatedCapacities": "pbi-", + "purviewAccounts": "pview-", + "recoveryServicesVaults": "rsv-", + "resourcesResourceGroups": "rg-", + "searchSearchServices": "srch-", + "serviceBusNamespaces": "sb-", + "serviceBusNamespacesQueues": "sbq-", + "serviceBusNamespacesTopics": "sbt-", + "serviceEndPointPolicies": "se-", + "serviceFabricClusters": "sf-", + "signalRServiceSignalR": "sigr", + "sqlManagedInstances": "sqlmi-", + "sqlServers": "sql-", + "sqlServersDataWarehouse": "sqldw-", + "sqlServersDatabases": "sqldb-", + "sqlServersDatabasesStretch": "sqlstrdb-", + "storageStorageAccounts": "st", + "storageStorageAccountsVm": "stvm", + "storSimpleManagers": "ssimp", + "streamAnalyticsCluster": "asa-", + "synapseWorkspaces": "syn", + "synapseWorkspacesAnalyticsWorkspaces": "synw", + "synapseWorkspacesSqlPoolsDedicated": "syndp", + "synapseWorkspacesSqlPoolsSpark": "synsp", + "timeSeriesInsightsEnvironments": "tsi-", + "webServerFarms": "plan-", + "webSitesAppService": "app-", + "webSitesAppServiceEnvironment": "ase-", + "webSitesFunctions": "func-", + "webStaticSites": "stapp-" + } + \ No newline at end of file diff --git a/infra/app/functions.bicep b/infra/app/functions.bicep new file mode 100644 index 0000000..5b603a0 --- /dev/null +++ b/infra/app/functions.bicep @@ -0,0 +1,49 @@ +param functionAppName string +param location string = resourceGroup().location +param hostingPlanId string +param storageAccountName string +@secure() +param sqlConnectionString string +param keyVaultName string +param tags object = {} +param applicationInsightsConnectionString string +param useKeyVault bool +param keyVaultEndpoint string = '' +@secure() +param openAIEndpoint string +param openAIKeyName string +param openAIName string +param openAIEmebddingDeploymentName string = 'embeddings' +param openAIGPTDeploymentName string = 'gpt' + +module functionApp '../core/host/functions.bicep' = { + name: 'function1' + params: { + location: location + alwaysOn: false + tags: union(tags, { 'azd-service-name': 'functionapp' }) + kind: 'functionapp' + keyVaultName: keyVaultName + appServicePlanId: hostingPlanId + name: functionAppName + runtimeName: 'dotnet-isolated' + runtimeVersion: '8.0' + storageAccountName: storageAccountName + appSettings: { + WEBSITE_CONTENTSHARE: toLower(functionAppName) + WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: 'DefaultEndpointsProtocol=https;AccountName=${storageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.Storage/storageAccounts', storageAccountName), '2022-05-01').keys[0].value}' + APPLICATIONINSIGHTS_CONNECTION_STRING: applicationInsightsConnectionString + AZURE_SQL_CONNECTION_STRING: sqlConnectionString + AZURE_OPENAI_ENDPOINT: openAIEndpoint + AZURE_OPENAI_KEY: useKeyVault ? openAIKeyName : listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', openAIName), '2023-05-01').key1 + AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT_NAME: openAIEmebddingDeploymentName + AZURE_OPENAI_GPT_DEPLOYMENT_NAME: openAIGPTDeploymentName + AZURE_KEY_VAULT_ENDPOINT: useKeyVault ? keyVaultEndpoint : '' + } + } +} + +output functionAppResourceId string = functionApp.outputs.functionAppResourceId +output name string = functionApp.outputs.name +output uri string = functionApp.outputs.uri +output identityPrincipalId string = functionApp.outputs.identityPrincipalId diff --git a/infra/app/openai.bicep b/infra/app/openai.bicep new file mode 100644 index 0000000..f9af5d8 --- /dev/null +++ b/infra/app/openai.bicep @@ -0,0 +1,57 @@ +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' +} +param keyVaultName string +param useKeyVault bool + +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 + } +}] + +resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' existing = if (useKeyVault) { + name: keyVaultName +} + +resource openAIKey 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = if (useKeyVault) { + parent: keyVault + name: 'openAIKey' + properties: { + value: account.listKeys().key1 + } +} + +output endpoint string = account.properties.endpoint +output id string = account.id +output name string = account.name +output openAIKeyName string = openAIKey.name diff --git a/infra/app/sqlserver.bicep b/infra/app/sqlserver.bicep new file mode 100644 index 0000000..82d662f --- /dev/null +++ b/infra/app/sqlserver.bicep @@ -0,0 +1,114 @@ +metadata description = 'Creates an Azure SQL Server instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +param databaseName string +param principalId string +param connectionStringKey string = 'AZURE-SQL-CONNECTION-STRING' + +param sqlAdmin string = 'sqlAdmin' +@secure() +param sqlAdminPassword string + +param appUser string = 'session_recommender_app' +@secure() +param appUserPassword string + +@secure() +param openAIEndpoint string +param openAIDeploymentName string +param openAIServiceName 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.0' + endIpAddress: '0.0.0.0' + } + } + + resource symbolicname 'administrators@2022-05-01-preview' = { + name: 'ActiveDirectory' + properties: { + administratorType: 'ActiveDirectory' + login: 'EntraAdmin' + sid: principalId + tenantId: tenant().tenantId + } + } +} + +resource createDBScript2 'Microsoft.Resources/deploymentScripts@2023-08-01' = { + name: '${name}-createDB-script' + location: location + kind: 'AzurePowerShell' + properties: { + azPowerShellVersion: '10.0' + retentionInterval: 'PT1H' // Retain the script resource for 1 hour after it ends running + timeout: 'PT5M' // Five minutes + cleanupPreference: 'OnSuccess' + environmentVariables: [ + { + name: 'DBNAME' + value: databaseName + } + { + name: 'DBSERVER' + value: sqlServer.properties.fullyQualifiedDomainName + } + { + name: 'SQLCMDPASSWORD' + secureValue: sqlAdminPassword + } + { + name: 'SQLADMIN' + value: sqlAdmin + } + { + name: 'OPEN_AI_ENDPOINT' + value: openAIEndpoint + } + { + name: 'OPEN_AI_DEPLOYMENT' + value: openAIDeploymentName + } + { + name: 'OPEN_AI_KEY' + secureValue: listKeys(resourceId(subscription().subscriptionId, resourceGroup().name, 'Microsoft.CognitiveServices/accounts', openAIServiceName), '2023-05-01').key1 + } + { + name: 'APP_USER_PASSWORD' + secureValue: appUserPassword + } + ] + scriptContent: loadTextContent('../../database/setup-database.ps1') + } +} + +var connectionString = 'Server=${sqlServer.properties.fullyQualifiedDomainName}; Database=${sqlServer::database.name}; User=${appUser}' +output connectionStringKey string = connectionStringKey +output connectionString string = connectionString +output databaseName string = sqlServer::database.name +output name string = sqlServer.name +output id string = sqlServer.id diff --git a/infra/app/staticwebapp.bicep b/infra/app/staticwebapp.bicep new file mode 100644 index 0000000..c84003e --- /dev/null +++ b/infra/app/staticwebapp.bicep @@ -0,0 +1,39 @@ +metadata description = 'Creates an Azure Static Web Apps instance.' +param name string +param location string = resourceGroup().location +param tags object = {} +param sku object = { + name: 'Standard' + tier: 'Standard' +} +param sqlServerLocation string +param sqlServerId string +@secure() +param sqlConnectionString string +param apiResourceId string + +resource web 'Microsoft.Web/staticSites@2022-09-01' = { + name: name + location: location + tags: tags + properties: {} + sku: sku + resource apifunc 'linkedBackends@2022-09-01' = { + name: 'default' + properties: { + backendResourceId: apiResourceId + region: location + } + } + resource dbconn 'databaseConnections@2022-09-01' = { + name: 'default' + properties: { + connectionString: sqlConnectionString + region: sqlServerLocation + resourceId: sqlServerId + } + } +} + +output name string = web.name +output uri string = 'https://${web.properties.defaultHostname}' diff --git a/infra/core/host/appservice-appsettings.bicep b/infra/core/host/appservice-appsettings.bicep new file mode 100644 index 0000000..f4b22f8 --- /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 0000000..6580cd2 --- /dev/null +++ b/infra/core/host/appservice.bicep @@ -0,0 +1,124 @@ +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 basicPublishingCredentialsPoliciesFtp 'basicPublishingCredentialsPolicies' = { + name: 'ftp' + properties: { + allow: false + } + } + + resource basicPublishingCredentialsPoliciesScm 'basicPublishingCredentialsPolicies' = { + name: 'scm' + properties: { + allow: false + } + } +} + +// 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 + 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 } : {}) + } +} + +// 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 +} + +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}' +output functionAppResourceId string = appService.id diff --git a/infra/core/host/appserviceplan.bicep b/infra/core/host/appserviceplan.bicep new file mode 100644 index 0000000..2e37e04 --- /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 0000000..e57fff7 --- /dev/null +++ b/infra/core/host/functions.bicep @@ -0,0 +1,87 @@ +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 +output functionAppResourceId string = functions.outputs.functionAppResourceId diff --git a/infra/core/monitor/applicationinsights-dashboard.bicep b/infra/core/monitor/applicationinsights-dashboard.bicep new file mode 100644 index 0000000..d082e66 --- /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 0000000..4b4d01e --- /dev/null +++ b/infra/core/monitor/applicationinsights.bicep @@ -0,0 +1,30 @@ +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 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 (!empty(dashboardName)) { + 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 0000000..33f9dc2 --- /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/security/keyvault-access.bicep b/infra/core/security/keyvault-access.bicep new file mode 100644 index 0000000..316775f --- /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.bicep b/infra/core/security/keyvault.bicep new file mode 100644 index 0000000..314a1db --- /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/role.bicep b/infra/core/security/role.bicep new file mode 100644 index 0000000..0b30cfd --- /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 new file mode 100644 index 0000000..4b6febb --- /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 new file mode 100644 index 0000000..6284d35 --- /dev/null +++ b/infra/main.bicep @@ -0,0 +1,224 @@ +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 + +@minLength(1) +@description('Primary location for all resources') +param location string + +// Identity +@description('Id of the user or app to assign application roles') +param principalId string + +// OpenAI +param openAIServiceName string = '' +param openAISkuName string = 'S0' +param embeddingDeploymentName string = 'embeddings' +param gptDeploymentName string = 'gpt' + +// Azure SQL +@secure() +@description('SQL Server administrator password') +param sqlAdminPassword string +@secure() +@description('Application user password') +param appUserPassword string +param dbServiceName string = '' +param dbName string = 'session_recommender_v2' + +param keyVaultName string = '' + +param storageAccountName string = '' + +param functionAppName string = '' + +param hostingPlanName string = '' +param staticWebAppName string = '' + +param applicationInsightsName string = '' + +param logAnalyticsName string = '' + +@description('Flag to Use keyvault to store and use keys') +param useKeyVault bool = true + +var abbrs = loadJsonContent('./abbreviations.json') +var resourceToken = toLower(uniqueString(subscription().id, environmentName, location)) +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 'app/openai.bicep' = { + name: 'openai' + scope: rg + params: { + name: !empty(openAIServiceName) ? openAIServiceName : '${abbrs.cognitiveServicesAccounts}${resourceToken}' + location: location + tags: tags + sku: { + name: openAISkuName + } + deployments: [ + { + name: embeddingDeploymentName + model: { + format: 'OpenAI' + name: 'text-embedding-ada-002' + version: '2' + } + capacity: 30 + } + { + name: gptDeploymentName + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0613' + } + capacity: 120 + } + ] + keyVaultName: keyVault.outputs.name + useKeyVault: useKeyVault + } +} + +module database 'app/sqlserver.bicep' = { + name: 'database' + scope: rg + params: { + tags: tags + location: location + appUserPassword: appUserPassword + sqlAdminPassword: sqlAdminPassword + databaseName: dbName + name: !empty(dbServiceName) ? dbServiceName : '${abbrs.sqlServers}catalog-${resourceToken}' + openAIEndpoint: openAI.outputs.endpoint + openAIServiceName: openAI.outputs.name + openAIDeploymentName: embeddingDeploymentName + principalId: principalId + } +} + +module keyVault 'core/security/keyvault.bicep' = { + name: 'keyvault' + scope: rg + params: { + name: !empty(keyVaultName) ? keyVaultName : '${abbrs.keyVaultVaults}${resourceToken}' + location: location + tags: tags + principalId: principalId + } +} + +module hostingPlan 'core/host/appserviceplan.bicep' = { + name: 'hostingPlan' + scope: rg + params: { + tags: tags + location: location + name: !empty(hostingPlanName) ? hostingPlanName : '${abbrs.webServerFarms}${resourceToken}' + sku: { + name: 'Y1' + tier: 'Dynamic' + } + kind: 'linux' + } +} + +module logAnalytics 'core/monitor/loganalytics.bicep' ={ + name: 'logAnalytics' + scope: rg + params: { + name: !empty(logAnalyticsName) ? logAnalyticsName : '${abbrs.insightsComponents}${resourceToken}' + location: location + } +} + +module applicationInsights 'core/monitor/applicationinsights.bicep' = { + name: 'monitoring' + scope: rg + params: { + location: location + tags: tags + name: !empty(applicationInsightsName) ? applicationInsightsName : '${abbrs.insightsComponents}${resourceToken}' + logAnalyticsWorkspaceId: logAnalytics.outputs.id + } +} + +module functionApp 'app/functions.bicep' = { + name: 'function' + scope: rg + params: { + tags: union(tags, { 'azd-service-name': 'functionapp' }) + location: location + storageAccountName: storageAccount.outputs.name + openAIKeyName: useKeyVault ? openAI.outputs.openAIKeyName : '' + functionAppName: !empty(functionAppName) ? functionAppName : '${abbrs.webSitesFunctions}${resourceToken}' + hostingPlanId: hostingPlan.outputs.id + sqlConnectionString: '${database.outputs.connectionString}; Password=${appUserPassword}' + openAIEmebddingDeploymentName: embeddingDeploymentName + openAIGPTDeploymentName: gptDeploymentName + openAIEndpoint: openAI.outputs.endpoint + keyVaultName: keyVault.outputs.name + applicationInsightsConnectionString: applicationInsights.outputs.connectionString + useKeyVault: useKeyVault + openAIName: openAI.outputs.name + keyVaultEndpoint: keyVault.outputs.endpoint + } +} + +module storageAccount 'core/storage/storage-account.bicep' = { + name: 'storage' + scope: rg + params: { + tags: tags + name: !empty(storageAccountName) ? storageAccountName : '${abbrs.storageStorageAccounts}${resourceToken}' + location: location + } +} + +module funcaccess './core/security/keyvault-access.bicep' = if (useKeyVault) { + name: 'web-keyvault-access' + scope: rg + params: { + keyVaultName: keyVault.outputs.name + principalId: functionApp.outputs.identityPrincipalId + } +} + +module web 'app/staticwebapp.bicep' = { + name: 'web' + scope: rg + params: { + name: !empty(staticWebAppName) ? staticWebAppName : '${abbrs.webStaticSites}${resourceToken}' + location: location + tags: union(tags, { 'azd-service-name': 'web' }) + sqlConnectionString: '${database.outputs.connectionString}; Password=${appUserPassword}' + sqlServerId: database.outputs.id + sqlServerLocation: location + apiResourceId: functionApp.outputs.functionAppResourceId + } +} + +output AZURE_SQL_SQLSERVICE_CONNECTION_STRING_KEY string = database.outputs.connectionStringKey +output AZURE_FUNCTIONAPP_NAME string = functionApp.outputs.name +output AZURE_FUNCTIONAPP_ID string = functionApp.outputs.functionAppResourceId +output AZURE_KEY_VAULT_ENDPOINT string = keyVault.outputs.endpoint +output AZURE_KEY_VALUT_NAME string = keyVault.outputs.name +output AZURE_LOCATION string = location +output AZURE_TENANT_ID string = tenant().tenantId +output APPLICATIONINSIGHTS_CONNECTION_STRING string = applicationInsights.outputs.connectionString +output AZURE_STORAGE_NAME string = storageAccount.outputs.name +output AZURE_STATIC_WEB_URL string = web.outputs.uri +output LOG_ANALYTICS_ID string = logAnalytics.outputs.id +output USE_KEY_VAULT bool = useKeyVault diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 0000000..f82b5e1 --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "value": "${AZURE_LOCATION}" + }, + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + }, + "sqlAdminPassword": { + "value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} sqlAdminPassword)" + }, + "appUserPassword": { + "value": "$(secretOrRandomPassword ${AZURE_KEY_VAULT_NAME} appUserPassword)" + }, + "useKeyVault": { + "value": "${USE_KEY_VAULT=true}" + } + } +} \ No newline at end of file diff --git a/swa-cli.config.json b/swa-cli.config.json new file mode 100644 index 0000000..aafbc9f --- /dev/null +++ b/swa-cli.config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://aka.ms/azure/static-web-apps-cli/schema", + "configurations": { + "client": { + "appLocation": "client", + "outputLocation": "dist", + "appBuildCommand": "npm run build", + "run": "npm run dev" + } + } +} diff --git a/swa-db-connections/staticwebapp.database.config.json b/swa-db-connections/staticwebapp.database.config.json new file mode 100644 index 0000000..2389f09 --- /dev/null +++ b/swa-db-connections/staticwebapp.database.config.json @@ -0,0 +1,131 @@ +{ + "$schema": "https://github.com/Azure/data-api-builder/releases/download/v0.9.7/dab.draft.schema.json", + "data-source": { + "database-type": "mssql", + "connection-string": "@env('MSSQL')", + "options": { + "set-session-context": false + } + }, + "runtime": { + "rest": { + "enabled": true, + "path": "/rest", + "request-body-strict": true + }, + "graphql": { + "enabled": true, + "path": "/graphql", + "allow-introspection": true + }, + "host": { + "cors": { + "origins": [ + "*" + ], + "allow-credentials": false + }, + "authentication": { + "provider": "StaticWebApps" + }, + "mode": "development" + } + }, + "entities": { + "FindRelatedSessions": { + "source": { + "object": "web.find_sessions", + "type": "stored-procedure", + "parameters": { + "text": "", + "top": 10, + "min_similarity": 0.75 + } + }, + "graphql": { + "enabled": false, + "operation": "query" + }, + "rest": { + "enabled": true, + "path": "/find", + "methods": [ + "post" + ] + }, + "permissions": [ + { + "role": "anonymous", + "actions": [ + { + "action": "execute" + } + ] + } + ] + }, + "GetSessionsCount": { + "source": { + "object": "web.get_sessions_count", + "type": "stored-procedure" + }, + "graphql": { + "enabled": false, + "operation": "query" + }, + "rest": { + "enabled": true, + "path": "/sessions-count", + "methods": [ + "get" + ] + }, + "permissions": [ + { + "role": "anonymous", + "actions": [ + { + "action": "execute" + } + ] + } + ] + }, + "Session": { + "source": { + "object": "web.sessions", + "type": "table" + }, + "graphql": { + "enabled": true, + "type": { + "singular": "Session", + "plural": "Sessions" + } + }, + "rest": { + "enabled": true, + "path": "/sessions" + }, + "permissions": [ + { + "role": "anonymous", + "actions": [ + { + "action": "read" + } + ] + }, + { + "role": "authenticated", + "actions": [ + { + "action": "*" + } + ] + } + + ] + } + } +} \ No newline at end of file From bc969735c8c60ca806d73b795732f0503b440ae7 Mon Sep 17 00:00:00 2001 From: Davide Mauri Date: Tue, 12 Mar 2024 14:16:35 -0700 Subject: [PATCH 2/4] fixed urls --- database/setup-database.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/setup-database.ps1 b/database/setup-database.ps1 index 7395c98..466fa8f 100644 --- a/database/setup-database.ps1 +++ b/database/setup-database.ps1 @@ -8,8 +8,8 @@ chmod a+x ./sqlpackage/sqlpackage Write-Host "Downloading dacpacs" -Invoke-WebRequest -Uri https://github.com/yorek/session_recommender_v2/raw/main/database/session_recommender_v2.dacpac -OutFile session_recommender_v2.dacpac -Invoke-WebRequest -Uri https://github.com/yorek/session_recommender_v2/raw/main/database/master.dacpac -OutFile master.dacpac +Invoke-WebRequest -Uri https://github.com/azure-samples/azure-sql-db-session-recommender-v2/raw/main/database/session_recommender_v2.dacpac -OutFile session_recommender_v2.dacpac +Invoke-WebRequest -Uri https://github.com/azure-samples/azure-sql-db-session-recommender-v2/raw/main/database/master.dacpac -OutFile master.dacpac Write-Host "Deploying database to $Env:DBSERVER with name $Env:DBNAME" From d8b8ca8e20aa1665e2369e8ee23544c501c3acaf Mon Sep 17 00:00:00 2001 From: Davide Mauri Date: Tue, 12 Mar 2024 14:16:44 -0700 Subject: [PATCH 3/4] updated readme --- README.md | 145 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 134 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index bd5e086..46433b3 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,119 @@ -Coming Soon! +--- +page_type: sample +languages: +- azdeveloper +- csharp +- sql +- tsql +- javascript +- html +- bicep +products: +- azure-functions +- azure-sql-database +- static-web-apps +- sql-server +- azure-sql-managed-instance +- azure-sqlserver-vm +- dotnet +- azure-openai +urlFragment: azure-sql-db-session-recommender +name: Session Recommender using Azure SQL DB, Open AI and Vector Search +description: Build a session recommender using Jamstack and Event-Driven architecture, using Azure SQL DB to store and search vectors embeddings generated using OpenAI +--- + -In the meantime take a look at v1 of this project: +# Session Assistant Sample - Retrieveal Augmented Generation with Azure SQL DB and OpenAI -- https://sessionfinder.dotnetconf.net/ -- https://github.com/Azure-Samples/azure-sql-db-session-recommender +This repository is a evoution of the [Session Recommender](https://github.com/azure-samples/azure-sql-db-session-recommender) sample. In addition to vector search, also Retrival Augmented Generation (RAG) is used to generate the response to the user query. If you are completely new to this topic, you may want to start there, and then come back here. -Coming Soon! +![Architecture Diagram](./_docs/session-recommender-architecture.png) -In the meantime take a look at v1 of this project: +A session recommender built using -- https://sessionfinder.dotnetconf.net/ -- https://github.com/Azure-Samples/azure-sql-db-session-recommender +- [Azure Static Web Apps](https://learn.microsoft.com/en-us/azure/static-web-apps/overview) +- [Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/) +- [Azure Functions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview?pivots=programming-language-csharp) +- [Azure SQL Database](https://www.sqlservercentral.com/articles/the-sql-developer-experience-beyond-rdbms) +- [Data API builder](https://aka.ms/dab) -# Session Recommender V2 +For more details on the solution check also the following articles: -Coming soon... +- [How I built a session recommender in 1 hour using Open AI](https://dev.to/azure/how-i-built-a-session-recommender-in-1-hour-using-open-ai-5419) +- [Vector Similarity Search with Azure SQL database and OpenAI](https://devblogs.microsoft.com/azure-sql/vector-similarity-search-with-azure-sql-database-and-openai/) +# Deploy 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. + +## Prerequisites + +- Install [AZD CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd). +- Install [.NET SDK](https://dotnet.microsoft.com/download). +- Install [Node.js](https://nodejs.org/download/). +- Install [SWA CLI](https://azure.github.io/static-web-apps-cli/docs/use/install#installing-the-cli). + +## Install AZD CLI + +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 + +```bash +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. + +## Authenticate with Azure + +Make sure AZD CLI can access Azure resources. You can use the following command to log in to Azure: + +```bash +azd auth login ``` +## Initialize the template + +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). + +```bash +azd init -t Azure-Samples/azure-sql-db-session-recommender-v2 +``` + +Enter an environment name. + +## Deploy the sample + +Run `azd up` to provision all the resources to Azure and deploy the code to those resources. + +```bash +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. + +**Note**: Make sure to pick a region where all services are available like, for example, *West Europe* or *East US 2* + +## GitHub Actions + +Using the Azure Developer CLI, you can setup your pipelines, monitor your application, test and debug locally. + +```bash +azd pipeline config +``` + +## Test the solution + +Add a new row to the `Sessions` table using the following SQL statement (you can use tools like [Azure Data Studio](https://learn.microsoft.com/en-us/azure-data-studio/quickstart-sql-database) or [SQL Server Management Studio](https://learn.microsoft.com/en-us/azure/azure-sql/database/connect-query-ssms?view=azuresql) to connect to the database. No need to install them if you don't want. In that case you can use the [SQL Editor in the Azure Portal](https://learn.microsoft.com/en-us/azure/azure-sql/database/connect-query-portal?view=azuresql)): + +```sql insert into web.sessions (title, abstract, external_id, start_time_PST, end_time_PST, require_embeddings_update) values @@ -30,10 +125,34 @@ values '2024-03-10 11:00:00', 1 ) +``` + +immediately the deployed Azure Function will get executed in response to the `INSERT` statement. The Azure Function will call the OpenAI service to generate the text embedding for the session title and abstract, and then store the embedding in the database, specifically in the `web.sessions_embeddings` table. +```sql +select * from web.sessions_embeddings ``` +You can now open the URL associated with the created Static Web App to see the session recommender in action. You can get the URL from the Static Web App overview page in the Azure portal. + +![Website running](./_docs/session-recommender.png) + +## Run the solution locally + +The whole solution can be executed locally, using [Static Web App CLI](https://github.com/Azure/static-web-apps-cli) and [Azure Function CLI](https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=windows%2Cisolated-process%2Cnode-v4%2Cpython-v2%2Chttp-trigger%2Ccontainer-apps&pivots=programming-language-csharp). + +Install the required node packages needed by the fronted: + +```bash +cd client +npm install ``` + +once finished, create a `./func/local.settings.json` and `.env` starting from provided samples files, and fill out the settings using the correct values for your environment. + +From the sample root folder run: + +```bash swa start ./client --api-location ./func --data-api-location ./swa-db-connections ``` @@ -42,4 +161,8 @@ swa start ./client --api-location ./func --data-api-location ./swa-db-connection The solution uses Fluent UI for the UI components. The Fluent UI is a collection of UX frameworks from Microsoft that provides a consistent design language for web, mobile, and desktop applications. More details about Fluent UI can be found at the following links: - https://github.com/microsoft/fluentui -- https://react.fluentui.dev/ \ No newline at end of file +- https://react.fluentui.dev/ + +## Credits + +Thanks a lot to [Aaron Powell](https://www.aaron-powell.com/) for having helped in building the RAG sample, doing a complete UI revamp using the Fluent UI and for the implementaiton of the `ask` endpoint. \ No newline at end of file From 8a51a7e8735850dbd71a711b4c86d4ca8392cbee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Mar 2024 21:17:19 +0000 Subject: [PATCH 4/4] Bump @babel/traverse from 7.23.0 to 7.24.0 in /client Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.23.0 to 7.24.0. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.24.0/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] --- client/package-lock.json | 58 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 6dfe1fc..dd95fcd 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -57,12 +57,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -109,12 +109,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -238,9 +238,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -279,9 +279,9 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -293,9 +293,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -360,20 +360,20 @@ } }, "node_modules/@babel/traverse": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz", - "integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -381,12 +381,12 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" },