diff --git a/.github/workflows/client_tags.yml b/.github/workflows/client_tags.yml new file mode 100644 index 00000000..17f1773a --- /dev/null +++ b/.github/workflows/client_tags.yml @@ -0,0 +1,95 @@ +name: Build and Publish package on tags + +on: + push: + tags: + - 'v*' + +env: + NODE_VERSION: '16.x' + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '16.x' + registry-url: 'https://npm.pkg.github.com' + + - name: Install dependencies and build + run: cd ./client/ && npm ci && npm run build + + - name: Prepare Artifact + run: | + cd ./client/ + mkdir artifact + cp LICENSE README.md package.json artifact + cp -r dist artifact/dist + + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: package + path: client/artifact + + publish-to-npm-pkg-github-com: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + needs: [ build ] + + steps: + - name: Download Artifact + uses: actions/download-artifact@v2 + with: + name: package + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + registry-url: 'https://npm.pkg.github.com' + + - name: Publish package to npm.pkg.github.com + run: cd ./client/ && npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + publish-to-registry-npmjs-org: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + needs: [ build ] + + steps: + - name: Download Artifact + uses: actions/download-artifact@v2 + with: + name: package + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: ${{ env.NODE_VERSION }} + registry-url: 'https://registry.npmjs.org' + + - name: Replace npm.pkg.github.com with registry.npmjs.org + run: sed -i 's/npm\.pkg\.github\.com/registry\.npmjs\.org/' client/package.json + + - name: Publish package to registry.npmjs.org + run: cd ./client/ &&npm publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} diff --git a/.github/workflows/pull_requests.yml b/.github/workflows/pull_requests.yml index f0740d0d..e5859ab6 100644 --- a/.github/workflows/pull_requests.yml +++ b/.github/workflows/pull_requests.yml @@ -6,7 +6,7 @@ on: - master jobs: - build-image: + build-server: runs-on: ubuntu-latest permissions: contents: read @@ -18,4 +18,22 @@ jobs: - name: Build Docker image uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc with: - context: . + context: ./server + + build-client: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '16.x' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies and build + run: cd ./client && npm install && npm run build diff --git a/.github/workflows/build.yml b/.github/workflows/server_build.yml similarity index 97% rename from .github/workflows/build.yml rename to .github/workflows/server_build.yml index 4c207153..4efda2e3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/server_build.yml @@ -38,7 +38,7 @@ jobs: - name: Build and push Docker image uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc with: - context: . + context: ./server/ push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/build_variants.yml b/.github/workflows/server_build_variants.yml similarity index 98% rename from .github/workflows/build_variants.yml rename to .github/workflows/server_build_variants.yml index e104e164..14000cf7 100644 --- a/.github/workflows/build_variants.yml +++ b/.github/workflows/server_build_variants.yml @@ -46,7 +46,7 @@ jobs: - name: Build and push Docker image uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc with: - context: . + context: ./server/ file: ${{ matrix.dockerfile }} push: true tags: ${{ steps.meta.outputs.tags }} diff --git a/.gitignore b/.gitignore index 6784a149..a6a112b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,36 @@ -runtime/fonts/* -!runtime/fonts/.gitkeep +# OS files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db -runtime/icon-theme/* -!runtime/icon-theme/.gitkeep +# Log/Temp files +.tmp/ +tmp/ +*.tmp +.logs/ +logs/ +*.log +core +.build -plugins/* -!plugins/.gitkeep +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# TypeScript incremental compilation cache +*.tsbuildinfo + +# Node modules +node_modules +dist +bin + +# Environment files +*.env +.env.development diff --git a/.vscode/settings.json b/.vscode/settings.json index 45ab0ed9..fb0c7984 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,5 @@ { "go.inferGopath": false, - "go.autocompleteUnimportedPackages": true, "go.delveConfig": { "useApiV1": false }, @@ -9,5 +8,14 @@ "editor.codeActionsOnSave": { "source.organizeImports": "explicit" } - } + }, + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.detectIndentation": false, + "files.encoding": "utf8", + "files.eol": "\n", + "editor.formatOnSave": false, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, } diff --git a/LICENSE b/LICENSE index b9454a1f..e9480337 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,8 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2020 Nurdism , 2020- https://github.com/m1k1o + Copyright 2020 Nurdism + Copyright 2020-2024 m1k1o Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 2426f48d..e564079a 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,125 @@ Notable differences to the [m1k1o/neko](https://github.com/m1k1o/neko) are: ## Docs *TBD.* + +# neko-client +Connect to [demodesk/neko](https://github.com/demodesk/neko) backend with self contained vue component. + +For **community edition** neko with GUI and _plug & play_ deployment visit [m1k1o/neko](https://github.com/m1k1o/neko). + +## Installation +Code is published to public NPM registry and GitHub npm repository. + +```bash +# npm command +npm i @demodesk/neko +# yarn command +yarn add @demodesk/neko +``` + +### Build + +You can set keyboard provider at build time, either `novnc` or the default `guacamole`. + +```bash +# by default uses guacamole keyboard +npm run build +# uses novnc keyboard +KEYBOARD=novnc npm run build +``` + +## Recommended IDE Setup + +[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). + +## Type Support for `.vue` Imports in TS + +TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types. + +## Customize configuration + +See [Vite Configuration Reference](https://vitejs.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Type-Check, Compile and Minify for Production + +```sh +npm run build +``` + +### Run Unit Tests with [Vitest](https://vitest.dev/) + +```sh +npm run test:unit +``` + +### Run End-to-End Tests with [Playwright](https://playwright.dev) + +```sh +# Install browsers for the first run +npx playwright install + +# When testing on CI, must build the project first +npm run build + +# Runs the end-to-end tests +npm run test:e2e +# Runs the tests only on Chromium +npm run test:e2e -- --project=chromium +# Runs the tests of a specific file +npm run test:e2e -- tests/example.spec.ts +# Runs the tests in debug mode +npm run test:e2e -- --debug +``` + +### Lint with [ESLint](https://eslint.org/) + +```sh +npm run lint +``` + + +### Example +API consists of accessing Vue reactive state, calling various methods and subscribing to events. Simple usage: + +```html + + + + + + + +
+ +
+ + +``` diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs new file mode 100644 index 00000000..b07cee20 --- /dev/null +++ b/client/.eslintrc.cjs @@ -0,0 +1,26 @@ +/* eslint-env node */ +require('@rushstack/eslint-patch/modern-module-resolution') + +module.exports = { + root: true, + 'extends': [ + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/eslint-config-typescript', + '@vue/eslint-config-prettier/skip-formatting' + ], + overrides: [ + { + files: [ + 'e2e/**/*.{test,spec}.{js,ts,jsx,tsx}' + ], + 'extends': [ + 'plugin:playwright/recommended' + ] + } + ], + parserOptions: { + ecmaVersion: 'latest' + }, + ignorePatterns: ["**/*.js"] +} diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 00000000..5117716a --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,7 @@ +test-results/ +playwright-report/ + +/src/page/plugins/* +!/src/page/plugins/filetransfer +!/src/page/plugins/chat +!/src/page/plugins/.gitkeep diff --git a/client/.prettierrc.json b/client/.prettierrc.json new file mode 100644 index 00000000..66e23359 --- /dev/null +++ b/client/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": false, + "tabWidth": 2, + "singleQuote": true, + "printWidth": 100, + "trailingComma": "none" +} \ No newline at end of file diff --git a/client/dev/api-gen b/client/dev/api-gen new file mode 100755 index 00000000..477f6be4 --- /dev/null +++ b/client/dev/api-gen @@ -0,0 +1,20 @@ +#!/bin/bash +OPENAPI_URL="https://raw.githubusercontent.com/demodesk/neko/master/openapi.yaml" + +rm -rf "${PWD}/../src/component/api" +mkdir "${PWD}/../src/component/api" + +docker run --rm \ + --user "$(id -u):$(id -g)" \ + -v "${PWD}/../src/component/api:/local/out" \ + openapitools/openapi-generator-cli generate \ + -i "$OPENAPI_URL" \ + -g typescript-axios \ + -o /local/out \ + --additional-properties=enumPropertyNaming=original,modelPropertyNaming=original,withSeparateModelsAndApi=true,modelPackage=models,apiPackage=api + +# Remove not needed git_push.sh +rm -f "${PWD}/../src/component/api/git_push.sh" + +# Fix lint errors +./npm run lint -- --fix src/component/api diff --git a/client/dev/build b/client/dev/build new file mode 100755 index 00000000..61a7078c --- /dev/null +++ b/client/dev/build @@ -0,0 +1,12 @@ +#!/bin/sh +cd "$(dirname "$0")" + +APP_PATH="$(realpath ../)" + +# start component watch +docker run --rm -it \ + --user "$(id -u):$(id -g)" \ + --volume "$APP_PATH:$APP_PATH" \ + --entrypoint="npm" \ + --workdir="$APP_PATH" \ + node:18-buster-slim run build diff --git a/client/dev/exec b/client/dev/exec new file mode 100755 index 00000000..ee056008 --- /dev/null +++ b/client/dev/exec @@ -0,0 +1,12 @@ +#!/bin/sh +cd "$(dirname "$0")" + +APP_PATH="$(realpath ../)" + +# start component watch +docker run --rm -it \ + --user "$(id -u):$(id -g)" \ + --volume "$APP_PATH:$APP_PATH" \ + --entrypoint="/bin/bash" \ + --workdir="$APP_PATH" \ + node:18-buster-slim diff --git a/client/dev/npm b/client/dev/npm new file mode 100755 index 00000000..c7d7e9df --- /dev/null +++ b/client/dev/npm @@ -0,0 +1,12 @@ +#!/bin/sh +cd "$(dirname "$0")" + +APP_PATH="$(realpath ../)" + +# npm +docker run --rm -it \ + --user "$(id -u):$(id -g)" \ + --volume "$APP_PATH:$APP_PATH" \ + --entrypoint="npm" \ + --workdir="$APP_PATH" \ + node:18-buster-slim "$@" diff --git a/client/dev/serve b/client/dev/serve new file mode 100755 index 00000000..ec27c9b9 --- /dev/null +++ b/client/dev/serve @@ -0,0 +1,40 @@ +#!/bin/sh +cd "$(dirname "$0")" + +if [ -z $NEKO_PORT ]; then + NEKO_PORT="3000" +fi + +if [ -z $NEKO_HOST ]; then + for i in $(ifconfig -l 2>/dev/null); do + NEKO_HOST=$(ipconfig getifaddr $i) + if [ ! -z $NEKO_HOST ]; then + break + fi + done + + if [ -z $NEKO_HOST ]; then + NEKO_HOST=$(hostname -I 2>/dev/null | awk '{print $1}') + fi + + if [ -z $NEKO_HOST ]; then + NEKO_HOST=$(hostname -i 2>/dev/null) + fi +fi + +echo "Using app port: ${NEKO_PORT}" +echo "Using IP address: ${NEKO_HOST}" + +APP_PATH="$(realpath ../)" + +# npm run serve +docker run --rm -it \ + -p 3001:3001 \ + -e "NEKO_HOST=$NEKO_HOST" \ + -e "NEKO_PORT=$NEKO_PORT" \ + -e "VUE_APP_LOG_COLOR=true" \ + --user "$(id -u):$(id -g)" \ + --volume "$APP_PATH:$APP_PATH" \ + --entrypoint="npm" \ + --workdir="$APP_PATH" \ + node:18-buster-slim run dev -- --port 3001 --host diff --git a/client/dev/version b/client/dev/version new file mode 100755 index 00000000..7f6e4f92 --- /dev/null +++ b/client/dev/version @@ -0,0 +1,23 @@ +#!/bin/bash +cd "$(dirname "$0")" + +if ! git diff-index --quiet HEAD +then + echo "Please clean git before publishing." + exit +fi + +# bump npm version +VERSION=$(./npm version "${1-patch}" --no-git-tag-version) +if [ $? -ne 0 ]; then + echo "Npm version bump failed." + exit +fi + +VERSION=$(echo "$VERSION" | head -1 | cut -c 2- | tr -d '\r') +echo "New version is: $VERSION" + +git add ../package* +git commit -m "version ${VERSION}" +git tag -a "v${VERSION}" -m "version ${VERSION}" +git push origin "v${VERSION}" diff --git a/client/e2e/tsconfig.json b/client/e2e/tsconfig.json new file mode 100644 index 00000000..be3bbfc0 --- /dev/null +++ b/client/e2e/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "include": ["./**/*"] +} diff --git a/client/e2e/vue.spec.ts b/client/e2e/vue.spec.ts new file mode 100644 index 00000000..3e5a3d02 --- /dev/null +++ b/client/e2e/vue.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + +// See here how to get started: +// https://playwright.dev/docs/intro +test('visits the app root url', async ({ page }) => { + await page.goto('/'); + await expect(page.locator('div.greetings > h1')).toHaveText('You did it!'); +}) diff --git a/client/env.d.ts b/client/env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/client/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/client/index.html b/client/index.html new file mode 100644 index 00000000..7bdea28b --- /dev/null +++ b/client/index.html @@ -0,0 +1,12 @@ + + + + + + Neko + + +
+ + + diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 00000000..f81e66bd --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,4871 @@ +{ + "name": "@demodesk/neko", + "version": "2.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@demodesk/neko", + "version": "2.0.0", + "dependencies": { + "axios": "^1.6.8", + "eventemitter3": "^5.0.1", + "resize-observer-polyfill": "^1.5.1", + "vue": "^3.4.21" + }, + "devDependencies": { + "@fortawesome/fontawesome-free": "^6.5.1", + "@playwright/test": "^1.42.1", + "@rushstack/eslint-patch": "^1.3.3", + "@tsconfig/node20": "^20.1.2", + "@types/jsdom": "^21.1.6", + "@types/node": "^20.11.25", + "@vitejs/plugin-vue": "^5.0.4", + "@vue/eslint-config-prettier": "^8.0.0", + "@vue/eslint-config-typescript": "^12.0.0", + "@vue/test-utils": "^2.4.4", + "@vue/tsconfig": "^0.5.1", + "eslint": "^8.49.0", + "eslint-plugin-playwright": "^1.5.2", + "eslint-plugin-vue": "^9.17.0", + "jsdom": "^24.0.0", + "npm-run-all2": "^6.1.2", + "prettier": "^3.0.3", + "sass": "^1.72.0", + "typescript": "~5.4.0", + "vite": "^5.1.5", + "vitest": "^1.3.1", + "vue-tsc": "^2.0.6" + } + }, + "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/@babel/parser": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", + "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", + "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", + "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", + "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", + "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", + "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", + "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", + "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", + "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", + "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", + "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", + "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", + "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", + "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", + "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", + "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", + "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", + "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", + "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", + "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", + "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", + "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "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.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "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/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/@eslint/eslintrc/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/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.5.1.tgz", + "integrity": "sha512-CNy5vSwN3fsUStPRLX7fUYojyuzoEMSXPl7zSLJ8TgtRfjv24LOnOWKT2zYwaHZCJGkdyRnTmstR0P+Ah503Gw==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/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/@humanwhocodes/config-array/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/@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": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.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==" + }, + "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/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@playwright/test": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", + "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "dev": true, + "dependencies": { + "playwright": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", + "integrity": "sha512-5ZYPOuaAqEH/W3gYsRkxQATBW3Ii1MfaT4EQstTnLKViLi2gLSQmlmtTpGucNP3sXEpOiI5tdGhjdE111ekyEg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.13.0.tgz", + "integrity": "sha512-BSbaCmn8ZadK3UAQdlauSvtaJjhlDEjS5hEVVIN3A4bbl3X+otyf/kOJV08bYiRxfejP3DXFzO2jz3G20107+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.13.0.tgz", + "integrity": "sha512-Ovf2evVaP6sW5Ut0GHyUSOqA6tVKfrTHddtmxGQc1CTQa1Cw3/KMCDEEICZBbyppcwnhMwcDce9ZRxdWRpVd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.13.0.tgz", + "integrity": "sha512-U+Jcxm89UTK592vZ2J9st9ajRv/hrwHdnvyuJpa5A2ngGSVHypigidkQJP+YiGL6JODiUeMzkqQzbCG3At81Gg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.13.0.tgz", + "integrity": "sha512-8wZidaUJUTIR5T4vRS22VkSMOVooG0F4N+JSwQXWSRiC6yfEsFMLTYRFHvby5mFFuExHa/yAp9juSphQQJAijQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.13.0.tgz", + "integrity": "sha512-Iu0Kno1vrD7zHQDxOmvweqLkAzjxEVqNhUIXBsZ8hu8Oak7/5VTPrxOEZXYC1nmrBVJp0ZcL2E7lSuuOVaE3+w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.13.0.tgz", + "integrity": "sha512-C31QrW47llgVyrRjIwiOwsHFcaIwmkKi3PCroQY5aVq4H0A5v/vVVAtFsI1nfBngtoRpeREvZOkIhmRwUKkAdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.13.0.tgz", + "integrity": "sha512-Oq90dtMHvthFOPMl7pt7KmxzX7E71AfyIhh+cPhLY9oko97Zf2C9tt/XJD4RgxhaGeAraAXDtqxvKE1y/j35lA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.13.0.tgz", + "integrity": "sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.13.0.tgz", + "integrity": "sha512-9RyNqoFNdF0vu/qqX63fKotBh43fJQeYC98hCaf89DYQpv+xu0D8QFSOS0biA7cGuqJFOc1bJ+m2rhhsKcw1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.13.0.tgz", + "integrity": "sha512-46ue8ymtm/5PUU6pCvjlic0z82qWkxv54GTJZgHrQUuZnVH+tvvSP0LsozIDsCBFO4VjJ13N68wqrKSeScUKdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.13.0.tgz", + "integrity": "sha512-P5/MqLdLSlqxbeuJ3YDeX37srC8mCflSyTrUsgbU1c/U9j6l2g2GiIdYaGD9QjdMQPMSgYm7hgg0551wHyIluw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.13.0.tgz", + "integrity": "sha512-UKXUQNbO3DOhzLRwHSpa0HnhhCgNODvfoPWv2FCXme8N/ANFfhIPMGuOT+QuKd16+B5yxZ0HdpNlqPvTMS1qfw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.7.2.tgz", + "integrity": "sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@tsconfig/node20": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.2.tgz", + "integrity": "sha512-madaWq2k+LYMEhmcp0fs+OGaLFk0OenpHa4gmI4VEmCKX4PJntQ6fnnGADVFrVkBj0wIdAlQnK/MrlYTHsa1gQ==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/@types/jsdom": { + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.6.tgz", + "integrity": "sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "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/node": { + "version": "20.11.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", + "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.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/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.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.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.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.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.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.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "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.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.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/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "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.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.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/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.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==", + "dev": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", + "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitest/expect": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", + "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.4.0", + "@vitest/utils": "1.4.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", + "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.4.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/runner/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", + "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", + "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", + "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@volar/language-core": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.1.2.tgz", + "integrity": "sha512-5qsDp0Gf6fE09UWCeK7bkVn6NxMwC9OqFWQkMMkeej8h8XjyABPdRygC2RCrqDrfVdGijqlMQeXs6yRS+vfZYA==", + "dev": true, + "dependencies": { + "@volar/source-map": "2.1.2" + } + }, + "node_modules/@volar/source-map": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.1.2.tgz", + "integrity": "sha512-yFJqsuLm1OaWrsz9E3yd3bJcYIlHqdZ8MbmIoZLrAzMYQDcoF26/INIhgziEXSdyHc8xd7rd/tJdSnUyh0gH4Q==", + "dev": true, + "dependencies": { + "muggle-string": "^0.4.0" + } + }, + "node_modules/@volar/typescript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.1.2.tgz", + "integrity": "sha512-lhTancZqamvaLvoz0u/uth8dpudENNt2LFZOWCw9JZiX14xRFhdhfzmphiCRb7am9E6qAJSbdS/gMt1utXAoHQ==", + "dev": true, + "dependencies": { + "@volar/language-core": "2.1.2", + "path-browserify": "^1.0.1" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", + "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", + "dependencies": { + "@babel/parser": "^7.23.9", + "@vue/shared": "3.4.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/@vue/compiler-dom": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", + "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", + "dependencies": { + "@vue/compiler-core": "3.4.21", + "@vue/shared": "3.4.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", + "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", + "dependencies": { + "@babel/parser": "^7.23.9", + "@vue/compiler-core": "3.4.21", + "@vue/compiler-dom": "3.4.21", + "@vue/compiler-ssr": "3.4.21", + "@vue/shared": "3.4.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.7", + "postcss": "^8.4.35", + "source-map-js": "^1.0.2" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", + "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", + "dependencies": { + "@vue/compiler-dom": "3.4.21", + "@vue/shared": "3.4.21" + } + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz", + "integrity": "sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg==", + "dev": true, + "dependencies": { + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-prettier": "^5.0.0" + }, + "peerDependencies": { + "eslint": ">= 8.0.0", + "prettier": ">= 3.0.0" + } + }, + "node_modules/@vue/eslint-config-typescript": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-typescript/-/eslint-config-typescript-12.0.0.tgz", + "integrity": "sha512-StxLFet2Qe97T8+7L8pGlhYBBr8Eg05LPuTDVopQV6il+SK6qqom59BA/rcFipUef2jD8P2X44Vd8tMFytfvlg==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", + "vue-eslint-parser": "^9.3.1" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0", + "eslint-plugin-vue": "^9.0.0", + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/language-core": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.6.tgz", + "integrity": "sha512-UzqU12tzf9XLqRO3TiWPwRNpP4fyUzE6MAfOQWQNZ4jy6a30ARRUpmODDKq6O8C4goMc2AlPqTmjOHPjHkilSg==", + "dev": true, + "dependencies": { + "@volar/language-core": "~2.1.2", + "@vue/compiler-dom": "^3.4.0", + "@vue/shared": "^3.4.0", + "computeds": "^0.0.1", + "minimatch": "^9.0.3", + "path-browserify": "^1.0.1", + "vue-template-compiler": "^2.7.14" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz", + "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", + "dependencies": { + "@vue/shared": "3.4.21" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz", + "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", + "dependencies": { + "@vue/reactivity": "3.4.21", + "@vue/shared": "3.4.21" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz", + "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", + "dependencies": { + "@vue/runtime-core": "3.4.21", + "@vue/shared": "3.4.21", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz", + "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", + "dependencies": { + "@vue/compiler-ssr": "3.4.21", + "@vue/shared": "3.4.21" + }, + "peerDependencies": { + "vue": "3.4.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", + "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" + }, + "node_modules/@vue/test-utils": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.5.tgz", + "integrity": "sha512-oo2u7vktOyKUked36R93NB7mg2B+N7Plr8lxp2JBGwr18ch6EggFjixSCdIVVLkT6Qr0z359Xvnafc9dcKyDUg==", + "dev": true, + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, + "node_modules/@vue/tsconfig": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@vue/tsconfig/-/tsconfig-0.5.1.tgz", + "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "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/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "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": "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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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-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/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "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/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/chai": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", + "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "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/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/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/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/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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/computeds": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", + "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", + "dev": true + }, + "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/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "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/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz", + "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==", + "dev": true, + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "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": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "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-config-prettier": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-playwright": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-1.5.2.tgz", + "integrity": "sha512-TMzLrLGQMccngU8GogtzIc9u5RzXGnfsQEUjLfEfshINuVR2fS4SHfDtU7xYP90Vwm5vflHECf610KTdGvO53w==", + "dev": true, + "dependencies": { + "globals": "^13.23.0" + }, + "engines": { + "node": ">=16.6.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0", + "eslint-plugin-jest": ">=25" + }, + "peerDependenciesMeta": { + "eslint-plugin-jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.8.6" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.23.0.tgz", + "integrity": "sha512-Bqd/b7hGYGrlV+wP/g77tjyFmp81lh5TMw0be9093X02SyelxRRfCI6/IsGq/J7Um0YwB9s0Ry0wlFyjPdmtUw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.0", + "vue-eslint-parser": "^9.4.2", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.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/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/eslint/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/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-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "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/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "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-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "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.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "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.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "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": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "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/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "dev": true + }, + "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/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-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-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-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", + "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "dev": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", + "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==", + "dev": true + }, + "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/jsdom": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz", + "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", + "dev": true, + "dependencies": { + "cssstyle": "^4.0.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.16.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "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-parse-even-better-errors": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", + "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "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/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, + "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/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dev": true, + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "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/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/magic-string": { + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "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/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/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mlly": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", + "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.0.3", + "ufo": "^1.3.2" + } + }, + "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==", + "dev": true + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "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/nopt": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "dev": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-run-all2": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/npm-run-all2/-/npm-run-all2-6.1.2.tgz", + "integrity": "sha512-WwwnS8Ft+RpXve6T2EIEVpFLSqN+ORHRvgNk3H9N62SZXjmzKoRhMFg3I17TK3oMaAEr+XFbRirWS2Fn3BCPSg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "cross-spawn": "^7.0.3", + "memorystream": "^0.3.1", + "minimatch": "^9.0.0", + "pidtree": "^0.6.0", + "read-package-json-fast": "^3.0.2", + "shell-quote": "^1.7.3" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "npm-run-all2": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0", + "npm": ">= 8" + } + }, + "node_modules/npm-run-all2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", + "dev": true + }, + "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/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true + }, + "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-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "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/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, + "node_modules/playwright": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", + "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "dev": true, + "dependencies": { + "playwright-core": "1.42.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.42.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", + "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/postcss": { + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "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.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.16", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", + "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "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/prettier": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "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-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-package-json-fast": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "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/rimraf/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/rimraf/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/rimraf/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/rollup": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz", + "integrity": "sha512-3YegKemjoQnYKmsBlOHfMLVPPA5xLkQ8MHLLSw/fBrFaVkEayL51DilPpNNLq1exr98F2B1TzrV0FUlN3gWRPg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.13.0", + "@rollup/rollup-android-arm64": "4.13.0", + "@rollup/rollup-darwin-arm64": "4.13.0", + "@rollup/rollup-darwin-x64": "4.13.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.13.0", + "@rollup/rollup-linux-arm64-gnu": "4.13.0", + "@rollup/rollup-linux-arm64-musl": "4.13.0", + "@rollup/rollup-linux-riscv64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-gnu": "4.13.0", + "@rollup/rollup-linux-x64-musl": "4.13.0", + "@rollup/rollup-win32-arm64-msvc": "4.13.0", + "@rollup/rollup-win32-ia32-msvc": "4.13.0", + "@rollup/rollup-win32-x64-msvc": "4.13.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", + "dev": true + }, + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sass": { + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz", + "integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/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/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/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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-ansi-cjs": { + "name": "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-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/strip-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", + "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", + "dev": true, + "dependencies": { + "js-tokens": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "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/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/synckit": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", + "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "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/tinybench": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", + "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", + "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "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/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ts-api-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", + "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "dev": true, + "engines": { + "node": ">=16" + }, + "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==", + "dev": true + }, + "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-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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/typescript": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", + "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "devOptional": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.1.tgz", + "integrity": "sha512-HGyF79+/qZ4soRvM+nHERR2pJ3VXDZ/8sL1uLahdgEDf580NkgiWOxLk33FetExqOWp352JZRsgXbG/4MaGOSg==", + "dev": true + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.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/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vite": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", + "integrity": "sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==", + "dev": true, + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "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/vite-node": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", + "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/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/vitest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", + "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.4.0", + "@vitest/runner": "1.4.0", + "@vitest/snapshot": "1.4.0", + "@vitest/spy": "1.4.0", + "@vitest/utils": "1.4.0", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.2", + "vite": "^5.0.0", + "vite-node": "1.4.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.4.0", + "@vitest/ui": "1.4.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", + "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", + "dependencies": { + "@vue/compiler-dom": "3.4.21", + "@vue/compiler-sfc": "3.4.21", + "@vue/runtime-dom": "3.4.21", + "@vue/server-renderer": "3.4.21", + "@vue/shared": "3.4.21" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-component-type-helpers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.0.6.tgz", + "integrity": "sha512-qdGXCtoBrwqk1BT6r2+1Wcvl583ZVkuSZ3or7Y1O2w5AvWtlvvxwjGhmz5DdPJS9xqRdDlgTJ/38ehWnEi0tFA==", + "dev": true + }, + "node_modules/vue-eslint-parser": { + "version": "9.4.2", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.2.tgz", + "integrity": "sha512-Ry9oiGmCAK91HrKMtCrKFWmSFWvYkpGglCeFAIqDdr9zdXmMMpJOmUJS7WWsW7fX81h6mwHmUZCQQ1E0PkSwYQ==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.6.tgz", + "integrity": "sha512-kK50W4XqQL34vHRkxlRWLicrT6+F9xfgCgJ4KSmCHcytKzc1u3c94XXgI+CjmhOSxyw0krpExF7Obo7y4+0dVQ==", + "dev": true, + "dependencies": { + "@volar/typescript": "~2.1.2", + "@vue/language-core": "2.0.6", + "semver": "^7.5.4" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": "*" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dev": true, + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "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/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" + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 00000000..5c7c6922 --- /dev/null +++ b/client/package.json @@ -0,0 +1,61 @@ +{ + "name": "@demodesk/neko", + "version": "2.0.0", + "description": "Client as reusable Vue.js component for neko streaming server.", + "repository": "https://github.com/demodesk/neko-client", + "publishConfig": { + "registry": "https://npm.pkg.github.com" + }, + "type": "module", + "main": "dist/neko.umd.js", + "module": "dist/neko.common.js", + "typings": "dist/types/main.d.ts", + "scripts": { + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "test:unit": "vitest", + "test:e2e": "playwright test", + "build-only": "vite build", + "type-check": "vue-tsc --build --force", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "format": "prettier --write src/" + }, + "dependencies": { + "axios": "^1.6.8", + "eventemitter3": "^5.0.1", + "resize-observer-polyfill": "^1.5.1", + "vue": "^3.4.21" + }, + "devDependencies": { + "@fortawesome/fontawesome-free": "^6.5.1", + "@playwright/test": "^1.42.1", + "@rushstack/eslint-patch": "^1.3.3", + "@tsconfig/node20": "^20.1.2", + "@types/jsdom": "^21.1.6", + "@types/node": "^20.11.25", + "@vitejs/plugin-vue": "^5.0.4", + "@vue/eslint-config-prettier": "^8.0.0", + "@vue/eslint-config-typescript": "^12.0.0", + "@vue/test-utils": "^2.4.4", + "@vue/tsconfig": "^0.5.1", + "eslint": "^8.49.0", + "eslint-plugin-playwright": "^1.5.2", + "eslint-plugin-vue": "^9.17.0", + "jsdom": "^24.0.0", + "npm-run-all2": "^6.1.2", + "prettier": "^3.0.3", + "sass": "^1.72.0", + "typescript": "~5.4.0", + "vite": "^5.1.5", + "vitest": "^1.3.1", + "vue-tsc": "^2.0.6" + }, + "files": [ + "dist/*" + ], + "browserslist": [ + "> 1%", + "last 2 versions" + ] +} diff --git a/client/playwright.config.ts b/client/playwright.config.ts new file mode 100644 index 00000000..92075cc1 --- /dev/null +++ b/client/playwright.config.ts @@ -0,0 +1,110 @@ +import process from 'node:process' +import { defineConfig, devices } from '@playwright/test' + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + /* Maximum time one test can run for. */ + timeout: 30 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000 + }, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: 'http://localhost:5173', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + + /* Only on CI systems run the tests headless */ + headless: !!process.env.CI + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'] + } + }, + { + name: 'firefox', + use: { + ...devices['Desktop Firefox'] + } + }, + { + name: 'webkit', + use: { + ...devices['Desktop Safari'] + } + } + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { + // ...devices['Pixel 5'], + // }, + // }, + // { + // name: 'Mobile Safari', + // use: { + // ...devices['iPhone 12'], + // }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { + // channel: 'msedge', + // }, + // }, + // { + // name: 'Google Chrome', + // use: { + // channel: 'chrome', + // }, + // }, + ], + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', + + /* Run your local dev server before starting the tests */ + webServer: { + /** + * Use the dev server by default for faster feedback loop. + * Use the preview server on CI for more realistic testing. + * Playwright will re-use the local server if there is already a dev-server running. + */ + command: process.env.CI ? 'vite preview --port 5173' : 'vite dev', + port: 5173, + reuseExistingServer: !process.env.CI + } +}) diff --git a/client/src/component/api/.gitignore b/client/src/component/api/.gitignore new file mode 100644 index 00000000..149b5765 --- /dev/null +++ b/client/src/component/api/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/client/src/component/api/.npmignore b/client/src/component/api/.npmignore new file mode 100644 index 00000000..999d88df --- /dev/null +++ b/client/src/component/api/.npmignore @@ -0,0 +1 @@ +# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/client/src/component/api/.openapi-generator-ignore b/client/src/component/api/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/client/src/component/api/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/client/src/component/api/.openapi-generator/FILES b/client/src/component/api/.openapi-generator/FILES new file mode 100644 index 00000000..7f6d9afc --- /dev/null +++ b/client/src/component/api/.openapi-generator/FILES @@ -0,0 +1,33 @@ +.gitignore +.npmignore +.openapi-generator-ignore +api.ts +api/default-api.ts +api/members-api.ts +api/room-api.ts +api/sessions-api.ts +base.ts +common.ts +configuration.ts +git_push.sh +index.ts +models/batch-request.ts +models/batch-response.ts +models/broadcast-status.ts +models/clipboard-text.ts +models/control-status.ts +models/error-message.ts +models/index.ts +models/keyboard-map.ts +models/keyboard-modifiers.ts +models/member-bulk-delete.ts +models/member-bulk-update.ts +models/member-create.ts +models/member-data.ts +models/member-password.ts +models/member-profile.ts +models/screen-configuration.ts +models/session-data.ts +models/session-login.ts +models/session-state.ts +models/settings.ts diff --git a/client/src/component/api/.openapi-generator/VERSION b/client/src/component/api/.openapi-generator/VERSION new file mode 100644 index 00000000..ecb21862 --- /dev/null +++ b/client/src/component/api/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.6.0-SNAPSHOT diff --git a/client/src/component/api/api.ts b/client/src/component/api/api.ts new file mode 100644 index 00000000..354e8c6f --- /dev/null +++ b/client/src/component/api/api.ts @@ -0,0 +1,21 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +export * from './api/default-api'; +export * from './api/members-api'; +export * from './api/room-api'; +export * from './api/sessions-api'; + diff --git a/client/src/component/api/api/default-api.ts b/client/src/component/api/api/default-api.ts new file mode 100644 index 00000000..b3ff7b9a --- /dev/null +++ b/client/src/component/api/api/default-api.ts @@ -0,0 +1,487 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { BatchRequest } from '../models'; +// @ts-ignore +import type { BatchResponse } from '../models'; +// @ts-ignore +import type { ErrorMessage } from '../models'; +// @ts-ignore +import type { SessionData } from '../models'; +// @ts-ignore +import type { SessionLogin } from '../models'; +/** + * DefaultApi - axios parameter creator + * @export + */ +export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary batch + * @param {Array} batchRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batch: async (batchRequest: Array, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'batchRequest' is not null or undefined + assertParamExists('batch', 'batchRequest', batchRequest) + const localVarPath = `/api/batch`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(batchRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary healthcheck + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + healthcheck: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/health`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary login + * @param {SessionLogin} sessionLogin + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + login: async (sessionLogin: SessionLogin, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'sessionLogin' is not null or undefined + assertParamExists('login', 'sessionLogin', sessionLogin) + const localVarPath = `/api/login`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(sessionLogin, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary logout + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + logout: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/logout`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + metrics: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/metrics`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary whoami + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + whoami: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/whoami`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * DefaultApi - functional programming interface + * @export + */ +export const DefaultApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration) + return { + /** + * + * @summary batch + * @param {Array} batchRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async batch(batchRequest: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.batch(batchRequest, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['DefaultApi.batch']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary healthcheck + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async healthcheck(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.healthcheck(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['DefaultApi.healthcheck']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary login + * @param {SessionLogin} sessionLogin + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async login(sessionLogin: SessionLogin, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.login(sessionLogin, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['DefaultApi.login']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary logout + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async logout(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.logout(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['DefaultApi.logout']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async metrics(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.metrics(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['DefaultApi.metrics']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary whoami + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async whoami(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.whoami(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['DefaultApi.whoami']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * DefaultApi - factory interface + * @export + */ +export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = DefaultApiFp(configuration) + return { + /** + * + * @summary batch + * @param {Array} batchRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + batch(batchRequest: Array, options?: any): AxiosPromise> { + return localVarFp.batch(batchRequest, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary healthcheck + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + healthcheck(options?: any): AxiosPromise { + return localVarFp.healthcheck(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary login + * @param {SessionLogin} sessionLogin + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + login(sessionLogin: SessionLogin, options?: any): AxiosPromise { + return localVarFp.login(sessionLogin, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary logout + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + logout(options?: any): AxiosPromise { + return localVarFp.logout(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + metrics(options?: any): AxiosPromise { + return localVarFp.metrics(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary whoami + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + whoami(options?: any): AxiosPromise { + return localVarFp.whoami(options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * DefaultApi - object-oriented interface + * @export + * @class DefaultApi + * @extends {BaseAPI} + */ +export class DefaultApi extends BaseAPI { + /** + * + * @summary batch + * @param {Array} batchRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public batch(batchRequest: Array, options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).batch(batchRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary healthcheck + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public healthcheck(options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).healthcheck(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary login + * @param {SessionLogin} sessionLogin + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public login(sessionLogin: SessionLogin, options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).login(sessionLogin, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary logout + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public logout(options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).logout(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary metrics + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public metrics(options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).metrics(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary whoami + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof DefaultApi + */ + public whoami(options?: RawAxiosRequestConfig) { + return DefaultApiFp(this.configuration).whoami(options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/client/src/component/api/api/members-api.ts b/client/src/component/api/api/members-api.ts new file mode 100644 index 00000000..545022c6 --- /dev/null +++ b/client/src/component/api/api/members-api.ts @@ -0,0 +1,731 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { ErrorMessage } from '../models'; +// @ts-ignore +import type { MemberBulkDelete } from '../models'; +// @ts-ignore +import type { MemberBulkUpdate } from '../models'; +// @ts-ignore +import type { MemberCreate } from '../models'; +// @ts-ignore +import type { MemberData } from '../models'; +// @ts-ignore +import type { MemberPassword } from '../models'; +// @ts-ignore +import type { MemberProfile } from '../models'; +/** + * MembersApi - axios parameter creator + * @export + */ +export const MembersApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary bulk delete members + * @param {MemberBulkDelete} memberBulkDelete + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersBulkDelete: async (memberBulkDelete: MemberBulkDelete, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'memberBulkDelete' is not null or undefined + assertParamExists('membersBulkDelete', 'memberBulkDelete', memberBulkDelete) + const localVarPath = `/api/members_bulk/delete`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(memberBulkDelete, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary bulk update members + * @param {MemberBulkUpdate} memberBulkUpdate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersBulkUpdate: async (memberBulkUpdate: MemberBulkUpdate, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'memberBulkUpdate' is not null or undefined + assertParamExists('membersBulkUpdate', 'memberBulkUpdate', memberBulkUpdate) + const localVarPath = `/api/members_bulk/update`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(memberBulkUpdate, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary create new member + * @param {MemberCreate} memberCreate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersCreate: async (memberCreate: MemberCreate, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'memberCreate' is not null or undefined + assertParamExists('membersCreate', 'memberCreate', memberCreate) + const localVarPath = `/api/members`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(memberCreate, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get member\'s profile + * @param {string} memberId member identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersGetProfile: async (memberId: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'memberId' is not null or undefined + assertParamExists('membersGetProfile', 'memberId', memberId) + const localVarPath = `/api/members/{memberId}` + .replace(`{${"memberId"}}`, encodeURIComponent(String(memberId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary list of members + * @param {number} [limit] + * @param {number} [offset] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersList: async (limit?: number, offset?: number, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/members`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (limit !== undefined) { + localVarQueryParameter['limit'] = limit; + } + + if (offset !== undefined) { + localVarQueryParameter['offset'] = offset; + } + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary remove member + * @param {string} memberId member identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersRemove: async (memberId: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'memberId' is not null or undefined + assertParamExists('membersRemove', 'memberId', memberId) + const localVarPath = `/api/members/{memberId}` + .replace(`{${"memberId"}}`, encodeURIComponent(String(memberId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary update member\'s password + * @param {string} memberId member identifier + * @param {MemberPassword} memberPassword + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersUpdatePassword: async (memberId: string, memberPassword: MemberPassword, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'memberId' is not null or undefined + assertParamExists('membersUpdatePassword', 'memberId', memberId) + // verify required parameter 'memberPassword' is not null or undefined + assertParamExists('membersUpdatePassword', 'memberPassword', memberPassword) + const localVarPath = `/api/members/{memberId}/password` + .replace(`{${"memberId"}}`, encodeURIComponent(String(memberId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(memberPassword, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary update member\'s profile + * @param {string} memberId member identifier + * @param {MemberProfile} memberProfile + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersUpdateProfile: async (memberId: string, memberProfile: MemberProfile, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'memberId' is not null or undefined + assertParamExists('membersUpdateProfile', 'memberId', memberId) + // verify required parameter 'memberProfile' is not null or undefined + assertParamExists('membersUpdateProfile', 'memberProfile', memberProfile) + const localVarPath = `/api/members/{memberId}` + .replace(`{${"memberId"}}`, encodeURIComponent(String(memberId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(memberProfile, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * MembersApi - functional programming interface + * @export + */ +export const MembersApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = MembersApiAxiosParamCreator(configuration) + return { + /** + * + * @summary bulk delete members + * @param {MemberBulkDelete} memberBulkDelete + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async membersBulkDelete(memberBulkDelete: MemberBulkDelete, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.membersBulkDelete(memberBulkDelete, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MembersApi.membersBulkDelete']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary bulk update members + * @param {MemberBulkUpdate} memberBulkUpdate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async membersBulkUpdate(memberBulkUpdate: MemberBulkUpdate, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.membersBulkUpdate(memberBulkUpdate, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MembersApi.membersBulkUpdate']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary create new member + * @param {MemberCreate} memberCreate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async membersCreate(memberCreate: MemberCreate, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.membersCreate(memberCreate, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MembersApi.membersCreate']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get member\'s profile + * @param {string} memberId member identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async membersGetProfile(memberId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.membersGetProfile(memberId, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MembersApi.membersGetProfile']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary list of members + * @param {number} [limit] + * @param {number} [offset] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async membersList(limit?: number, offset?: number, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.membersList(limit, offset, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MembersApi.membersList']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary remove member + * @param {string} memberId member identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async membersRemove(memberId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.membersRemove(memberId, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MembersApi.membersRemove']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary update member\'s password + * @param {string} memberId member identifier + * @param {MemberPassword} memberPassword + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async membersUpdatePassword(memberId: string, memberPassword: MemberPassword, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.membersUpdatePassword(memberId, memberPassword, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MembersApi.membersUpdatePassword']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary update member\'s profile + * @param {string} memberId member identifier + * @param {MemberProfile} memberProfile + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async membersUpdateProfile(memberId: string, memberProfile: MemberProfile, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.membersUpdateProfile(memberId, memberProfile, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['MembersApi.membersUpdateProfile']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * MembersApi - factory interface + * @export + */ +export const MembersApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = MembersApiFp(configuration) + return { + /** + * + * @summary bulk delete members + * @param {MemberBulkDelete} memberBulkDelete + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersBulkDelete(memberBulkDelete: MemberBulkDelete, options?: any): AxiosPromise { + return localVarFp.membersBulkDelete(memberBulkDelete, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary bulk update members + * @param {MemberBulkUpdate} memberBulkUpdate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersBulkUpdate(memberBulkUpdate: MemberBulkUpdate, options?: any): AxiosPromise { + return localVarFp.membersBulkUpdate(memberBulkUpdate, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary create new member + * @param {MemberCreate} memberCreate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersCreate(memberCreate: MemberCreate, options?: any): AxiosPromise { + return localVarFp.membersCreate(memberCreate, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get member\'s profile + * @param {string} memberId member identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersGetProfile(memberId: string, options?: any): AxiosPromise { + return localVarFp.membersGetProfile(memberId, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary list of members + * @param {number} [limit] + * @param {number} [offset] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersList(limit?: number, offset?: number, options?: any): AxiosPromise> { + return localVarFp.membersList(limit, offset, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary remove member + * @param {string} memberId member identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersRemove(memberId: string, options?: any): AxiosPromise { + return localVarFp.membersRemove(memberId, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary update member\'s password + * @param {string} memberId member identifier + * @param {MemberPassword} memberPassword + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersUpdatePassword(memberId: string, memberPassword: MemberPassword, options?: any): AxiosPromise { + return localVarFp.membersUpdatePassword(memberId, memberPassword, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary update member\'s profile + * @param {string} memberId member identifier + * @param {MemberProfile} memberProfile + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + membersUpdateProfile(memberId: string, memberProfile: MemberProfile, options?: any): AxiosPromise { + return localVarFp.membersUpdateProfile(memberId, memberProfile, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * MembersApi - object-oriented interface + * @export + * @class MembersApi + * @extends {BaseAPI} + */ +export class MembersApi extends BaseAPI { + /** + * + * @summary bulk delete members + * @param {MemberBulkDelete} memberBulkDelete + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MembersApi + */ + public membersBulkDelete(memberBulkDelete: MemberBulkDelete, options?: RawAxiosRequestConfig) { + return MembersApiFp(this.configuration).membersBulkDelete(memberBulkDelete, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary bulk update members + * @param {MemberBulkUpdate} memberBulkUpdate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MembersApi + */ + public membersBulkUpdate(memberBulkUpdate: MemberBulkUpdate, options?: RawAxiosRequestConfig) { + return MembersApiFp(this.configuration).membersBulkUpdate(memberBulkUpdate, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary create new member + * @param {MemberCreate} memberCreate + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MembersApi + */ + public membersCreate(memberCreate: MemberCreate, options?: RawAxiosRequestConfig) { + return MembersApiFp(this.configuration).membersCreate(memberCreate, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get member\'s profile + * @param {string} memberId member identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MembersApi + */ + public membersGetProfile(memberId: string, options?: RawAxiosRequestConfig) { + return MembersApiFp(this.configuration).membersGetProfile(memberId, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary list of members + * @param {number} [limit] + * @param {number} [offset] + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MembersApi + */ + public membersList(limit?: number, offset?: number, options?: RawAxiosRequestConfig) { + return MembersApiFp(this.configuration).membersList(limit, offset, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary remove member + * @param {string} memberId member identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MembersApi + */ + public membersRemove(memberId: string, options?: RawAxiosRequestConfig) { + return MembersApiFp(this.configuration).membersRemove(memberId, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary update member\'s password + * @param {string} memberId member identifier + * @param {MemberPassword} memberPassword + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MembersApi + */ + public membersUpdatePassword(memberId: string, memberPassword: MemberPassword, options?: RawAxiosRequestConfig) { + return MembersApiFp(this.configuration).membersUpdatePassword(memberId, memberPassword, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary update member\'s profile + * @param {string} memberId member identifier + * @param {MemberProfile} memberProfile + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof MembersApi + */ + public membersUpdateProfile(memberId: string, memberProfile: MemberProfile, options?: RawAxiosRequestConfig) { + return MembersApiFp(this.configuration).membersUpdateProfile(memberId, memberProfile, options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/client/src/component/api/api/room-api.ts b/client/src/component/api/api/room-api.ts new file mode 100644 index 00000000..eeae36ab --- /dev/null +++ b/client/src/component/api/api/room-api.ts @@ -0,0 +1,2027 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { BroadcastStatus } from '../models'; +// @ts-ignore +import type { ClipboardText } from '../models'; +// @ts-ignore +import type { ControlStatus } from '../models'; +// @ts-ignore +import type { ErrorMessage } from '../models'; +// @ts-ignore +import type { KeyboardMap } from '../models'; +// @ts-ignore +import type { KeyboardModifiers } from '../models'; +// @ts-ignore +import type { ScreenConfiguration } from '../models'; +// @ts-ignore +import type { Settings } from '../models'; +/** + * RoomApi - axios parameter creator + * @export + */ +export const RoomApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary start broadcast + * @param {BroadcastStatus} broadcastStatus + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + boradcastStart: async (broadcastStatus: BroadcastStatus, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'broadcastStatus' is not null or undefined + assertParamExists('boradcastStart', 'broadcastStatus', broadcastStatus) + const localVarPath = `/api/room/broadcast/start`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(broadcastStatus, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary stop broadcast + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + boradcastStop: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/broadcast/stop`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get broadcast status + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + broadcastStatus: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/broadcast`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get clipboard image content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + clipboardGetImage: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/clipboard/image.png`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get clipboard rich-text or plain-text content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + clipboardGetText: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/clipboard`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary set clipboard rich-text or plain-text content + * @param {ClipboardText} clipboardText + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + clipboardSetText: async (clipboardText: ClipboardText, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'clipboardText' is not null or undefined + assertParamExists('clipboardSetText', 'clipboardText', clipboardText) + const localVarPath = `/api/room/clipboard`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(clipboardText, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary give control + * @param {string} sessionId session ID + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlGive: async (sessionId: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'sessionId' is not null or undefined + assertParamExists('controlGive', 'sessionId', sessionId) + const localVarPath = `/api/room/control/give/{sessionId}` + .replace(`{${"sessionId"}}`, encodeURIComponent(String(sessionId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary release control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlRelease: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/control/release`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary request control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlRequest: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/control/request`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary reset control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlReset: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/control/reset`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get control status + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlStatus: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/control`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary take control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlTake: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/control/take`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get keyboard map + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + keyboardMapGet: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/keyboard/map`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary set keyboard map + * @param {KeyboardMap} keyboardMap + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + keyboardMapSet: async (keyboardMap: KeyboardMap, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'keyboardMap' is not null or undefined + assertParamExists('keyboardMapSet', 'keyboardMap', keyboardMap) + const localVarPath = `/api/room/keyboard/map`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(keyboardMap, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get keyboard modifiers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + keyboardModifiersGet: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/keyboard/modifiers`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary set keyboard modifiers + * @param {KeyboardModifiers} keyboardModifiers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + keyboardModifiersSet: async (keyboardModifiers: KeyboardModifiers, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'keyboardModifiers' is not null or undefined + assertParamExists('keyboardModifiersSet', 'keyboardModifiers', keyboardModifiers) + const localVarPath = `/api/room/keyboard/modifiers`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(keyboardModifiers, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get screencast image + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenCastImage: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/screen/cast.jpg`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get current screen configuration + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenConfiguration: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/screen`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary change screen configuration + * @param {ScreenConfiguration} screenConfiguration + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenConfigurationChange: async (screenConfiguration: ScreenConfiguration, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'screenConfiguration' is not null or undefined + assertParamExists('screenConfigurationChange', 'screenConfiguration', screenConfiguration) + const localVarPath = `/api/room/screen`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(screenConfiguration, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get list of all available screen configurations + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenConfigurationsList: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/screen/configurations`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get screenshot image + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenShotImage: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/screen/shot.jpg`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get settings + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + settingsGet: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/settings`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary set settings + * @param {Settings} settings + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + settingsSet: async (settings: Settings, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'settings' is not null or undefined + assertParamExists('settingsSet', 'settings', settings) + const localVarPath = `/api/room/settings`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + localVarHeaderParameter['Content-Type'] = 'application/json'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = serializeDataIfNeeded(settings, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary upload file to a dialog + * @param {Array} [files] files to be uploaded + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadDialog: async (files?: Array, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/upload/dialog`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + const localVarFormParams = new ((configuration && configuration.formDataCtor) || FormData)(); + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + if (files) { + files.forEach((element) => { + localVarFormParams.append('files', element as any); + }) + } + + + + localVarHeaderParameter['Content-Type'] = 'multipart/form-data'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = localVarFormParams; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary close file chooser dialog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadDialogClose: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/upload/dialog`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary upload and drop file + * @param {number} [x] X coordinate of drop + * @param {number} [y] Y coordinate of drop + * @param {Array} [files] files to be uploaded + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadDrop: async (x?: number, y?: number, files?: Array, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/room/upload/drop`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + const localVarFormParams = new ((configuration && configuration.formDataCtor) || FormData)(); + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + if (x !== undefined) { + localVarFormParams.append('x', x as any); + } + + if (y !== undefined) { + localVarFormParams.append('y', y as any); + } + if (files) { + files.forEach((element) => { + localVarFormParams.append('files', element as any); + }) + } + + + + localVarHeaderParameter['Content-Type'] = 'multipart/form-data'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = localVarFormParams; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * RoomApi - functional programming interface + * @export + */ +export const RoomApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = RoomApiAxiosParamCreator(configuration) + return { + /** + * + * @summary start broadcast + * @param {BroadcastStatus} broadcastStatus + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async boradcastStart(broadcastStatus: BroadcastStatus, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.boradcastStart(broadcastStatus, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.boradcastStart']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary stop broadcast + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async boradcastStop(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.boradcastStop(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.boradcastStop']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get broadcast status + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async broadcastStatus(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.broadcastStatus(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.broadcastStatus']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get clipboard image content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async clipboardGetImage(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.clipboardGetImage(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.clipboardGetImage']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get clipboard rich-text or plain-text content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async clipboardGetText(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.clipboardGetText(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.clipboardGetText']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary set clipboard rich-text or plain-text content + * @param {ClipboardText} clipboardText + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async clipboardSetText(clipboardText: ClipboardText, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.clipboardSetText(clipboardText, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.clipboardSetText']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary give control + * @param {string} sessionId session ID + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async controlGive(sessionId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.controlGive(sessionId, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.controlGive']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary release control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async controlRelease(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.controlRelease(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.controlRelease']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary request control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async controlRequest(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.controlRequest(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.controlRequest']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary reset control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async controlReset(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.controlReset(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.controlReset']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get control status + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async controlStatus(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.controlStatus(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.controlStatus']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary take control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async controlTake(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.controlTake(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.controlTake']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get keyboard map + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async keyboardMapGet(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.keyboardMapGet(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.keyboardMapGet']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary set keyboard map + * @param {KeyboardMap} keyboardMap + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async keyboardMapSet(keyboardMap: KeyboardMap, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.keyboardMapSet(keyboardMap, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.keyboardMapSet']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get keyboard modifiers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async keyboardModifiersGet(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.keyboardModifiersGet(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.keyboardModifiersGet']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary set keyboard modifiers + * @param {KeyboardModifiers} keyboardModifiers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async keyboardModifiersSet(keyboardModifiers: KeyboardModifiers, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.keyboardModifiersSet(keyboardModifiers, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.keyboardModifiersSet']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get screencast image + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async screenCastImage(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.screenCastImage(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.screenCastImage']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get current screen configuration + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async screenConfiguration(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.screenConfiguration(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.screenConfiguration']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary change screen configuration + * @param {ScreenConfiguration} screenConfiguration + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async screenConfigurationChange(screenConfiguration: ScreenConfiguration, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.screenConfigurationChange(screenConfiguration, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.screenConfigurationChange']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get list of all available screen configurations + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async screenConfigurationsList(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.screenConfigurationsList(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.screenConfigurationsList']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get screenshot image + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async screenShotImage(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.screenShotImage(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.screenShotImage']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get settings + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async settingsGet(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.settingsGet(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.settingsGet']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary set settings + * @param {Settings} settings + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async settingsSet(settings: Settings, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.settingsSet(settings, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.settingsSet']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary upload file to a dialog + * @param {Array} [files] files to be uploaded + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async uploadDialog(files?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.uploadDialog(files, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.uploadDialog']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary close file chooser dialog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async uploadDialogClose(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.uploadDialogClose(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.uploadDialogClose']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary upload and drop file + * @param {number} [x] X coordinate of drop + * @param {number} [y] Y coordinate of drop + * @param {Array} [files] files to be uploaded + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async uploadDrop(x?: number, y?: number, files?: Array, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.uploadDrop(x, y, files, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['RoomApi.uploadDrop']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * RoomApi - factory interface + * @export + */ +export const RoomApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = RoomApiFp(configuration) + return { + /** + * + * @summary start broadcast + * @param {BroadcastStatus} broadcastStatus + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + boradcastStart(broadcastStatus: BroadcastStatus, options?: any): AxiosPromise { + return localVarFp.boradcastStart(broadcastStatus, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary stop broadcast + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + boradcastStop(options?: any): AxiosPromise { + return localVarFp.boradcastStop(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get broadcast status + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + broadcastStatus(options?: any): AxiosPromise { + return localVarFp.broadcastStatus(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get clipboard image content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + clipboardGetImage(options?: any): AxiosPromise { + return localVarFp.clipboardGetImage(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get clipboard rich-text or plain-text content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + clipboardGetText(options?: any): AxiosPromise { + return localVarFp.clipboardGetText(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary set clipboard rich-text or plain-text content + * @param {ClipboardText} clipboardText + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + clipboardSetText(clipboardText: ClipboardText, options?: any): AxiosPromise { + return localVarFp.clipboardSetText(clipboardText, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary give control + * @param {string} sessionId session ID + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlGive(sessionId: string, options?: any): AxiosPromise { + return localVarFp.controlGive(sessionId, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary release control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlRelease(options?: any): AxiosPromise { + return localVarFp.controlRelease(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary request control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlRequest(options?: any): AxiosPromise { + return localVarFp.controlRequest(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary reset control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlReset(options?: any): AxiosPromise { + return localVarFp.controlReset(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get control status + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlStatus(options?: any): AxiosPromise { + return localVarFp.controlStatus(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary take control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + controlTake(options?: any): AxiosPromise { + return localVarFp.controlTake(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get keyboard map + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + keyboardMapGet(options?: any): AxiosPromise { + return localVarFp.keyboardMapGet(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary set keyboard map + * @param {KeyboardMap} keyboardMap + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + keyboardMapSet(keyboardMap: KeyboardMap, options?: any): AxiosPromise { + return localVarFp.keyboardMapSet(keyboardMap, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get keyboard modifiers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + keyboardModifiersGet(options?: any): AxiosPromise { + return localVarFp.keyboardModifiersGet(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary set keyboard modifiers + * @param {KeyboardModifiers} keyboardModifiers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + keyboardModifiersSet(keyboardModifiers: KeyboardModifiers, options?: any): AxiosPromise { + return localVarFp.keyboardModifiersSet(keyboardModifiers, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get screencast image + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenCastImage(options?: any): AxiosPromise { + return localVarFp.screenCastImage(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get current screen configuration + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenConfiguration(options?: any): AxiosPromise { + return localVarFp.screenConfiguration(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary change screen configuration + * @param {ScreenConfiguration} screenConfiguration + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenConfigurationChange(screenConfiguration: ScreenConfiguration, options?: any): AxiosPromise { + return localVarFp.screenConfigurationChange(screenConfiguration, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get list of all available screen configurations + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenConfigurationsList(options?: any): AxiosPromise> { + return localVarFp.screenConfigurationsList(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get screenshot image + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + screenShotImage(options?: any): AxiosPromise { + return localVarFp.screenShotImage(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get settings + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + settingsGet(options?: any): AxiosPromise { + return localVarFp.settingsGet(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary set settings + * @param {Settings} settings + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + settingsSet(settings: Settings, options?: any): AxiosPromise { + return localVarFp.settingsSet(settings, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary upload file to a dialog + * @param {Array} [files] files to be uploaded + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadDialog(files?: Array, options?: any): AxiosPromise { + return localVarFp.uploadDialog(files, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary close file chooser dialog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadDialogClose(options?: any): AxiosPromise { + return localVarFp.uploadDialogClose(options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary upload and drop file + * @param {number} [x] X coordinate of drop + * @param {number} [y] Y coordinate of drop + * @param {Array} [files] files to be uploaded + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadDrop(x?: number, y?: number, files?: Array, options?: any): AxiosPromise { + return localVarFp.uploadDrop(x, y, files, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * RoomApi - object-oriented interface + * @export + * @class RoomApi + * @extends {BaseAPI} + */ +export class RoomApi extends BaseAPI { + /** + * + * @summary start broadcast + * @param {BroadcastStatus} broadcastStatus + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public boradcastStart(broadcastStatus: BroadcastStatus, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).boradcastStart(broadcastStatus, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary stop broadcast + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public boradcastStop(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).boradcastStop(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get broadcast status + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public broadcastStatus(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).broadcastStatus(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get clipboard image content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public clipboardGetImage(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).clipboardGetImage(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get clipboard rich-text or plain-text content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public clipboardGetText(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).clipboardGetText(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary set clipboard rich-text or plain-text content + * @param {ClipboardText} clipboardText + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public clipboardSetText(clipboardText: ClipboardText, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).clipboardSetText(clipboardText, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary give control + * @param {string} sessionId session ID + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public controlGive(sessionId: string, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).controlGive(sessionId, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary release control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public controlRelease(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).controlRelease(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary request control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public controlRequest(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).controlRequest(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary reset control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public controlReset(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).controlReset(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get control status + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public controlStatus(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).controlStatus(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary take control + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public controlTake(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).controlTake(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get keyboard map + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public keyboardMapGet(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).keyboardMapGet(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary set keyboard map + * @param {KeyboardMap} keyboardMap + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public keyboardMapSet(keyboardMap: KeyboardMap, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).keyboardMapSet(keyboardMap, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get keyboard modifiers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public keyboardModifiersGet(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).keyboardModifiersGet(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary set keyboard modifiers + * @param {KeyboardModifiers} keyboardModifiers + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public keyboardModifiersSet(keyboardModifiers: KeyboardModifiers, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).keyboardModifiersSet(keyboardModifiers, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get screencast image + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public screenCastImage(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).screenCastImage(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get current screen configuration + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public screenConfiguration(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).screenConfiguration(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary change screen configuration + * @param {ScreenConfiguration} screenConfiguration + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public screenConfigurationChange(screenConfiguration: ScreenConfiguration, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).screenConfigurationChange(screenConfiguration, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get list of all available screen configurations + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public screenConfigurationsList(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).screenConfigurationsList(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get screenshot image + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public screenShotImage(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).screenShotImage(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get settings + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public settingsGet(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).settingsGet(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary set settings + * @param {Settings} settings + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public settingsSet(settings: Settings, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).settingsSet(settings, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary upload file to a dialog + * @param {Array} [files] files to be uploaded + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public uploadDialog(files?: Array, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).uploadDialog(files, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary close file chooser dialog + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public uploadDialogClose(options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).uploadDialogClose(options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary upload and drop file + * @param {number} [x] X coordinate of drop + * @param {number} [y] Y coordinate of drop + * @param {Array} [files] files to be uploaded + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof RoomApi + */ + public uploadDrop(x?: number, y?: number, files?: Array, options?: RawAxiosRequestConfig) { + return RoomApiFp(this.configuration).uploadDrop(x, y, files, options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/client/src/component/api/api/sessions-api.ts b/client/src/component/api/api/sessions-api.ts new file mode 100644 index 00000000..92289fac --- /dev/null +++ b/client/src/component/api/api/sessions-api.ts @@ -0,0 +1,369 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import type { ErrorMessage } from '../models'; +// @ts-ignore +import type { SessionData } from '../models'; +/** + * SessionsApi - axios parameter creator + * @export + */ +export const SessionsApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary disconnect session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sessionDisconnect: async (sessionId: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'sessionId' is not null or undefined + assertParamExists('sessionDisconnect', 'sessionId', sessionId) + const localVarPath = `/api/sessions/{sessionId}/disconnect` + .replace(`{${"sessionId"}}`, encodeURIComponent(String(sessionId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sessionGet: async (sessionId: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'sessionId' is not null or undefined + assertParamExists('sessionGet', 'sessionId', sessionId) + const localVarPath = `/api/sessions/{sessionId}` + .replace(`{${"sessionId"}}`, encodeURIComponent(String(sessionId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary remove session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sessionRemove: async (sessionId: string, options: RawAxiosRequestConfig = {}): Promise => { + // verify required parameter 'sessionId' is not null or undefined + assertParamExists('sessionRemove', 'sessionId', sessionId) + const localVarPath = `/api/sessions/{sessionId}` + .replace(`{${"sessionId"}}`, encodeURIComponent(String(sessionId))); + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'DELETE', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * + * @summary get sessions + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sessionsGet: async (options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/sessions`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * SessionsApi - functional programming interface + * @export + */ +export const SessionsApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = SessionsApiAxiosParamCreator(configuration) + return { + /** + * + * @summary disconnect session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async sessionDisconnect(sessionId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.sessionDisconnect(sessionId, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['SessionsApi.sessionDisconnect']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async sessionGet(sessionId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.sessionGet(sessionId, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['SessionsApi.sessionGet']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary remove session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async sessionRemove(sessionId: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.sessionRemove(sessionId, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['SessionsApi.sessionRemove']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + /** + * + * @summary get sessions + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async sessionsGet(options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise>> { + const localVarAxiosArgs = await localVarAxiosParamCreator.sessionsGet(options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['SessionsApi.sessionsGet']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * SessionsApi - factory interface + * @export + */ +export const SessionsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = SessionsApiFp(configuration) + return { + /** + * + * @summary disconnect session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sessionDisconnect(sessionId: string, options?: any): AxiosPromise { + return localVarFp.sessionDisconnect(sessionId, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sessionGet(sessionId: string, options?: any): AxiosPromise { + return localVarFp.sessionGet(sessionId, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary remove session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sessionRemove(sessionId: string, options?: any): AxiosPromise { + return localVarFp.sessionRemove(sessionId, options).then((request) => request(axios, basePath)); + }, + /** + * + * @summary get sessions + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sessionsGet(options?: any): AxiosPromise> { + return localVarFp.sessionsGet(options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * SessionsApi - object-oriented interface + * @export + * @class SessionsApi + * @extends {BaseAPI} + */ +export class SessionsApi extends BaseAPI { + /** + * + * @summary disconnect session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SessionsApi + */ + public sessionDisconnect(sessionId: string, options?: RawAxiosRequestConfig) { + return SessionsApiFp(this.configuration).sessionDisconnect(sessionId, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SessionsApi + */ + public sessionGet(sessionId: string, options?: RawAxiosRequestConfig) { + return SessionsApiFp(this.configuration).sessionGet(sessionId, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary remove session + * @param {string} sessionId session identifier + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SessionsApi + */ + public sessionRemove(sessionId: string, options?: RawAxiosRequestConfig) { + return SessionsApiFp(this.configuration).sessionRemove(sessionId, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * + * @summary get sessions + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof SessionsApi + */ + public sessionsGet(options?: RawAxiosRequestConfig) { + return SessionsApiFp(this.configuration).sessionsGet(options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/client/src/component/api/base.ts b/client/src/component/api/base.ts new file mode 100644 index 00000000..5c61b870 --- /dev/null +++ b/client/src/component/api/base.ts @@ -0,0 +1,86 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; + +export const BASE_PATH = "http://localhost:3000".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: RawAxiosRequestConfig; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath ?? basePath; + } + } +}; + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + constructor(public field: string, msg?: string) { + super(msg); + this.name = "RequiredError" + } +} + +interface ServerMap { + [key: string]: { + url: string, + description: string, + }[]; +} + +/** + * + * @export + */ +export const operationServerMap: ServerMap = { +} diff --git a/client/src/component/api/common.ts b/client/src/component/api/common.ts new file mode 100644 index 00000000..64f52759 --- /dev/null +++ b/client/src/component/api/common.ts @@ -0,0 +1,150 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from 'axios'; +import { RequiredError } from "./base"; + +/** + * + * @export + */ +export const DUMMY_BASE_URL = 'https://example.com' + +/** + * + * @throws {RequiredError} + * @export + */ +export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); + } +} + +/** + * + * @export + */ +export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = typeof configuration.apiKey === 'function' + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +} + +/** + * + * @export + */ +export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { username: configuration.username, password: configuration.password }; + } +} + +/** + * + * @export + */ +export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +} + +/** + * + * @export + */ +export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = typeof configuration.accessToken === 'function' + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +} + +function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter)) { + (parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key)); + } + else { + Object.keys(parameter).forEach(currentKey => + setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`) + ); + } + } + else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } + else { + urlSearchParams.set(key, parameter); + } + } +} + +/** + * + * @export + */ +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +} + +/** + * + * @export + */ +export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { + const nonString = typeof value !== 'string'; + const needsSerialization = nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers['Content-Type']) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}) + : (value || ""); +} + +/** + * + * @export + */ +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash +} + +/** + * + * @export + */ +export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { + return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs = {...axiosArgs.options, url: (axios.defaults.baseURL ? '' : configuration?.basePath ?? basePath) + axiosArgs.url}; + return axios.request(axiosRequestArgs); + }; +} diff --git a/client/src/component/api/configuration.ts b/client/src/component/api/configuration.ts new file mode 100644 index 00000000..3ac55f1f --- /dev/null +++ b/client/src/component/api/configuration.ts @@ -0,0 +1,110 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ConfigurationParameters { + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + serverIndex?: number; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * override server index + * + * @type {number} + * @memberof Configuration + */ + serverIndex?: number; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.serverIndex = param.serverIndex; + this.baseOptions = param.baseOptions; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); + return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); + } +} diff --git a/client/src/component/api/index.ts b/client/src/component/api/index.ts new file mode 100644 index 00000000..077cfecf --- /dev/null +++ b/client/src/component/api/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; +export * from "./models"; diff --git a/client/src/component/api/models/batch-request.ts b/client/src/component/api/models/batch-request.ts new file mode 100644 index 00000000..4b818305 --- /dev/null +++ b/client/src/component/api/models/batch-request.ts @@ -0,0 +1,51 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface BatchRequest + */ +export interface BatchRequest { + /** + * + * @type {string} + * @memberof BatchRequest + */ + 'path'?: string; + /** + * + * @type {string} + * @memberof BatchRequest + */ + 'method'?: BatchRequestMethodEnum; + /** + * Request body + * @type {any} + * @memberof BatchRequest + */ + 'body'?: any; +} + +export const BatchRequestMethodEnum = { + GET: 'GET', + POST: 'POST', + DELETE: 'DELETE' +} as const; + +export type BatchRequestMethodEnum = typeof BatchRequestMethodEnum[keyof typeof BatchRequestMethodEnum]; + + diff --git a/client/src/component/api/models/batch-response.ts b/client/src/component/api/models/batch-response.ts new file mode 100644 index 00000000..6a1cd77f --- /dev/null +++ b/client/src/component/api/models/batch-response.ts @@ -0,0 +1,57 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface BatchResponse + */ +export interface BatchResponse { + /** + * + * @type {string} + * @memberof BatchResponse + */ + 'path'?: string; + /** + * + * @type {string} + * @memberof BatchResponse + */ + 'method'?: BatchResponseMethodEnum; + /** + * Response body + * @type {any} + * @memberof BatchResponse + */ + 'body'?: any; + /** + * + * @type {number} + * @memberof BatchResponse + */ + 'status'?: number; +} + +export const BatchResponseMethodEnum = { + GET: 'GET', + POST: 'POST', + DELETE: 'DELETE' +} as const; + +export type BatchResponseMethodEnum = typeof BatchResponseMethodEnum[keyof typeof BatchResponseMethodEnum]; + + diff --git a/client/src/component/api/models/broadcast-status.ts b/client/src/component/api/models/broadcast-status.ts new file mode 100644 index 00000000..946bf17a --- /dev/null +++ b/client/src/component/api/models/broadcast-status.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface BroadcastStatus + */ +export interface BroadcastStatus { + /** + * + * @type {string} + * @memberof BroadcastStatus + */ + 'url'?: string; + /** + * + * @type {boolean} + * @memberof BroadcastStatus + */ + 'is_active'?: boolean; +} + diff --git a/client/src/component/api/models/clipboard-text.ts b/client/src/component/api/models/clipboard-text.ts new file mode 100644 index 00000000..d60f0346 --- /dev/null +++ b/client/src/component/api/models/clipboard-text.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface ClipboardText + */ +export interface ClipboardText { + /** + * + * @type {string} + * @memberof ClipboardText + */ + 'text'?: string; + /** + * + * @type {string} + * @memberof ClipboardText + */ + 'html'?: string; +} + diff --git a/client/src/component/api/models/control-status.ts b/client/src/component/api/models/control-status.ts new file mode 100644 index 00000000..d381754c --- /dev/null +++ b/client/src/component/api/models/control-status.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface ControlStatus + */ +export interface ControlStatus { + /** + * + * @type {boolean} + * @memberof ControlStatus + */ + 'has_host'?: boolean; + /** + * + * @type {string} + * @memberof ControlStatus + */ + 'host_id'?: string; +} + diff --git a/client/src/component/api/models/error-message.ts b/client/src/component/api/models/error-message.ts new file mode 100644 index 00000000..abeb3de6 --- /dev/null +++ b/client/src/component/api/models/error-message.ts @@ -0,0 +1,30 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface ErrorMessage + */ +export interface ErrorMessage { + /** + * + * @type {string} + * @memberof ErrorMessage + */ + 'message'?: string; +} + diff --git a/client/src/component/api/models/index.ts b/client/src/component/api/models/index.ts new file mode 100644 index 00000000..8070542b --- /dev/null +++ b/client/src/component/api/models/index.ts @@ -0,0 +1,19 @@ +export * from './batch-request'; +export * from './batch-response'; +export * from './broadcast-status'; +export * from './clipboard-text'; +export * from './control-status'; +export * from './error-message'; +export * from './keyboard-map'; +export * from './keyboard-modifiers'; +export * from './member-bulk-delete'; +export * from './member-bulk-update'; +export * from './member-create'; +export * from './member-data'; +export * from './member-password'; +export * from './member-profile'; +export * from './screen-configuration'; +export * from './session-data'; +export * from './session-login'; +export * from './session-state'; +export * from './settings'; diff --git a/client/src/component/api/models/keyboard-map.ts b/client/src/component/api/models/keyboard-map.ts new file mode 100644 index 00000000..6cb5a0e4 --- /dev/null +++ b/client/src/component/api/models/keyboard-map.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface KeyboardMap + */ +export interface KeyboardMap { + /** + * + * @type {string} + * @memberof KeyboardMap + */ + 'layout'?: string; + /** + * + * @type {string} + * @memberof KeyboardMap + */ + 'variant'?: string; +} + diff --git a/client/src/component/api/models/keyboard-modifiers.ts b/client/src/component/api/models/keyboard-modifiers.ts new file mode 100644 index 00000000..b8d283f3 --- /dev/null +++ b/client/src/component/api/models/keyboard-modifiers.ts @@ -0,0 +1,72 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface KeyboardModifiers + */ +export interface KeyboardModifiers { + /** + * + * @type {boolean} + * @memberof KeyboardModifiers + */ + 'shift'?: boolean; + /** + * + * @type {boolean} + * @memberof KeyboardModifiers + */ + 'capslock'?: boolean; + /** + * + * @type {boolean} + * @memberof KeyboardModifiers + */ + 'control'?: boolean; + /** + * + * @type {boolean} + * @memberof KeyboardModifiers + */ + 'alt'?: boolean; + /** + * + * @type {boolean} + * @memberof KeyboardModifiers + */ + 'numlock'?: boolean; + /** + * + * @type {boolean} + * @memberof KeyboardModifiers + */ + 'meta'?: boolean; + /** + * + * @type {boolean} + * @memberof KeyboardModifiers + */ + 'super'?: boolean; + /** + * + * @type {boolean} + * @memberof KeyboardModifiers + */ + 'altgr'?: boolean; +} + diff --git a/client/src/component/api/models/member-bulk-delete.ts b/client/src/component/api/models/member-bulk-delete.ts new file mode 100644 index 00000000..42f3502c --- /dev/null +++ b/client/src/component/api/models/member-bulk-delete.ts @@ -0,0 +1,30 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface MemberBulkDelete + */ +export interface MemberBulkDelete { + /** + * + * @type {Array} + * @memberof MemberBulkDelete + */ + 'ids'?: Array; +} + diff --git a/client/src/component/api/models/member-bulk-update.ts b/client/src/component/api/models/member-bulk-update.ts new file mode 100644 index 00000000..d880663d --- /dev/null +++ b/client/src/component/api/models/member-bulk-update.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { MemberProfile } from './member-profile'; + +/** + * + * @export + * @interface MemberBulkUpdate + */ +export interface MemberBulkUpdate { + /** + * + * @type {Array} + * @memberof MemberBulkUpdate + */ + 'ids'?: Array; + /** + * + * @type {MemberProfile} + * @memberof MemberBulkUpdate + */ + 'profile'?: MemberProfile; +} + diff --git a/client/src/component/api/models/member-create.ts b/client/src/component/api/models/member-create.ts new file mode 100644 index 00000000..fd0ca750 --- /dev/null +++ b/client/src/component/api/models/member-create.ts @@ -0,0 +1,45 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { MemberProfile } from './member-profile'; + +/** + * + * @export + * @interface MemberCreate + */ +export interface MemberCreate { + /** + * + * @type {string} + * @memberof MemberCreate + */ + 'username'?: string; + /** + * + * @type {string} + * @memberof MemberCreate + */ + 'password'?: string; + /** + * + * @type {MemberProfile} + * @memberof MemberCreate + */ + 'profile'?: MemberProfile; +} + diff --git a/client/src/component/api/models/member-data.ts b/client/src/component/api/models/member-data.ts new file mode 100644 index 00000000..1fe32bd0 --- /dev/null +++ b/client/src/component/api/models/member-data.ts @@ -0,0 +1,39 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { MemberProfile } from './member-profile'; + +/** + * + * @export + * @interface MemberData + */ +export interface MemberData { + /** + * + * @type {string} + * @memberof MemberData + */ + 'id'?: string; + /** + * + * @type {MemberProfile} + * @memberof MemberData + */ + 'profile'?: MemberProfile; +} + diff --git a/client/src/component/api/models/member-password.ts b/client/src/component/api/models/member-password.ts new file mode 100644 index 00000000..53f3efca --- /dev/null +++ b/client/src/component/api/models/member-password.ts @@ -0,0 +1,30 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface MemberPassword + */ +export interface MemberPassword { + /** + * + * @type {string} + * @memberof MemberPassword + */ + 'password'?: string; +} + diff --git a/client/src/component/api/models/member-profile.ts b/client/src/component/api/models/member-profile.ts new file mode 100644 index 00000000..04866a8f --- /dev/null +++ b/client/src/component/api/models/member-profile.ts @@ -0,0 +1,90 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface MemberProfile + */ +export interface MemberProfile { + /** + * + * @type {string} + * @memberof MemberProfile + */ + 'name'?: string; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'is_admin'?: boolean; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'can_login'?: boolean; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'can_connect'?: boolean; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'can_watch'?: boolean; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'can_host'?: boolean; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'can_share_media'?: boolean; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'can_access_clipboard'?: boolean; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'sends_inactive_cursor'?: boolean; + /** + * + * @type {boolean} + * @memberof MemberProfile + */ + 'can_see_inactive_cursors'?: boolean; + /** + * + * @type {{ [key: string]: any; }} + * @memberof MemberProfile + */ + 'plugins'?: { [key: string]: any; }; +} + diff --git a/client/src/component/api/models/screen-configuration.ts b/client/src/component/api/models/screen-configuration.ts new file mode 100644 index 00000000..1bc6204b --- /dev/null +++ b/client/src/component/api/models/screen-configuration.ts @@ -0,0 +1,42 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface ScreenConfiguration + */ +export interface ScreenConfiguration { + /** + * + * @type {number} + * @memberof ScreenConfiguration + */ + 'width'?: number; + /** + * + * @type {number} + * @memberof ScreenConfiguration + */ + 'height'?: number; + /** + * + * @type {number} + * @memberof ScreenConfiguration + */ + 'rate'?: number; +} + diff --git a/client/src/component/api/models/session-data.ts b/client/src/component/api/models/session-data.ts new file mode 100644 index 00000000..be3af06b --- /dev/null +++ b/client/src/component/api/models/session-data.ts @@ -0,0 +1,54 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +// May contain unused imports in some cases +// @ts-ignore +import type { MemberProfile } from './member-profile'; +// May contain unused imports in some cases +// @ts-ignore +import type { SessionState } from './session-state'; + +/** + * + * @export + * @interface SessionData + */ +export interface SessionData { + /** + * + * @type {string} + * @memberof SessionData + */ + 'id'?: string; + /** + * Only if cookie authentication is disabled. + * @type {string} + * @memberof SessionData + */ + 'token'?: string; + /** + * + * @type {MemberProfile} + * @memberof SessionData + */ + 'profile'?: MemberProfile; + /** + * + * @type {SessionState} + * @memberof SessionData + */ + 'state'?: SessionState; +} + diff --git a/client/src/component/api/models/session-login.ts b/client/src/component/api/models/session-login.ts new file mode 100644 index 00000000..accd29fa --- /dev/null +++ b/client/src/component/api/models/session-login.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface SessionLogin + */ +export interface SessionLogin { + /** + * + * @type {string} + * @memberof SessionLogin + */ + 'username'?: string; + /** + * + * @type {string} + * @memberof SessionLogin + */ + 'password'?: string; +} + diff --git a/client/src/component/api/models/session-state.ts b/client/src/component/api/models/session-state.ts new file mode 100644 index 00000000..e9c0bf3e --- /dev/null +++ b/client/src/component/api/models/session-state.ts @@ -0,0 +1,36 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface SessionState + */ +export interface SessionState { + /** + * + * @type {boolean} + * @memberof SessionState + */ + 'is_connected'?: boolean; + /** + * + * @type {boolean} + * @memberof SessionState + */ + 'is_watching'?: boolean; +} + diff --git a/client/src/component/api/models/settings.ts b/client/src/component/api/models/settings.ts new file mode 100644 index 00000000..8c57ba74 --- /dev/null +++ b/client/src/component/api/models/settings.ts @@ -0,0 +1,60 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface Settings + */ +export interface Settings { + /** + * + * @type {boolean} + * @memberof Settings + */ + 'private_mode'?: boolean; + /** + * + * @type {boolean} + * @memberof Settings + */ + 'locked_controls'?: boolean; + /** + * + * @type {boolean} + * @memberof Settings + */ + 'implicit_hosting'?: boolean; + /** + * + * @type {boolean} + * @memberof Settings + */ + 'inactive_cursors'?: boolean; + /** + * + * @type {boolean} + * @memberof Settings + */ + 'merciful_reconnect'?: boolean; + /** + * + * @type {{ [key: string]: any; }} + * @memberof Settings + */ + 'plugins'?: { [key: string]: any; }; +} + diff --git a/client/src/component/cursors.vue b/client/src/component/cursors.vue new file mode 100644 index 00000000..acd58e1c --- /dev/null +++ b/client/src/component/cursors.vue @@ -0,0 +1,266 @@ + + + + + diff --git a/client/src/component/internal/api.ts b/client/src/component/internal/api.ts new file mode 100644 index 00000000..d7f07236 --- /dev/null +++ b/client/src/component/internal/api.ts @@ -0,0 +1,41 @@ +import * as Api from '../api' + +export class NekoApi { + public readonly config = new Api.Configuration({ + basePath: location.href.replace(/\/+$/, ''), + baseOptions: { withCredentials: true }, + }) + + public setUrl(url: string) { + this.config.basePath = url.replace(/\/+$/, '') + } + + public setToken(token: string) { + this.config.accessToken = token + } + + get url(): string { + return this.config.basePath || location.href.replace(/\/+$/, '') + } + + get default(): DefaultApi { + return new Api.DefaultApi(this.config) + } + + get sessions(): SessionsApi { + return new Api.SessionsApi(this.config) + } + + get room(): RoomApi { + return new Api.RoomApi(this.config) + } + + get members(): MembersApi { + return new Api.MembersApi(this.config) + } +} + +export type DefaultApi = Api.DefaultApi +export type SessionsApi = Api.SessionsApi +export type RoomApi = Api.RoomApi +export type MembersApi = Api.MembersApi diff --git a/client/src/component/internal/connection.ts b/client/src/component/internal/connection.ts new file mode 100644 index 00000000..d00e1f02 --- /dev/null +++ b/client/src/component/internal/connection.ts @@ -0,0 +1,263 @@ +import EventEmitter from 'eventemitter3' +import * as EVENT from '../types/events' +import type * as webrtcTypes from '../types/webrtc' + +import { NekoWebSocket } from './websocket' +import { NekoLoggerFactory } from './logger' +import { NekoWebRTC } from './webrtc' +import type { Connection, WebRTCStats } from '../types/state' + +import { Reconnector } from './reconnector' +import { WebsocketReconnector } from './reconnector/websocket' +import { WebrtcReconnector } from './reconnector/webrtc' +import type { Logger } from '../utils/logger' + +const WEBRTC_RECONN_MAX_LOSS = 25 +const WEBRTC_RECONN_FAILED_ATTEMPTS = 5 + +const WEBRTC_FALLBACK_TIMEOUT_MS = 750 + +export interface NekoConnectionEvents { + close: (error?: Error) => void +} + +export class NekoConnection extends EventEmitter { + private _open = false + private _closing = false + private _peerRequest?: webrtcTypes.PeerRequest + + public websocket = new NekoWebSocket() + public logger = new NekoLoggerFactory(this.websocket) + public webrtc = new NekoWebRTC(this.logger.new('webrtc')) + + private _reconnector: { + websocket: Reconnector + webrtc: Reconnector + } + + private _onConnectHandle: () => void + private _onDisconnectHandle: () => void + private _onCloseHandle: (error?: Error) => void + + private _webrtcStatsHandle: (stats: WebRTCStats) => void + private _webrtcStableHandle: (isStable: boolean) => void + private _webrtcCongestionControlHandle: (stats: WebRTCStats) => void + + // eslint-disable-next-line + constructor( + private readonly _state: Connection, + ) { + super() + + this._reconnector = { + websocket: new Reconnector(new WebsocketReconnector(_state, this.websocket), _state.websocket.config), + webrtc: new Reconnector(new WebrtcReconnector(_state, this.websocket, this.webrtc), _state.webrtc.config), + } + + this._onConnectHandle = () => { + this._state.websocket.connected = this.websocket.connected // TODO: Vue.Set + this._state.webrtc.connected = this.webrtc.connected // TODO: Vue.Set + + if (this._state.status !== 'connected' && this.websocket.connected && this.webrtc.connected) { + this._state.status = 'connected' // TODO: Vue.Set + } + + if (this.websocket.connected && !this.webrtc.connected) { + // if custom peer request is set, send custom peer request + if (this._peerRequest) { + this.websocket.send(EVENT.SIGNAL_REQUEST, this._peerRequest) + this._peerRequest = undefined + } else { + // otherwise use reconnectors connect method + this._reconnector.webrtc.connect() + } + } + } + + this._onDisconnectHandle = () => { + this._state.websocket.connected = this.websocket.connected // TODO: Vue.Set + this._state.webrtc.connected = this.webrtc.connected // TODO: Vue.Set + + if (this._state.webrtc.stable && !this.webrtc.connected) { + this._state.webrtc.stable = false // TODO: Vue.Set + } + + if (this._state.status === 'connected' && this.activated) { + this._state.status = 'connecting' // TODO: Vue.Set + } + } + + this._onCloseHandle = this.close.bind(this) + + // bind events to all reconnectors + Object.values(this._reconnector).forEach((r) => { + r.on('connect', this._onConnectHandle) + r.on('disconnect', this._onDisconnectHandle) + r.on('close', this._onCloseHandle) + }) + + // synchronize webrtc stats with global state + this._webrtcStatsHandle = (stats: WebRTCStats) => { + this._state.webrtc.stats = stats // TODO: Vue.Set + } + this.webrtc.on('stats', this._webrtcStatsHandle) + + // synchronize webrtc stable with global state + this._webrtcStableHandle = (isStable: boolean) => { + this._state.webrtc.stable = isStable // TODO: Vue.Set + } + this.webrtc.on('stable', this._webrtcStableHandle) + + // + // TODO: Use server side congestion control. + // + + let webrtcCongestion: number = 0 + let webrtcFallbackTimeout: number + + this._webrtcCongestionControlHandle = (stats: WebRTCStats) => { + // if automatic quality adjusting is turned off + if (this._state.webrtc.video.auto) return + + // when connection is paused or video disabled, 0fps and muted track is expected + if (stats.paused || this._state.webrtc.video.disabled) return + + // if automatic quality adjusting is turned off + if (!this._reconnector.webrtc.isOpen) return + + // if there are no or just one quality, no switching can be done + if (this._state.webrtc.videos.length <= 1) return + + // current quality is not known + if (this._state.webrtc.video.id == '') return + + // check if video is not playing smoothly + if (stats.fps && stats.packetLoss < WEBRTC_RECONN_MAX_LOSS && !stats.muted) { + if (webrtcFallbackTimeout) { + window.clearTimeout(webrtcFallbackTimeout) + } + + this._state.webrtc.connected = true // TODO: Vue.Set + webrtcCongestion = 0 + return + } + + // try to downgrade quality if it happend many times + if (++webrtcCongestion >= WEBRTC_RECONN_FAILED_ATTEMPTS) { + webrtcFallbackTimeout = window.setTimeout(() => { + this._state.webrtc.connected = false // TODO: Vue.Set + }, WEBRTC_FALLBACK_TIMEOUT_MS) + + webrtcCongestion = 0 + + const quality = this._webrtcQualityDowngrade(this._state.webrtc.video.id) + + // downgrade if lower video quality exists + if (quality && this.webrtc.connected) { + this.websocket.send(EVENT.SIGNAL_VIDEO, { video: quality }) + } + + // try to perform ice restart, if available + if (this.webrtc.open) { + this.websocket.send(EVENT.SIGNAL_RESTART) + return + } + + // try to reconnect webrtc + this._reconnector.webrtc.reconnect() + } + } + this.webrtc.on('stats', this._webrtcCongestionControlHandle) + } + + public get activated() { + // check if every reconnecter is open + return Object.values(this._reconnector).every((r) => r.isOpen) + } + + public reloadConfigs() { + this._reconnector.websocket.config = this._state.websocket.config + this._reconnector.webrtc.config = this._state.webrtc.config + } + + public getLogger(scope?: string): Logger { + return this.logger.new(scope) + } + + public open(peerRequest?: webrtcTypes.PeerRequest) { + if (this._open) { + throw new Error('connection already open') + } + + this._open = true + this._peerRequest = peerRequest + + this._state.status = 'connecting' // TODO: Vue.Set + + // open all reconnectors with deferred connection + Object.values(this._reconnector).forEach((r) => r.open(true)) + + this._reconnector.websocket.connect() + } + + public close(error?: Error) { + // we want to make sure that close event is only emitted once + // and is not intercepted by any other close event + const active = this._open && !this._closing + + if (active) { + // set state to disconnected + this._state.websocket.connected = false // TODO: Vue.Set + this._state.webrtc.connected = false // TODO: Vue.Set + this._state.status = 'disconnected' // TODO: Vue.Set + this._closing = true + } + + // close all reconnectors + Object.values(this._reconnector).forEach((r) => r.close()) + + if (active) { + this._open = false + this._closing = false + this.emit('close', error) + } + } + + public destroy() { + this.logger.destroy() + + this.webrtc.off('stats', this._webrtcStatsHandle) + this.webrtc.off('stable', this._webrtcStableHandle) + // TODO: Use server side congestion control. + this.webrtc.off('stats', this._webrtcCongestionControlHandle) + + // unbind events from all reconnectors + Object.values(this._reconnector).forEach((r) => { + r.off('connect', this._onConnectHandle) + r.off('disconnect', this._onDisconnectHandle) + r.off('close', this._onCloseHandle) + }) + + // destroy all reconnectors + Object.values(this._reconnector).forEach((r) => r.destroy()) + + // set state to disconnected + this._state.websocket.connected = false // TODO: Vue.Set + this._state.webrtc.connected = false // TODO: Vue.Set + this._state.status = 'disconnected' // TODO: Vue.Set + } + + _webrtcQualityDowngrade(quality: string): string | undefined { + // get index of selected or surrent quality + const index = this._state.webrtc.videos.indexOf(quality) + + // edge case: current quality is not in qualities list + if (index === -1) return + + // current quality is the lowest one + if (index + 1 == this._state.webrtc.videos.length) return + + // downgrade video quality + return this._state.webrtc.videos[index + 1] + } +} diff --git a/client/src/component/internal/control.ts b/client/src/component/internal/control.ts new file mode 100644 index 00000000..0b7d666f --- /dev/null +++ b/client/src/component/internal/control.ts @@ -0,0 +1,166 @@ +import * as EVENT from '../types/events' +import type * as message from '../types/messages' + +import EventEmitter from 'eventemitter3' +import type { NekoConnection } from './connection' +import type { Control } from '../types/state' + +export interface NekoControlEvents { + ['overlay.click']: (e: MouseEvent) => void + ['overlay.contextmenu']: (e: MouseEvent) => void +} + +export interface ControlPos { + x: number + y: number +} + +export interface ControlScroll { + delta_x: number + delta_y: number + control_key?: boolean +} + +export class NekoControl extends EventEmitter { + // eslint-disable-next-line + constructor( + private readonly _connection: NekoConnection, + private readonly _state: Control, + ) { + super() + } + + get useWebrtc() { + // we want to use webrtc if we're connected and we're the host + // because webrtc is faster and it doesn't request control + // in contrast to the websocket + return this._connection.webrtc.connected && this._state.is_host + } + + get enabledTouchEvents() { + return this._state.touch.enabled + } + + get supportedTouchEvents() { + return this._state.touch.supported + } + + public lock() { + this._state.locked = true // TODO: Vue.Set + } + + public unlock() { + this._state.locked = false // TODO: Vue.Set + } + + public request() { + this._connection.websocket.send(EVENT.CONTROL_REQUEST) + } + + public release() { + this._connection.websocket.send(EVENT.CONTROL_RELEASE) + } + + public move(pos: ControlPos) { + if (this.useWebrtc) { + this._connection.webrtc.send('mousemove', pos) + } else { + this._connection.websocket.send(EVENT.CONTROL_MOVE, pos as message.ControlPos) + } + } + + // TODO: add pos parameter + public scroll(scroll: ControlScroll) { + if (this.useWebrtc) { + this._connection.webrtc.send('wheel', scroll) + } else { + this._connection.websocket.send(EVENT.CONTROL_SCROLL, scroll as message.ControlScroll) + } + } + + // buttonpress ensures that only one button is pressed at a time + public buttonPress(code: number, pos?: ControlPos) { + this._connection.websocket.send(EVENT.CONTROL_BUTTONPRESS, { code, ...pos } as message.ControlButton) + } + + public buttonDown(code: number, pos?: ControlPos) { + if (this.useWebrtc) { + if (pos) this._connection.webrtc.send('mousemove', pos) + this._connection.webrtc.send('mousedown', { key: code }) + } else { + this._connection.websocket.send(EVENT.CONTROL_BUTTONDOWN, { code, ...pos } as message.ControlButton) + } + } + + public buttonUp(code: number, pos?: ControlPos) { + if (this.useWebrtc) { + if (pos) this._connection.webrtc.send('mousemove', pos) + this._connection.webrtc.send('mouseup', { key: code }) + } else { + this._connection.websocket.send(EVENT.CONTROL_BUTTONUP, { code, ...pos } as message.ControlButton) + } + } + + // keypress ensures that only one key is pressed at a time + public keyPress(keysym: number, pos?: ControlPos) { + this._connection.websocket.send(EVENT.CONTROL_KEYPRESS, { keysym, ...pos } as message.ControlKey) + } + + public keyDown(keysym: number, pos?: ControlPos) { + if (this.useWebrtc) { + if (pos) this._connection.webrtc.send('mousemove', pos) + this._connection.webrtc.send('keydown', { key: keysym }) + } else { + this._connection.websocket.send(EVENT.CONTROL_KEYDOWN, { keysym, ...pos } as message.ControlKey) + } + } + + public keyUp(keysym: number, pos?: ControlPos) { + if (this.useWebrtc) { + if (pos) this._connection.webrtc.send('mousemove', pos) + this._connection.webrtc.send('keyup', { key: keysym }) + } else { + this._connection.websocket.send(EVENT.CONTROL_KEYUP, { keysym, ...pos } as message.ControlKey) + } + } + + public touchBegin(touch_id: number, pos: ControlPos, pressure: number) { + if (this.useWebrtc) { + this._connection.webrtc.send('touchbegin', { touch_id, ...pos, pressure }) + } else { + this._connection.websocket.send(EVENT.CONTROL_TOUCHBEGIN, { touch_id, ...pos, pressure } as message.ControlTouch) + } + } + + public touchUpdate(touch_id: number, pos: ControlPos, pressure: number) { + if (this.useWebrtc) { + this._connection.webrtc.send('touchupdate', { touch_id, ...pos, pressure }) + } else { + this._connection.websocket.send(EVENT.CONTROL_TOUCHUPDATE, { touch_id, ...pos, pressure } as message.ControlTouch) + } + } + + public touchEnd(touch_id: number, pos: ControlPos, pressure: number) { + if (this.useWebrtc) { + this._connection.webrtc.send('touchend', { touch_id, ...pos, pressure }) + } else { + this._connection.websocket.send(EVENT.CONTROL_TOUCHEND, { touch_id, ...pos, pressure } as message.ControlTouch) + } + } + + public cut() { + this._connection.websocket.send(EVENT.CONTROL_CUT) + } + + public copy() { + this._connection.websocket.send(EVENT.CONTROL_COPY) + } + + public paste(text?: string) { + this._connection.websocket.send(EVENT.CONTROL_PASTE, { text } as message.ClipboardData) + } + + public selectAll() { + this._connection.websocket.send(EVENT.CONTROL_SELECT_ALL) + } +} diff --git a/client/src/component/internal/logger.ts b/client/src/component/internal/logger.ts new file mode 100644 index 00000000..41d6fa55 --- /dev/null +++ b/client/src/component/internal/logger.ts @@ -0,0 +1,130 @@ +import type { NekoWebSocket } from './websocket' +import * as EVENT from '../types/events' +import type * as message from '../types/messages' +import { Logger } from '../utils/logger' + +const MAX_LOG_MESSAGES = 25 +const FLUSH_TIMEOUT_MS = 250 +const RETRY_INTERVAL_MS = 2500 + +export class NekoLoggerFactory { + private _logs: message.SystemLog[] = [] + private _timeout: number | null = null + private _interval: number | null = null + + // eslint-disable-next-line + constructor( + private readonly _ws: NekoWebSocket, + ) {} + + private _flush() { + if (this._logs.length > 0) { + this._ws.send(EVENT.SYSTEM_LOGS, this._logs) + this._logs = [] + } + } + + private _send(level: string, message: string, fields?: Record) { + for (const key in fields) { + const field = fields[key] + + if (field instanceof Error) { + fields[key] = (field as Error).message + } + } + + const payload = { level, message, fields } as message.SystemLog + this._logs.push(payload) + + // rotate if exceeded maximum + if (this._logs.length > MAX_LOG_MESSAGES) { + this._logs.shift() + } + + // postpone logs sending + if (!this._timeout && !this._interval) { + this._timeout = window.setTimeout(() => { + if (!this._timeout) { + return + } + + if (this._ws.connected) { + this._flush() + } else { + this._interval = window.setInterval(() => { + if (!this._ws.connected || !this._interval) { + return + } + + this._flush() + window.clearInterval(this._interval) + this._interval = null + }, RETRY_INTERVAL_MS) + } + + window.clearTimeout(this._timeout) + this._timeout = null + }, FLUSH_TIMEOUT_MS) + } + } + + public new(submodule?: string): Logger { + return new NekoLogger((level: string, message: string, fields?: Record) => { + if (!fields) { + fields = { submodule } + } else { + fields['submodule'] = submodule + } + + this._send(level, message, fields) + }, submodule) + } + + public destroy() { + if (this._ws.connected) { + this._flush() + } + + if (this._interval) { + window.clearInterval(this._interval) + this._interval = null + } + + if (this._timeout) { + window.clearTimeout(this._timeout) + this._timeout = null + } + } +} + +type NekoLoggerMessage = (level: string, message: string, fields?: Record) => void + +export class NekoLogger extends Logger { + private _on_send: NekoLoggerMessage + + constructor(onSend: NekoLoggerMessage, scope?: string) { + super(scope) + + this._on_send = onSend + } + + public error(message: string, fields?: Record) { + this._console('error', message, fields) + this._on_send('error', message, fields) + } + + public warn(message: string, fields?: Record) { + this._console('warn', message, fields) + this._on_send('warn', message, fields) + } + + public info(message: string, fields?: Record) { + this._console('info', message, fields) + this._on_send('info', message, fields) + } + + public debug(message: string, fields?: Record) { + this._console('debug', message, fields) + this._on_send('debug', message, fields) + } +} diff --git a/client/src/component/internal/messages.ts b/client/src/component/internal/messages.ts new file mode 100644 index 00000000..ad821335 --- /dev/null +++ b/client/src/component/internal/messages.ts @@ -0,0 +1,361 @@ +import * as EVENT from '../types/events' +import type * as message from '../types/messages' + +import EventEmitter from 'eventemitter3' +import type { AxiosProgressEvent } from 'axios' +import { Logger } from '../utils/logger' +import type { NekoConnection } from './connection' +import type NekoState from '../types/state' +import type { Settings } from '../types/state' + +export interface NekoEvents { + // connection events + ['connection.status']: (status: 'connected' | 'connecting' | 'disconnected') => void + ['connection.type']: (status: 'fallback' | 'webrtc' | 'none') => void + ['connection.webrtc.sdp']: (type: 'local' | 'remote', data: string) => void + ['connection.webrtc.sdp.candidate']: (type: 'local' | 'remote', data: RTCIceCandidateInit) => void + ['connection.closed']: (error?: Error) => void + + // drag and drop events + ['upload.drop.started']: () => void + ['upload.drop.progress']: (progressEvent: AxiosProgressEvent) => void + ['upload.drop.finished']: (error?: Error) => void + + // upload dialog events + ['upload.dialog.requested']: () => void + ['upload.dialog.overlay']: (id: string) => void + ['upload.dialog.closed']: () => void + + // custom messages events + ['receive.unicast']: (sender: string, subject: string, body: any) => void + ['receive.broadcast']: (sender: string, subject: string, body: any) => void + + // session events + ['session.created']: (id: string) => void + ['session.deleted']: (id: string) => void + ['session.updated']: (id: string) => void + + // room events + ['room.control.host']: (hasHost: boolean, hostID: string | undefined, id: string) => void + ['room.control.request']: (id: string) => void + ['room.screen.updated']: (width: number, height: number, rate: number, id: string) => void + ['room.settings.updated']: (settings: Settings, id: string) => void + ['room.clipboard.updated']: (text: string) => void + ['room.broadcast.status']: (isActive: boolean, url?: string) => void + + // external message events + ['message']: (event: string, payload: any) => void +} + +export class NekoMessages extends EventEmitter { + private _localLog: Logger + private _remoteLog: Logger + + // eslint-disable-next-line + constructor( + private readonly _connection: NekoConnection, + private readonly _state: NekoState, + ) { + super() + + this._localLog = new Logger('messages') + this._remoteLog = _connection.getLogger('messages') + + this._connection.websocket.on('message', async (event: string, payload: any) => { + // @ts-ignore + if (typeof this[event] === 'function') { + try { + // @ts-ignore + await this[event](payload) + } catch (error: any) { + this._remoteLog.error(`error while processing websocket event`, { event, error }) + } + } else { + this._remoteLog.debug(`emitting external message`, { event, payload }) + this.emit('message', event, payload) + } + }) + + this._connection.webrtc.on('candidate', (candidate: RTCIceCandidateInit) => { + this._connection.websocket.send(EVENT.SIGNAL_CANDIDATE, candidate) + this.emit('connection.webrtc.sdp.candidate', 'local', candidate) + }) + + this._connection.webrtc.on('negotiation', ({ sdp, type }: RTCSessionDescriptionInit) => { + if (!sdp) { + this._remoteLog.warn(`sdp empty while negotiation event`) + return + } + + if (type == 'answer') { + this._connection.websocket.send(EVENT.SIGNAL_ANSWER, { sdp }) + } else if (type == 'offer') { + this._connection.websocket.send(EVENT.SIGNAL_OFFER, { sdp }) + } else { + this._remoteLog.warn(`unsupported negotiation type`, { type }) + } + + // TODO: Return whole signal description (if answer / offer). + this.emit('connection.webrtc.sdp', 'local', sdp) + }) + } + + ///////////////////////////// + // System Events + ///////////////////////////// + + protected [EVENT.SYSTEM_INIT](conf: message.SystemInit) { + this._localLog.debug(`EVENT.SYSTEM_INIT`) + this._state.session_id = conf.session_id // TODO: Vue.Set + // check if backend supports touch events + this._state.control.touch.supported = conf.touch_events // TODO: Vue.Set + this._state.connection.screencast = conf.screencast_enabled // TODO: Vue.Set + this._state.connection.webrtc.videos = conf.webrtc.videos // TODO: Vue.Set + + for (const id in conf.sessions) { + this[EVENT.SESSION_CREATED](conf.sessions[id]) + } + + const { width, height, rate } = conf.screen_size + this._state.screen.size = { width, height, rate } // TODO: Vue.Set + this[EVENT.CONTROL_HOST](conf.control_host) + this._state.settings = conf.settings // TODO: Vue.Set + } + + protected [EVENT.SYSTEM_ADMIN]({ screen_sizes_list, broadcast_status }: message.SystemAdmin) { + this._localLog.debug(`EVENT.SYSTEM_ADMIN`) + + const list = screen_sizes_list.sort((a, b) => { + if (b.width === a.width && b.height == a.height) { + return b.rate - a.rate + } else if (b.width === a.width) { + return b.height - a.height + } + return b.width - a.width + }) + + this._state.screen.configurations = list // TODO: Vue.Set + + this[EVENT.BORADCAST_STATUS](broadcast_status) + } + + protected [EVENT.SYSTEM_SETTINGS]({ id, ...settings }: message.SystemSettingsUpdate) { + this._localLog.debug(`EVENT.SYSTEM_SETTINGS`) + this._state.settings = settings // TODO: Vue.Set + this.emit('room.settings.updated', settings, id) + } + + protected [EVENT.SYSTEM_DISCONNECT]({ message }: message.SystemDisconnect) { + this._localLog.debug(`EVENT.SYSTEM_DISCONNECT`) + this._connection.close(new Error(message)) + } + + ///////////////////////////// + // Signal Events + ///////////////////////////// + + protected async [EVENT.SIGNAL_PROVIDE]({ sdp, iceservers, video, audio }: message.SignalProvide) { + this._localLog.debug(`EVENT.SIGNAL_PROVIDE`) + + // create WebRTC connection + await this._connection.webrtc.connect(iceservers) + + // set remote offer + await this._connection.webrtc.setOffer(sdp) + + // TODO: Return whole signal description (if answer / offer). + this.emit('connection.webrtc.sdp', 'remote', sdp) + + this[EVENT.SIGNAL_VIDEO](video) + this[EVENT.SIGNAL_AUDIO](audio) + } + + protected async [EVENT.SIGNAL_OFFER]({ sdp }: message.SignalDescription) { + this._localLog.debug(`EVENT.SIGNAL_OFFER`) + + // set remote offer + await this._connection.webrtc.setOffer(sdp) + + // TODO: Return whole signal description (if answer / offer). + this.emit('connection.webrtc.sdp', 'remote', sdp) + } + + protected async [EVENT.SIGNAL_ANSWER]({ sdp }: message.SignalDescription) { + this._localLog.debug(`EVENT.SIGNAL_ANSWER`) + + // set remote answer + await this._connection.webrtc.setAnswer(sdp) + + // TODO: Return whole signal description (if answer / offer). + this.emit('connection.webrtc.sdp', 'remote', sdp) + } + + // TODO: Use offer event intead. + protected async [EVENT.SIGNAL_RESTART]({ sdp }: message.SignalDescription) { + this[EVENT.SIGNAL_OFFER]({ sdp }) + } + + protected async [EVENT.SIGNAL_CANDIDATE](candidate: message.SignalCandidate) { + this._localLog.debug(`EVENT.SIGNAL_CANDIDATE`) + + // set remote candidate + await this._connection.webrtc.setCandidate(candidate) + this.emit('connection.webrtc.sdp.candidate', 'remote', candidate) + } + + protected [EVENT.SIGNAL_VIDEO]({ disabled, id, auto }: message.SignalVideo) { + this._localLog.debug(`EVENT.SIGNAL_VIDEO`, { disabled, id, auto }) + this._state.connection.webrtc.video.disabled = disabled // TODO: Vue.Set + this._state.connection.webrtc.video.id = id // TODO: Vue.Set + this._state.connection.webrtc.video.auto = auto // TODO: Vue.Set + } + + protected [EVENT.SIGNAL_AUDIO]({ disabled }: message.SignalAudio) { + this._localLog.debug(`EVENT.SIGNAL_AUDIO`, { disabled }) + this._state.connection.webrtc.audio.disabled = disabled // TODO: Vue.Set + } + + protected [EVENT.SIGNAL_CLOSE]() { + this._localLog.debug(`EVENT.SIGNAL_CLOSE`) + this._connection.webrtc.close() + } + + ///////////////////////////// + // Session Events + ///////////////////////////// + + protected [EVENT.SESSION_CREATED]({ id, profile, ...state }: message.SessionData) { + this._localLog.debug(`EVENT.SESSION_CREATED`, { id }) + this._state.sessions[id] = { id, profile, state } // TODO: Vue.Set + this.emit('session.created', id) + } + + protected [EVENT.SESSION_DELETED]({ id }: message.SessionID) { + this._localLog.debug(`EVENT.SESSION_DELETED`, { id }) + delete this._state.sessions[id] // TODO: Vue.Delete + this.emit('session.deleted', id) + } + + protected [EVENT.SESSION_PROFILE]({ id, ...profile }: message.MemberProfile) { + if (id in this._state.sessions) { + this._localLog.debug(`EVENT.SESSION_PROFILE`, { id }) + this._state.sessions[id].profile = profile // TODO: Vue.Set + this.emit('session.updated', id) + } + } + + protected [EVENT.SESSION_STATE]({ id, ...state }: message.SessionState) { + if (id in this._state.sessions) { + this._localLog.debug(`EVENT.SESSION_STATE`, { id }) + this._state.sessions[id].state = state // TODO: Vue.Set + this.emit('session.updated', id) + } + } + + protected [EVENT.SESSION_CURSORS](cursors: message.SessionCursor[]) { + // + // TODO: Resolve conflict with state.cursors. + // + //@ts-ignore + this._state.cursors = cursors // TODO: Vue.Set + } + + ///////////////////////////// + // Control Events + ///////////////////////////// + + protected [EVENT.CONTROL_REQUEST]({ id }: message.SessionID) { + this._localLog.debug(`EVENT.CONTROL_REQUEST`) + this.emit('room.control.request', id) + } + + protected [EVENT.CONTROL_HOST]({ has_host, host_id, id }: message.ControlHost) { + this._localLog.debug(`EVENT.CONTROL_HOST`) + + if (has_host && host_id) { + this._state.control.host_id = host_id // TODO: Vue.Set + } else { + this._state.control.host_id = null // TODO: Vue.Set + } + + // save if user is host + this._state.control.is_host = has_host && this._state.control.host_id === this._state.session_id // TODO: Vue.Set + + this.emit('room.control.host', has_host, host_id, id) + } + + ///////////////////////////// + // Screen Events + ///////////////////////////// + + protected [EVENT.SCREEN_UPDATED]({ width, height, rate, id }: message.ScreenSizeUpdate) { + this._localLog.debug(`EVENT.SCREEN_UPDATED`) + this._state.screen.size = { width, height, rate } // TODO: Vue.Set + this.emit('room.screen.updated', width, height, rate, id) + } + + ///////////////////////////// + // Clipboard Events + ///////////////////////////// + + protected [EVENT.CLIPBOARD_UPDATED]({ text }: message.ClipboardData) { + this._localLog.debug(`EVENT.CLIPBOARD_UPDATED`) + this._state.control.clipboard = { text } // TODO: Vue.Set + + try { + navigator.clipboard.writeText(text) // sync user's clipboard + } catch (error: any) { + this._remoteLog.warn(`unable to write text to client's clipboard`, { + error, + // works only for HTTPs + protocol: location.protocol, + clipboard: typeof navigator.clipboard, + }) + } + + this.emit('room.clipboard.updated', text) + } + + ///////////////////////////// + // Broadcast Events + ///////////////////////////// + + protected [EVENT.BORADCAST_STATUS]({ url, is_active }: message.BroadcastStatus) { + this._localLog.debug(`EVENT.BORADCAST_STATUS`) + // TODO: Handle. + this.emit('room.broadcast.status', is_active, url) + } + + ///////////////////////////// + // Send Events + ///////////////////////////// + + protected [EVENT.SEND_UNICAST]({ sender, subject, body }: message.SendMessage) { + this._localLog.debug(`EVENT.SEND_UNICAST`) + this.emit('receive.unicast', sender, subject, body) + } + + protected [EVENT.SEND_BROADCAST]({ sender, subject, body }: message.SendMessage) { + this._localLog.debug(`EVENT.BORADCAST_STATUS`) + this.emit('receive.broadcast', sender, subject, body) + } + + ///////////////////////////// + // FileChooserDialog Events + ///////////////////////////// + + protected [EVENT.FILE_CHOOSER_DIALOG_OPENED]({ id }: message.SessionID) { + this._localLog.debug(`EVENT.FILE_CHOOSER_DIALOG_OPENED`) + + if (id == this._state.session_id) { + this.emit('upload.dialog.requested') + } else { + this.emit('upload.dialog.overlay', id) + } + } + + protected [EVENT.FILE_CHOOSER_DIALOG_CLOSED]({}: message.SessionID) { + this._localLog.debug(`EVENT.FILE_CHOOSER_DIALOG_CLOSED`) + this.emit('upload.dialog.closed') + } +} diff --git a/client/src/component/internal/reconnector/index.ts b/client/src/component/internal/reconnector/index.ts new file mode 100644 index 00000000..6e6ae173 --- /dev/null +++ b/client/src/component/internal/reconnector/index.ts @@ -0,0 +1,222 @@ +import EventEmitter from 'eventemitter3' + +import type { ReconnectorConfig } from '../../types/reconnector' + +export interface ReconnectorAbstractEvents { + connect: () => void + disconnect: (error?: Error) => void +} + +export abstract class ReconnectorAbstract extends EventEmitter { + constructor() { + super() + + if (this.constructor == ReconnectorAbstract) { + throw new Error("Abstract classes can't be instantiated.") + } + } + + public abstract get connected(): boolean + + public abstract connect(): void + public abstract disconnect(): void + public abstract destroy(): void +} + +/* + +Reconnector handles reconnection logic according to supplied config for an abstract class. It can reconnect anything that: +- can be connected to +- can send event once it is connected to +- can be disconnected from +- can send event once it is disconnected from +- can provide information at any moment if it is connected to or not + +Reconnector creates one additional abstract layer for a user. User can open and close a connection. If the connection is open, +when connection will be disconnected, reconnector will attempt to connect to it again. Once connection is closed, no further +events will be emitted and connection will be disconnected. +- When using deferred connection in opening function, reconnector does not try to connect when opening a connection. This is +the initial state, when reconnector is not connected but no reconnect attempts are in progress, since there has not been +any disconnect even. It is up to user to call initial connect attempt. +- Events 'open' and 'close' will be fired exactly once, no matter how many times open() and close() funxtions were called. +- Events 'connecŧ' and 'disconnect' can fire throughout open connection at any time. + +*/ +export interface ReconnectorEvents { + open: () => void + connect: () => void + disconnect: () => void + close: (error?: Error) => void +} + +export class Reconnector extends EventEmitter { + private _config: ReconnectorConfig + private _timeout?: number + + private _open = false + private _total_reconnects = 0 + private _last_connected?: Date + + private _onConnectHandle: () => void + private _onDisconnectHandle: (error?: Error) => void + + // eslint-disable-next-line + constructor( + private readonly _conn: ReconnectorAbstract, + config?: ReconnectorConfig, + ) { + super() + + // setup default config values + this._config = { + max_reconnects: 10, + timeout_ms: 1500, + backoff_ms: 750, + ...config, + } + + // register connect and disconnect handlers with current class + // as 'this' context, store them to a variable so that they + // can be later unregistered + + this._onConnectHandle = this.onConnect.bind(this) + this._conn.on('connect', this._onConnectHandle) + + this._onDisconnectHandle = this.onDisconnect.bind(this) + this._conn.on('disconnect', this._onDisconnectHandle) + } + + private clearTimeout() { + if (this._timeout) { + window.clearTimeout(this._timeout) + this._timeout = undefined + } + } + + private onConnect() { + this.clearTimeout() + + // only if connection is open, fire connect event + if (this._open) { + this._last_connected = new Date() + this._total_reconnects = 0 + this.emit('connect') + } else { + // in this case we are connected but this connection + // has been closed, so we simply disconnect again + this._conn.disconnect() + } + } + + private onDisconnect() { + this.clearTimeout() + + // only if connection is open, fire disconnect event + // and start reconnecteing logic + if (this._open) { + this.emit('disconnect') + this.reconnect() + } + } + + public get isOpen(): boolean { + return this._open + } + + public get isConnected(): boolean { + return this._conn.connected + } + + public get totalReconnects(): number { + return this._total_reconnects + } + + public get lastConnected(): Date | undefined { + return this._last_connected + } + + public get config(): ReconnectorConfig { + return { ...this._config } + } + + public set config(conf: ReconnectorConfig) { + this._config = { ...this._config, ...conf } + + if (this._config.max_reconnects <= this._total_reconnects) { + this.close(new Error('reconnection config changed')) + } + } + + // allows future reconnect attempts and connects if not set + // deferred connection to true + public open(deferredConnection = false): void { + this.clearTimeout() + + // assuming open event can fire multiple times, we need to + // ensure, that open event get fired only once along with + // resetting total reconnects counter + + if (!this._open) { + this._open = true + this._total_reconnects = 0 + this.emit('open') + } + + if (!deferredConnection && !this._conn.connected) { + this.connect() + } + } + + // disconnects and forbids future reconnect attempts + public close(error?: Error): void { + this.clearTimeout() + + // assuming close event can fire multiple times, the same + // precautions need to be taken as in open event, so that + // close event fires only once + + if (this._open) { + this._open = false + this._last_connected = undefined + this.emit('close', error) + } + + // if connected, tries to disconnect even if it has been + // called multiple times by user + + if (this._conn.connected) { + this._conn.disconnect() + } + } + + // tries to connect and calls on disconnected if it could not + // connect within specified timeout according to config + public connect(): void { + this.clearTimeout() + + this._conn.connect() + this._timeout = window.setTimeout(this.onDisconnect.bind(this), this._config.timeout_ms) + } + + // tries to connect with specified backoff time if + // maximum reconnect theshold was not exceeded, otherwise + // closes the connection with an error message + public reconnect(): void { + this.clearTimeout() + + if (this._config.max_reconnects > ++this._total_reconnects || this._config.max_reconnects == -1) { + this._timeout = window.setTimeout(this.connect.bind(this), this._config.backoff_ms) + } else { + this.close(new Error('reconnection failed')) + } + } + + // closes connection and unregisters all events + public destroy() { + this.close(new Error('connection destroyed')) + + this._conn.off('connect', this._onConnectHandle) + this._conn.off('disconnect', this._onDisconnectHandle) + this._conn.destroy() + } +} diff --git a/client/src/component/internal/reconnector/webrtc.ts b/client/src/component/internal/reconnector/webrtc.ts new file mode 100644 index 00000000..26608964 --- /dev/null +++ b/client/src/component/internal/reconnector/webrtc.ts @@ -0,0 +1,71 @@ +import * as EVENT from '../../types/events' +import type { Connection } from '../../types/state' + +import type { NekoWebSocket } from '../websocket' +import type { NekoWebRTC } from '../webrtc' + +import { ReconnectorAbstract } from '.' + +export class WebrtcReconnector extends ReconnectorAbstract { + private _onConnectHandle: () => void + private _onDisconnectHandle: (error?: Error) => void + + // eslint-disable-next-line + constructor( + private readonly _state: Connection, + private readonly _websocket: NekoWebSocket, + private readonly _webrtc: NekoWebRTC, + ) { + super() + + this._onConnectHandle = () => this.emit('connect') + this._webrtc.on('connected', this._onConnectHandle) + + this._onDisconnectHandle = (error?: Error) => this.emit('disconnect', error) + this._webrtc.on('disconnected', this._onDisconnectHandle) + } + + public get connected() { + return this._webrtc.connected + } + + public connect() { + if (!this._webrtc.supported) return + + if (this._webrtc.connected) { + this._webrtc.disconnect() + } + + if (this._websocket.connected) { + // use requests from state to connect with selected values + + let selector = null + if (this._state.webrtc.video.id) { + selector = { + id: this._state.webrtc.video.id, + type: 'exact', + } + } + + this._websocket.send(EVENT.SIGNAL_REQUEST, { + video: { + disabled: this._state.webrtc.video.disabled, + selector, + auto: this._state.webrtc.video.auto, + }, + audio: { + disabled: this._state.webrtc.audio.disabled, + }, + }) + } + } + + public disconnect() { + this._webrtc.disconnect() + } + + public destroy() { + this._webrtc.off('connected', this._onConnectHandle) + this._webrtc.off('disconnected', this._onDisconnectHandle) + } +} diff --git a/client/src/component/internal/reconnector/websocket.ts b/client/src/component/internal/reconnector/websocket.ts new file mode 100644 index 00000000..fd09a06e --- /dev/null +++ b/client/src/component/internal/reconnector/websocket.ts @@ -0,0 +1,55 @@ +import type { Connection } from '../../types/state' + +import type { NekoWebSocket } from '../websocket' + +import { ReconnectorAbstract } from '.' + +export class WebsocketReconnector extends ReconnectorAbstract { + private _onConnectHandle: () => void + private _onDisconnectHandle: (error?: Error) => void + + // eslint-disable-next-line + constructor( + private readonly _state: Connection, + private readonly _websocket: NekoWebSocket, + ) { + super() + + this._onConnectHandle = () => this.emit('connect') + this._websocket.on('connected', this._onConnectHandle) + + this._onDisconnectHandle = (error?: Error) => this.emit('disconnect', error) + this._websocket.on('disconnected', this._onDisconnectHandle) + } + + public get connected() { + return this._websocket.connected + } + + public connect() { + if (!this._websocket.supported) return + + if (this._websocket.connected) { + this._websocket.disconnect('connection replaced') + } + + let url = this._state.url + url = url.replace(/^http/, 'ws').replace(/\/+$/, '') + '/api/ws' + + const token = this._state.token + if (token) { + url += '?token=' + encodeURIComponent(token) + } + + this._websocket.connect(url) + } + + public disconnect() { + this._websocket.disconnect('manual disconnect') + } + + public destroy() { + this._websocket.off('connected', this._onConnectHandle) + this._websocket.off('disconnected', this._onDisconnectHandle) + } +} diff --git a/client/src/component/internal/video.ts b/client/src/component/internal/video.ts new file mode 100644 index 00000000..16306358 --- /dev/null +++ b/client/src/component/internal/video.ts @@ -0,0 +1,30 @@ +import type { Video } from '../types/state' + +export function register(el: HTMLVideoElement, state: Video) { + el.addEventListener('canplaythrough', () => { + state.playable = true + }) + el.addEventListener('playing', () => { + state.playing = true + }) + el.addEventListener('pause', () => { + state.playing = false + }) + el.addEventListener('emptied', () => { + state.playable = false + state.playing = false + }) + el.addEventListener('error', () => { + state.playable = false + state.playing = false + }) + el.addEventListener('volumechange', () => { + state.muted = el.muted + state.volume = el.volume + }) + + // Initial state + state.muted = el.muted + state.volume = el.volume + state.playing = !el.paused +} diff --git a/client/src/component/internal/webrtc.ts b/client/src/component/internal/webrtc.ts new file mode 100644 index 00000000..a736594d --- /dev/null +++ b/client/src/component/internal/webrtc.ts @@ -0,0 +1,626 @@ +import EventEmitter from 'eventemitter3' +import type { WebRTCStats, CursorPosition, CursorImage } from '../types/webrtc' +import { Logger } from '../utils/logger' +import { videoSnap } from '../utils/video-snap' + +const maxUint32 = 2 ** 32 - 1 + +export const OPCODE = { + MOVE: 0x01, + SCROLL: 0x02, + KEY_DOWN: 0x03, + KEY_UP: 0x04, + BTN_DOWN: 0x05, + BTN_UP: 0x06, + PING: 0x07, + // touch events + TOUCH_BEGIN: 0x08, + TOUCH_UPDATE: 0x09, + TOUCH_END: 0x0a, +} as const + +export interface ICEServer { + urls: string[] + username: string + credential: string +} + +export interface NekoWebRTCEvents { + connected: () => void + disconnected: (error?: Error) => void + track: (event: RTCTrackEvent) => void + candidate: (candidate: RTCIceCandidateInit) => void + negotiation: (description: RTCSessionDescriptionInit) => void + stable: (isStable: boolean) => void + stats: (stats: WebRTCStats) => void + fallback: (image: string) => void // send last frame image URL as fallback + ['cursor-position']: (data: CursorPosition) => void + ['cursor-image']: (data: CursorImage) => void +} + +export class NekoWebRTC extends EventEmitter { + // used for creating snaps from video for fallback mode + public video!: HTMLVideoElement + // information for WebRTC that server video has been paused, 0fps is expected + public paused = false + + private _peer?: RTCPeerConnection + private _channel?: RTCDataChannel + private _track?: MediaStreamTrack + private _state: RTCIceConnectionState = 'disconnected' + private _connected = false + private _candidates: RTCIceCandidateInit[] = [] + private _statsStop?: () => void + private _requestLatency = 0 + private _responseLatency = 0 + + // eslint-disable-next-line + constructor( + private readonly _log: Logger = new Logger('webrtc'), + ) { + super() + } + + get supported() { + return typeof RTCPeerConnection !== 'undefined' && typeof RTCPeerConnection.prototype.addTransceiver !== 'undefined' + } + + get open() { + return ( + typeof this._peer !== 'undefined' && typeof this._channel !== 'undefined' && this._channel.readyState == 'open' + ) + } + + get connected() { + return this.open && ['connected', 'checking', 'completed'].includes(this._state) + } + + public async setCandidate(candidate: RTCIceCandidateInit) { + if (!this._peer) { + this._candidates.push(candidate) + return + } + + await this._peer.addIceCandidate(candidate) + this._log.debug(`adding remote ICE candidate`, { candidate }) + } + + public async connect(iceServers: ICEServer[]) { + if (!this.supported) { + throw new Error('browser does not support webrtc') + } + + if (this.connected) { + throw new Error('attempting to create peer while connected') + } + + this._log.info(`connecting`) + + this._connected = false + this._peer = new RTCPeerConnection({ iceServers }) + + if (iceServers.length == 0) { + this._log.warn(`iceservers are empty`) + } + + this._peer.onicecandidate = (event: RTCPeerConnectionIceEvent) => { + if (!event.candidate) { + this._log.debug(`sent all local ICE candidates`) + return + } + + const init = event.candidate.toJSON() + this.emit('candidate', init) + this._log.debug(`sending local ICE candidate`, { init }) + } + + this._peer.onicecandidateerror = (event: Event) => { + const e = event as RTCPeerConnectionIceErrorEvent + const fields = { error: e.errorText, code: e.errorCode, port: e.port, url: e.url } + this._log.warn(`ICE candidate error`, fields) + } + + this._peer.onconnectionstatechange = () => { + if (!this._peer) { + this._log.warn(`attempting to call 'onconnectionstatechange' for nonexistent peer`) + return + } + + const state = this._peer.connectionState + this._log.info(`peer connection state changed`, { state }) + + switch (state) { + case 'connected': + this.onConnected() + break + // Chrome sends failed state change only for connectionState and not iceConnectionState, and firefox + // does not support connectionState at all. + case 'closed': + case 'failed': + this.onDisconnected(new Error('peer ' + state)) + break + } + } + + this._peer.oniceconnectionstatechange = () => { + if (!this._peer) { + this._log.warn(`attempting to call 'oniceconnectionstatechange' for nonexistent peer`) + return + } + + this._state = this._peer.iceConnectionState + this._log.info(`ice connection state changed`, { state: this._state }) + + switch (this._state) { + case 'connected': + this.onConnected() + // Connected event makes connection stable. + this.emit('stable', true) + break + case 'disconnected': + // Disconnected event makes connection unstable, + // may go back to a connected state after some time. + this.emit('stable', false) + break + // We don't watch the disconnected signaling state here as it can indicate temporary issues and may + // go back to a connected state after some time. Watching it would close the video call on any temporary + // network issue. + case 'closed': + case 'failed': + this.onDisconnected(new Error('peer ' + this._state)) + break + } + } + + this._peer.onsignalingstatechange = () => { + if (!this._peer) { + this._log.warn(`attempting to call 'onsignalingstatechange' for nonexistent peer`) + return + } + + const state = this._peer.signalingState + this._log.info(`signaling state changed`, { state }) + + // The closed signaling state has been deprecated in favor of the closed iceConnectionState. + // We are watching for it here to add a bit of backward compatibility. + if (state == 'closed') { + this.onDisconnected(new Error('signaling state changed to closed')) + } + } + + let negotiating = false + this._peer.onnegotiationneeded = async () => { + if (!this._peer) { + this._log.warn(`attempting to call 'onsignalingstatechange' for nonexistent peer`) + return + } + + const state = this._peer.signalingState + this._log.warn(`negotiation is needed`, { state }) + + if (negotiating) { + this._log.info(`negotiation already in progress; skipping...`) + return + } + + negotiating = true + + try { + // If the connection hasn't yet achieved the "stable" state, + // return to the caller. Another negotiationneeded event + // will be fired when the state stabilizes. + + if (state != 'stable') { + this._log.info(`connection isn't stable yet; postponing...`) + return + } + + const offer = await this._peer.createOffer() + await this._peer.setLocalDescription(offer) + + if (offer) { + this.emit('negotiation', offer) + } else { + this._log.warn(`negotiatoion offer is empty`) + } + } catch (error: any) { + this._log.error(`on negotiation needed failed`, { error }) + } finally { + negotiating = false + } + } + + this._peer.ontrack = this.onTrack.bind(this) + this._peer.ondatachannel = this.onDataChannel.bind(this) + } + + public async setOffer(sdp: string) { + if (!this._peer) { + throw new Error('attempting to set offer for nonexistent peer') + } + + await this._peer.setRemoteDescription({ type: 'offer', sdp }) + + if (this._candidates.length > 0) { + let candidates = 0 + for (const candidate of this._candidates) { + try { + await this._peer.addIceCandidate(candidate) + candidates++ + } catch (error: any) { + this._log.warn(`unable to add remote ICE candidate`, { error }) + } + } + + this._log.debug(`added ${candidates} remote ICE candidates`, { candidates: this._candidates }) + this._candidates = [] + } + + const answer = await this._peer.createAnswer() + + // add stereo=1 to answer sdp to enable stereo audio for chromium + answer.sdp = answer.sdp?.replace(/(stereo=1;)?useinbandfec=1/, 'useinbandfec=1;stereo=1') + + await this._peer.setLocalDescription(answer) + + if (answer) { + this.emit('negotiation', answer) + } else { + this._log.warn(`negiotation answer is empty`) + } + } + + public async setAnswer(sdp: string) { + if (!this._peer) { + throw new Error('attempting to set answer for nonexistent peer') + } + + await this._peer.setRemoteDescription({ type: 'answer', sdp }) + } + + public async close() { + if (!this._peer) { + throw new Error('attempting to close nonexistent peer') + } + + // create and emit video snap before closing connection + try { + const imageSrc = await videoSnap(this.video) + this.emit('fallback', imageSrc) + } catch (error: any) { + this._log.warn(`unable to generate video snap`, { error }) + } + + this.onDisconnected(new Error('connection closed')) + } + + public addTrack(track: MediaStreamTrack, ...streams: MediaStream[]): RTCRtpSender { + if (!this._peer) { + throw new Error('attempting to add track for nonexistent peer') + } + + // @ts-ignore + const isChromium = !!window.chrome + + // TOOD: Ugly workaround, find real cause of this issue. + if (isChromium) { + return this._peer.addTrack(track, ...streams) + } else { + return this._peer.addTransceiver(track, { direction: 'sendonly', streams }).sender + } + } + + public removeTrack(sender: RTCRtpSender) { + if (!this._peer) { + throw new Error('attempting to add track for nonexistent peer') + } + + this._peer.removeTrack(sender) + } + + public disconnect() { + if (typeof this._channel !== 'undefined') { + // unmount all events + this._channel.onerror = () => {} + this._channel.onmessage = () => {} + this._channel.onopen = () => {} + this._channel.onclose = () => {} + + try { + this._channel.close() + } catch {} + + this._channel = undefined + } + + if (typeof this._peer != 'undefined') { + // unmount all events + this._peer.onicecandidate = () => {} + this._peer.onicecandidateerror = () => {} + this._peer.onconnectionstatechange = () => {} + this._peer.oniceconnectionstatechange = () => {} + this._peer.onsignalingstatechange = () => {} + this._peer.onnegotiationneeded = () => {} + this._peer.ontrack = () => {} + this._peer.ondatachannel = () => {} + + try { + this._peer.close() + } catch {} + + this._peer = undefined + } + + if (this._statsStop && typeof this._statsStop === 'function') { + this._statsStop() + this._statsStop = undefined + } + + this._track = undefined + this._state = 'disconnected' + this._connected = false + this._candidates = [] + } + + public send(event: 'wheel', data: { delta_x: number; delta_y: number; control_key?: boolean }): void + public send(event: 'mousemove', data: { x: number; y: number }): void + public send(event: 'mousedown' | 'mouseup' | 'keydown' | 'keyup', data: { key: number }): void + public send(event: 'ping', data: number): void + public send( + event: 'touchbegin' | 'touchupdate' | 'touchend', + data: { touch_id: number; x: number; y: number; pressure: number }, + ): void + public send(event: string, data: any): void { + if (typeof this._channel === 'undefined' || this._channel.readyState !== 'open') { + this._log.warn(`attempting to send data, but data-channel is not open`, { event }) + return + } + + let buffer: ArrayBuffer + let payload: DataView + switch (event) { + case 'mousemove': + buffer = new ArrayBuffer(7) + payload = new DataView(buffer) + payload.setUint8(0, OPCODE.MOVE) + payload.setUint16(1, 4) + payload.setUint16(3, data.x) + payload.setUint16(5, data.y) + break + case 'wheel': + buffer = new ArrayBuffer(8) + payload = new DataView(buffer) + payload.setUint8(0, OPCODE.SCROLL) + payload.setUint16(1, 5) + payload.setInt16(3, data.delta_x) + payload.setInt16(5, data.delta_y) + payload.setUint8(7, data.control_key ? 1 : 0) + break + case 'keydown': + buffer = new ArrayBuffer(7) + payload = new DataView(buffer) + payload.setUint8(0, OPCODE.KEY_DOWN) + payload.setUint16(1, 4) + payload.setUint32(3, data.key) + break + case 'keyup': + buffer = new ArrayBuffer(7) + payload = new DataView(buffer) + payload.setUint8(0, OPCODE.KEY_UP) + payload.setUint16(1, 4) + payload.setUint32(3, data.key) + break + case 'mousedown': + buffer = new ArrayBuffer(7) + payload = new DataView(buffer) + payload.setUint8(0, OPCODE.BTN_DOWN) + payload.setUint16(1, 4) + payload.setUint32(3, data.key) + break + case 'mouseup': + buffer = new ArrayBuffer(7) + payload = new DataView(buffer) + payload.setUint8(0, OPCODE.BTN_UP) + payload.setUint16(1, 4) + payload.setUint32(3, data.key) + break + case 'ping': + buffer = new ArrayBuffer(11) + payload = new DataView(buffer) + payload.setUint8(0, OPCODE.PING) + payload.setUint16(1, 8) + payload.setUint32(3, Math.trunc(data / maxUint32)) + payload.setUint32(7, data % maxUint32) + break + case 'touchbegin': + case 'touchupdate': + case 'touchend': + buffer = new ArrayBuffer(16) + payload = new DataView(buffer) + if (event === 'touchbegin') { + payload.setUint8(0, OPCODE.TOUCH_BEGIN) + } else if (event === 'touchupdate') { + payload.setUint8(0, OPCODE.TOUCH_UPDATE) + } else if (event === 'touchend') { + payload.setUint8(0, OPCODE.TOUCH_END) + } + payload.setUint16(1, 13) + payload.setUint32(3, data.touch_id) + payload.setInt32(7, data.x) + payload.setInt32(11, data.y) + payload.setUint8(15, data.pressure) + break + default: + this._log.warn(`unknown data event`, { event }) + return + } + + this._channel.send(buffer) + } + + private onTrack(event: RTCTrackEvent) { + this._log.debug(`received track from peer`, { label: event.track.label }) + + const stream = event.streams[0] + if (!stream) { + this._log.warn(`no stream provided for track`, { label: event.track.label }) + return + } + + if (event.track.kind === 'video') { + this._track = event.track + } + + this.emit('track', event) + } + + private onDataChannel(event: RTCDataChannelEvent) { + this._log.debug(`received data channel from peer`, { label: event.channel.label }) + + this._channel = event.channel + this._channel.binaryType = 'arraybuffer' + this._channel.onerror = this.onDisconnected.bind(this, new Error('peer data channel error')) + this._channel.onmessage = this.onData.bind(this) + this._channel.onopen = this.onConnected.bind(this) + this._channel.onclose = this.onDisconnected.bind(this, new Error('peer data channel closed')) + } + + private onData(e: MessageEvent) { + const payload = new DataView(e.data) + const event = payload.getUint8(0) + const length = payload.getUint16(1) + + switch (event) { + case 1: + this.emit('cursor-position', { + x: payload.getUint16(3), + y: payload.getUint16(5), + }) + break + case 2: + const data = e.data.slice(11, length - 11) + + // TODO: get string from server + const blob = new Blob([data], { type: 'image/png' }) + const reader = new FileReader() + reader.onload = (e) => { + this.emit('cursor-image', { + width: payload.getUint16(3), + height: payload.getUint16(5), + x: payload.getUint16(7), + y: payload.getUint16(9), + uri: String(e.target!.result), + }) + } + reader.readAsDataURL(blob) + + break + case 3: + const nowTs = Date.now() + + const [clientTs1, clientTs2] = [payload.getUint32(3), payload.getUint32(7)] + const clientTs = clientTs1 * maxUint32 + clientTs2 + const [serverTs1, serverTs2] = [payload.getUint32(11), payload.getUint32(15)] + const serverTs = serverTs1 * maxUint32 + serverTs2 + + this._requestLatency = serverTs - clientTs + this._responseLatency = nowTs - serverTs + + break + default: + this._log.warn(`unhandled webrtc event`, { event, payload }) + } + } + + private onConnected() { + if (!this.connected || this._connected) { + return + } + + this._log.info(`connected`) + this.emit('connected') + + this._statsStop = this.statsEmitter() + this._connected = true + } + + private onDisconnected(error?: Error) { + const wasConnected = this._connected + this.disconnect() + + if (wasConnected) { + this._log.info(`disconnected`, { error }) + this.emit('disconnected', error) + } + } + + private statsEmitter(ms: number = 2000) { + let bytesReceived: number + let timestamp: number + let framesDecoded: number + let packetsLost: number + let packetsReceived: number + + const timer = window.setInterval(async () => { + if (!this._peer) return + + let stats: RTCStatsReport | undefined = undefined + if (this._peer.getStats.length === 0) { + stats = await this._peer.getStats() + } else { + // callback browsers support + await new Promise((res) => { + //@ts-ignore + this._peer.getStats((stats) => res(stats)) + }) + } + + if (!stats) return + + let report: any = null + stats.forEach(function (stat) { + if (stat.type === 'inbound-rtp' && stat.kind === 'video') { + report = { ...stat } + } + }) + + if (report === null) return + + if (timestamp) { + const bytesDiff = (report.bytesReceived - bytesReceived) * 8 + const tsDiff = report.timestamp - timestamp + const framesDecodedDiff = report.framesDecoded - framesDecoded + const packetsLostDiff = report.packetsLost - packetsLost + const packetsReceivedDiff = report.packetsReceived - packetsReceived + + this.emit('stats', { + // Firefox does not emit any event when starting paused + // because there is no video report found in stats. + paused: this.paused, + bitrate: (bytesDiff / tsDiff) * 1000, + packetLoss: (packetsLostDiff / (packetsLostDiff + packetsReceivedDiff)) * 100, + fps: Number(report.framesPerSecond || framesDecodedDiff / (tsDiff / 1000)), + width: report.frameWidth || NaN, + height: report.frameHeight || NaN, + muted: this._track?.muted, + // latency from ping/pong messages + latency: this._requestLatency + this._responseLatency, + requestLatency: this._requestLatency, + responseLatency: this._responseLatency, + }) + } + + bytesReceived = report.bytesReceived + timestamp = report.timestamp + framesDecoded = report.framesDecoded + packetsLost = report.packetsLost + packetsReceived = report.packetsReceived + + this.send('ping', Date.now()) + }, ms) + + return function () { + window.clearInterval(timer) + } + } +} diff --git a/client/src/component/internal/websocket.ts b/client/src/component/internal/websocket.ts new file mode 100644 index 00000000..c595a2f1 --- /dev/null +++ b/client/src/component/internal/websocket.ts @@ -0,0 +1,164 @@ +import EventEmitter from 'eventemitter3' +import { SYSTEM_HEARTBEAT, SYSTEM_LOGS } from '../types/events' +import { Logger } from '../utils/logger' + +export interface NekoWebSocketEvents { + connected: () => void + disconnected: (error?: Error) => void + message: (event: string, payload: any) => void +} + +// how long can connection be idle before closing +const STALE_TIMEOUT_MS = 12_500 // 12.5 seconds + +// how often should stale check be evaluated +const STALE_INTERVAL_MS = 7_000 // 7 seconds + +const STATUS_CODE_MAP = { + 1000: 'Normal Closure', + 1001: 'Going Away', + 1002: 'Protocol Error', + 1003: 'Unsupported Data', + 1004: '(For future)', + 1005: 'No Status Received', + 1006: 'Abnormal Closure', + 1007: 'Invalid frame payload data', + 1008: 'Policy Violation', + 1009: 'Message too big', + 1010: 'Missing Extension', + 1011: 'Internal Error', + 1012: 'Service Restart', + 1013: 'Try Again Later', + 1014: 'Bad Gateway', + 1015: 'TLS Handshake', +} as Record + +export class NekoWebSocket extends EventEmitter { + private _ws?: WebSocket + private _stale_interval?: number + private _last_received?: Date + + // eslint-disable-next-line + constructor( + private readonly _log: Logger = new Logger('websocket'), + ) { + super() + } + + get supported() { + return typeof WebSocket !== 'undefined' && WebSocket.OPEN === 1 + } + + get connected() { + return typeof this._ws !== 'undefined' && this._ws.readyState === WebSocket.OPEN + } + + public connect(url: string) { + if (!this.supported) { + throw new Error('browser does not support websockets') + } + + if (this.connected) { + throw new Error('attempting to create websocket while connection open') + } + + if (typeof this._ws !== 'undefined') { + this._log.debug(`previous websocket connection needs to be closed`) + this.disconnect('connection replaced') + } + + this._ws = new WebSocket(url) + + this._log.info(`connecting`) + + this._ws.onopen = this.onConnected.bind(this) + this._ws.onclose = (e: CloseEvent) => { + let reason = 'close' + + if (e.code in STATUS_CODE_MAP) { + reason = STATUS_CODE_MAP[e.code] + } + + this.onDisconnected(reason) + } + this._ws.onerror = this.onDisconnected.bind(this, 'error') + this._ws.onmessage = this.onMessage.bind(this) + } + + public disconnect(reason: string) { + this._last_received = undefined + + if (this._stale_interval) { + window.clearInterval(this._stale_interval) + this._stale_interval = undefined + } + + if (typeof this._ws !== 'undefined') { + // unmount all events + this._ws.onopen = () => {} + this._ws.onclose = () => {} + this._ws.onerror = () => {} + this._ws.onmessage = () => {} + + try { + this._ws.close(1000, reason) + } catch {} + + this._ws = undefined + } + } + + public send(event: string, payload?: any) { + if (!this.connected) { + this._log.warn(`attempting to send message while disconnected`) + return + } + + if (event != SYSTEM_LOGS) this._log.debug(`sending websocket event`, { event, payload }) + this._ws!.send(JSON.stringify({ event, payload })) + } + + private onMessage(e: MessageEvent) { + const { event, payload } = JSON.parse(e.data) + + this._last_received = new Date() + // heartbeat only updates last_received + if (event == SYSTEM_HEARTBEAT) return + + this._log.debug(`received websocket event`, { event, payload }) + this.emit('message', event, payload) + } + + private onConnected() { + if (!this.connected) { + this._log.warn(`onConnected called while being disconnected`) + return + } + + // periodically check if connection is stale + if (this._stale_interval) window.clearInterval(this._stale_interval) + this._stale_interval = window.setInterval(this.onStaleCheck.bind(this), STALE_INTERVAL_MS) + + this._log.info(`connected`) + this.emit('connected') + } + + private onDisconnected(reason: string) { + this.disconnect(reason) + + this._log.info(`disconnected`, { reason }) + this.emit('disconnected', new Error(`connection ${reason}`)) + } + + private onStaleCheck() { + if (!this._last_received) return + + // if we haven't received a message in specified time, + // assume the connection is dead + const diff = new Date().getTime() - this._last_received.getTime() + if (diff < STALE_TIMEOUT_MS) return + + this._log.warn(`websocket connection is stale, disconnecting`) + this.onDisconnected('stale') + } +} diff --git a/client/src/component/main.vue b/client/src/component/main.vue new file mode 100644 index 00000000..e27403c9 --- /dev/null +++ b/client/src/component/main.vue @@ -0,0 +1,832 @@ + + + + + diff --git a/client/src/component/overlay.vue b/client/src/component/overlay.vue new file mode 100644 index 00000000..da524e74 --- /dev/null +++ b/client/src/component/overlay.vue @@ -0,0 +1,993 @@ + + + + + diff --git a/client/src/page/components/header.vue b/client/src/page/components/header.vue new file mode 100644 index 00000000..5f5840b2 --- /dev/null +++ b/client/src/page/components/header.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/client/src/page/components/media.vue b/client/src/page/components/media.vue new file mode 100644 index 00000000..e78241f1 --- /dev/null +++ b/client/src/page/components/media.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/client/src/page/components/members.vue b/client/src/page/components/members.vue new file mode 100644 index 00000000..fa544ee3 --- /dev/null +++ b/client/src/page/components/members.vue @@ -0,0 +1,565 @@ + + + + + diff --git a/client/src/page/main.vue b/client/src/page/main.vue new file mode 100644 index 00000000..f1e92210 --- /dev/null +++ b/client/src/page/main.vue @@ -0,0 +1,640 @@ + + + + + + + diff --git a/client/src/page/plugins/.gitkeep b/client/src/page/plugins/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/client/src/page/plugins/chat/OpenApi.yaml b/client/src/page/plugins/chat/OpenApi.yaml new file mode 100644 index 00000000..7401794c --- /dev/null +++ b/client/src/page/plugins/chat/OpenApi.yaml @@ -0,0 +1,102 @@ +openapi: 3.0.0 + +info: + title: n.eko REST API + description: Next Gen Renderer Browser. + license: + name: Apache 2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' + version: "1.0.0" + +servers: + - description: Local server + url: http://localhost:3000 + # Added by API Auto Mocking Plugin + - description: SwaggerHub API Auto Mocking + url: https://virtserver.swaggerhub.com/m1k1o/n.eko/1.0.0 + +tags: + - name: chat + description: Chat API + +paths: + + # + # chat + # + + /api/chat: + post: + tags: + - chat + summary: Send a chat message + operationId: sendChatMessage + responses: + '200': + description: OK + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + requestBody: + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/Content' + +components: + securitySchemes: + CookieAuth: + type: apiKey + in: cookie + name: NEKO_SESSION + BearerAuth: + type: http + scheme: bearer + TokenAuth: + type: apiKey + in: query + name: token + + responses: + NotFound: + description: The specified resource was not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + Unauthorized: + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + Forbidden: + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorMessage' + + schemas: + ErrorMessage: + type: object + properties: + message: + type: string + + # + # chat + # + + Content: + type: object + properties: + text: + type: string + description: The message content + +security: + - BearerAuth: [] + - CookieAuth: [] + - TokenAuth: [] diff --git a/client/src/page/plugins/chat/api-gen b/client/src/page/plugins/chat/api-gen new file mode 100755 index 00000000..20eb3b09 --- /dev/null +++ b/client/src/page/plugins/chat/api-gen @@ -0,0 +1,18 @@ +#!/bin/bash +VERSION="1.0.0" + +rm -rf "${PWD}/api" +mkdir "${PWD}/api" + +docker run --rm \ + --user "$(id -u):$(id -g)" \ + -v "${PWD}/api:/local/out" \ + -v "${PWD}/OpenApi.yaml:/local/in.yaml" \ + openapitools/openapi-generator-cli generate \ + -i /local/in.yaml \ + -g typescript-axios \ + -o /local/out \ + --additional-properties=enumPropertyNaming=original,modelPropertyNaming=original,withSeparateModelsAndApi=true,modelPackage=models,apiPackage=api + +# Remove not needed git_push.sh +rm -f "${PWD}/api/git_push.sh" diff --git a/client/src/page/plugins/chat/api/.gitignore b/client/src/page/plugins/chat/api/.gitignore new file mode 100644 index 00000000..149b5765 --- /dev/null +++ b/client/src/page/plugins/chat/api/.gitignore @@ -0,0 +1,4 @@ +wwwroot/*.js +node_modules +typings +dist diff --git a/client/src/page/plugins/chat/api/.npmignore b/client/src/page/plugins/chat/api/.npmignore new file mode 100644 index 00000000..999d88df --- /dev/null +++ b/client/src/page/plugins/chat/api/.npmignore @@ -0,0 +1 @@ +# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm \ No newline at end of file diff --git a/client/src/page/plugins/chat/api/.openapi-generator-ignore b/client/src/page/plugins/chat/api/.openapi-generator-ignore new file mode 100644 index 00000000..7484ee59 --- /dev/null +++ b/client/src/page/plugins/chat/api/.openapi-generator-ignore @@ -0,0 +1,23 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md diff --git a/client/src/page/plugins/chat/api/.openapi-generator/FILES b/client/src/page/plugins/chat/api/.openapi-generator/FILES new file mode 100644 index 00000000..f4d9f699 --- /dev/null +++ b/client/src/page/plugins/chat/api/.openapi-generator/FILES @@ -0,0 +1,12 @@ +.gitignore +.npmignore +.openapi-generator-ignore +api.ts +api/chat-api.ts +base.ts +common.ts +configuration.ts +git_push.sh +index.ts +models/error-message.ts +models/index.ts diff --git a/client/src/page/plugins/chat/api/.openapi-generator/VERSION b/client/src/page/plugins/chat/api/.openapi-generator/VERSION new file mode 100644 index 00000000..08bfd064 --- /dev/null +++ b/client/src/page/plugins/chat/api/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.5.0-SNAPSHOT diff --git a/client/src/page/plugins/chat/api/api.ts b/client/src/page/plugins/chat/api/api.ts new file mode 100644 index 00000000..7faf378c --- /dev/null +++ b/client/src/page/plugins/chat/api/api.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer Browser. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +export * from './api/chat-api'; + diff --git a/client/src/page/plugins/chat/api/api/chat-api.ts b/client/src/page/plugins/chat/api/api/chat-api.ts new file mode 100644 index 00000000..30a653f8 --- /dev/null +++ b/client/src/page/plugins/chat/api/api/chat-api.ts @@ -0,0 +1,145 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer Browser. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from '../configuration'; +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; +// Some imports not used depending on template conditions +// @ts-ignore +import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common'; +// @ts-ignore +import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base'; +// @ts-ignore +import { ErrorMessage } from '../models'; +/** + * ChatApi - axios parameter creator + * @export + */ +export const ChatApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary Send a chat message + * @param {string} [content] The message content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sendChatMessage: async (content?: string, options: RawAxiosRequestConfig = {}): Promise => { + const localVarPath = `/api/chat`; + // use dummy base URL string because the URL constructor only accepts absolute URLs. + const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); + let baseOptions; + if (configuration) { + baseOptions = configuration.baseOptions; + } + + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + const localVarFormParams = new ((configuration && configuration.formDataCtor) || FormData)(); + + // authentication CookieAuth required + + // authentication TokenAuth required + await setApiKeyToObject(localVarQueryParameter, "token", configuration) + + // authentication BearerAuth required + // http bearer authentication required + await setBearerAuthToObject(localVarHeaderParameter, configuration) + + + if (content !== undefined) { + localVarFormParams.append('content', content as any); + } + + + localVarHeaderParameter['Content-Type'] = 'multipart/form-data'; + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.data = localVarFormParams; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * ChatApi - functional programming interface + * @export + */ +export const ChatApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = ChatApiAxiosParamCreator(configuration) + return { + /** + * + * @summary Send a chat message + * @param {string} [content] The message content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async sendChatMessage(content?: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.sendChatMessage(content, options); + const localVarOperationServerIndex = configuration?.serverIndex ?? 0; + const localVarOperationServerBasePath = operationServerMap['ChatApi.sendChatMessage']?.[localVarOperationServerIndex]?.url; + return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath); + }, + } +}; + +/** + * ChatApi - factory interface + * @export + */ +export const ChatApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = ChatApiFp(configuration) + return { + /** + * + * @summary Send a chat message + * @param {string} [content] The message content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + sendChatMessage(content?: string, options?: any): AxiosPromise { + return localVarFp.sendChatMessage(content, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * ChatApi - object-oriented interface + * @export + * @class ChatApi + * @extends {BaseAPI} + */ +export class ChatApi extends BaseAPI { + /** + * + * @summary Send a chat message + * @param {string} [content] The message content + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof ChatApi + */ + public sendChatMessage(content?: string, options?: RawAxiosRequestConfig) { + return ChatApiFp(this.configuration).sendChatMessage(content, options).then((request) => request(this.axios, this.basePath)); + } +} + diff --git a/client/src/page/plugins/chat/api/base.ts b/client/src/page/plugins/chat/api/base.ts new file mode 100644 index 00000000..60439c98 --- /dev/null +++ b/client/src/page/plugins/chat/api/base.ts @@ -0,0 +1,86 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer Browser. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from './configuration'; +// Some imports not used depending on template conditions +// @ts-ignore +import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios'; +import globalAxios from 'axios'; + +export const BASE_PATH = "http://localhost:3000".replace(/\/+$/, ""); + +/** + * + * @export + */ +export const COLLECTION_FORMATS = { + csv: ",", + ssv: " ", + tsv: "\t", + pipes: "|", +}; + +/** + * + * @export + * @interface RequestArgs + */ +export interface RequestArgs { + url: string; + options: RawAxiosRequestConfig; +} + +/** + * + * @export + * @class BaseAPI + */ +export class BaseAPI { + protected configuration: Configuration | undefined; + + constructor(configuration?: Configuration, protected basePath: string = BASE_PATH, protected axios: AxiosInstance = globalAxios) { + if (configuration) { + this.configuration = configuration; + this.basePath = configuration.basePath ?? basePath; + } + } +}; + +/** + * + * @export + * @class RequiredError + * @extends {Error} + */ +export class RequiredError extends Error { + constructor(public field: string, msg?: string) { + super(msg); + this.name = "RequiredError" + } +} + +interface ServerMap { + [key: string]: { + url: string, + description: string, + }[]; +} + +/** + * + * @export + */ +export const operationServerMap: ServerMap = { +} diff --git a/client/src/page/plugins/chat/api/common.ts b/client/src/page/plugins/chat/api/common.ts new file mode 100644 index 00000000..854e27fd --- /dev/null +++ b/client/src/page/plugins/chat/api/common.ts @@ -0,0 +1,150 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer Browser. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import type { Configuration } from "./configuration"; +import type { RequestArgs } from "./base"; +import type { AxiosInstance, AxiosResponse } from 'axios'; +import { RequiredError } from "./base"; + +/** + * + * @export + */ +export const DUMMY_BASE_URL = 'https://example.com' + +/** + * + * @throws {RequiredError} + * @export + */ +export const assertParamExists = function (functionName: string, paramName: string, paramValue: unknown) { + if (paramValue === null || paramValue === undefined) { + throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`); + } +} + +/** + * + * @export + */ +export const setApiKeyToObject = async function (object: any, keyParamName: string, configuration?: Configuration) { + if (configuration && configuration.apiKey) { + const localVarApiKeyValue = typeof configuration.apiKey === 'function' + ? await configuration.apiKey(keyParamName) + : await configuration.apiKey; + object[keyParamName] = localVarApiKeyValue; + } +} + +/** + * + * @export + */ +export const setBasicAuthToObject = function (object: any, configuration?: Configuration) { + if (configuration && (configuration.username || configuration.password)) { + object["auth"] = { username: configuration.username, password: configuration.password }; + } +} + +/** + * + * @export + */ +export const setBearerAuthToObject = async function (object: any, configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const accessToken = typeof configuration.accessToken === 'function' + ? await configuration.accessToken() + : await configuration.accessToken; + object["Authorization"] = "Bearer " + accessToken; + } +} + +/** + * + * @export + */ +export const setOAuthToObject = async function (object: any, name: string, scopes: string[], configuration?: Configuration) { + if (configuration && configuration.accessToken) { + const localVarAccessTokenValue = typeof configuration.accessToken === 'function' + ? await configuration.accessToken(name, scopes) + : await configuration.accessToken; + object["Authorization"] = "Bearer " + localVarAccessTokenValue; + } +} + +function setFlattenedQueryParams(urlSearchParams: URLSearchParams, parameter: any, key: string = ""): void { + if (parameter == null) return; + if (typeof parameter === "object") { + if (Array.isArray(parameter)) { + (parameter as any[]).forEach(item => setFlattenedQueryParams(urlSearchParams, item, key)); + } + else { + Object.keys(parameter).forEach(currentKey => + setFlattenedQueryParams(urlSearchParams, parameter[currentKey], `${key}${key !== '' ? '.' : ''}${currentKey}`) + ); + } + } + else { + if (urlSearchParams.has(key)) { + urlSearchParams.append(key, parameter); + } + else { + urlSearchParams.set(key, parameter); + } + } +} + +/** + * + * @export + */ +export const setSearchParams = function (url: URL, ...objects: any[]) { + const searchParams = new URLSearchParams(url.search); + setFlattenedQueryParams(searchParams, objects); + url.search = searchParams.toString(); +} + +/** + * + * @export + */ +export const serializeDataIfNeeded = function (value: any, requestOptions: any, configuration?: Configuration) { + const nonString = typeof value !== 'string'; + const needsSerialization = nonString && configuration && configuration.isJsonMime + ? configuration.isJsonMime(requestOptions.headers['Content-Type']) + : nonString; + return needsSerialization + ? JSON.stringify(value !== undefined ? value : {}) + : (value || ""); +} + +/** + * + * @export + */ +export const toPathString = function (url: URL) { + return url.pathname + url.search + url.hash +} + +/** + * + * @export + */ +export const createRequestFunction = function (axiosArgs: RequestArgs, globalAxios: AxiosInstance, BASE_PATH: string, configuration?: Configuration) { + return >(axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => { + const axiosRequestArgs = {...axiosArgs.options, url: (axios.defaults.baseURL ? '' : configuration?.basePath ?? basePath) + axiosArgs.url}; + return axios.request(axiosRequestArgs); + }; +} diff --git a/client/src/page/plugins/chat/api/configuration.ts b/client/src/page/plugins/chat/api/configuration.ts new file mode 100644 index 00000000..48054398 --- /dev/null +++ b/client/src/page/plugins/chat/api/configuration.ts @@ -0,0 +1,110 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer Browser. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export interface ConfigurationParameters { + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + username?: string; + password?: string; + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + basePath?: string; + serverIndex?: number; + baseOptions?: any; + formDataCtor?: new () => any; +} + +export class Configuration { + /** + * parameter for apiKey security + * @param name security name + * @memberof Configuration + */ + apiKey?: string | Promise | ((name: string) => string) | ((name: string) => Promise); + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + username?: string; + /** + * parameter for basic security + * + * @type {string} + * @memberof Configuration + */ + password?: string; + /** + * parameter for oauth2 security + * @param name security name + * @param scopes oauth2 scope + * @memberof Configuration + */ + accessToken?: string | Promise | ((name?: string, scopes?: string[]) => string) | ((name?: string, scopes?: string[]) => Promise); + /** + * override base path + * + * @type {string} + * @memberof Configuration + */ + basePath?: string; + /** + * override server index + * + * @type {number} + * @memberof Configuration + */ + serverIndex?: number; + /** + * base options for axios calls + * + * @type {any} + * @memberof Configuration + */ + baseOptions?: any; + /** + * The FormData constructor that will be used to create multipart form data + * requests. You can inject this here so that execution environments that + * do not support the FormData class can still run the generated client. + * + * @type {new () => FormData} + */ + formDataCtor?: new () => any; + + constructor(param: ConfigurationParameters = {}) { + this.apiKey = param.apiKey; + this.username = param.username; + this.password = param.password; + this.accessToken = param.accessToken; + this.basePath = param.basePath; + this.serverIndex = param.serverIndex; + this.baseOptions = param.baseOptions; + this.formDataCtor = param.formDataCtor; + } + + /** + * Check if the given MIME is a JSON MIME. + * JSON MIME examples: + * application/json + * application/json; charset=UTF8 + * APPLICATION/JSON + * application/vnd.company+json + * @param mime - MIME (Multipurpose Internet Mail Extensions) + * @return True if the given MIME is JSON, false otherwise. + */ + public isJsonMime(mime: string): boolean { + const jsonMime: RegExp = new RegExp('^(application\/json|[^;/ \t]+\/[^;/ \t]+[+]json)[ \t]*(;.*)?$', 'i'); + return mime !== null && (jsonMime.test(mime) || mime.toLowerCase() === 'application/json-patch+json'); + } +} diff --git a/client/src/page/plugins/chat/api/index.ts b/client/src/page/plugins/chat/api/index.ts new file mode 100644 index 00000000..77070e12 --- /dev/null +++ b/client/src/page/plugins/chat/api/index.ts @@ -0,0 +1,18 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer Browser. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +export * from "./api"; +export * from "./configuration"; +export * from "./models"; diff --git a/client/src/page/plugins/chat/api/models/error-message.ts b/client/src/page/plugins/chat/api/models/error-message.ts new file mode 100644 index 00000000..8764dd5d --- /dev/null +++ b/client/src/page/plugins/chat/api/models/error-message.ts @@ -0,0 +1,30 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * n.eko REST API + * Next Gen Renderer Browser. + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface ErrorMessage + */ +export interface ErrorMessage { + /** + * + * @type {string} + * @memberof ErrorMessage + */ + 'message'?: string; +} + diff --git a/client/src/page/plugins/chat/api/models/index.ts b/client/src/page/plugins/chat/api/models/index.ts new file mode 100644 index 00000000..595296f1 --- /dev/null +++ b/client/src/page/plugins/chat/api/models/index.ts @@ -0,0 +1 @@ +export * from './error-message'; diff --git a/client/src/page/plugins/chat/component.vue b/client/src/page/plugins/chat/component.vue new file mode 100644 index 00000000..3dd61408 --- /dev/null +++ b/client/src/page/plugins/chat/component.vue @@ -0,0 +1,329 @@ +