diff --git a/.env.mock b/.env.mock new file mode 100644 index 0000000..f1d20d7 --- /dev/null +++ b/.env.mock @@ -0,0 +1,41 @@ +##### Dev Server ########################## + +# optional +DEV_VITE_SERVER_CRT_PATH= + +# optional +DEV_VITE_SERVER_KEY_PATH= + +##### Application ######################### + +# required +VITE_APP_URL=https://localhost:3443 + +# required +VITE_ROCKETCHAT_URL=https://chat.staging.radical.sexy + +# required +VITE_CODIMD_URL=https://md.staging.radical.sexy + +# optional +VITE_LOG_LEVEL=debug + +# optional +VITE_REPOSITORY_URL=https://github.com/radicallyopensecurity/dashboard + +# optional +VITE_PUBLIC_ISSUE_TRACKER=https://github.com/radicallyopensecurity/dashboard/issues/new + +# optional +VITE_INTERNAL_ISSUE_TRACKER=https://github.com/radicallyopensecurity/dashboard/issues/new + +# optional +VITE_SECRET_GITLAB_TOKEN=notactuallyused + +##### OIDC Authentication ################# + +# required +VITE_GITLAB_CLIENT_ID=notactuallyused + +# required +VITE_GITLAB_AUTHORITY=http://localhost:3000 diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..938bf98 --- /dev/null +++ b/.env.sample @@ -0,0 +1,38 @@ +##### Dev Server ########################## + +# optional +DEV_VITE_SERVER_CRT_PATH= + +# optional +DEV_VITE_SERVER_KEY_PATH= + +##### Application ######################### + +# required +VITE_APP_URL= + +# required +VITE_ROCKETCHAT_URL= + +# required +VITE_CODIMD_URL= + +# optional +VITE_LOG_LEVEL=warn + +# optional +VITE_REPOSITORY_URL= + +# optional +VITE_PUBLIC_ISSUE_TRACKER= + +# optional +VITE_INTERNAL_ISSUE_TRACKER= + +##### OIDC Authentication ################# + +# required +VITE_GITLAB_CLIENT_ID= + +# required +VITE_GITLAB_AUTHORITY= diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..d51ca42 --- /dev/null +++ b/.env.test @@ -0,0 +1,41 @@ +##### Dev Server ########################## + +# optional +DEV_VITE_SERVER_CRT_PATH= + +# optional +DEV_VITE_SERVER_KEY_PATH= + +##### Application ######################### + +# required +VITE_APP_URL=https://localhost + +# required +VITE_ROCKETCHAT_URL=https://localhost + +# required +VITE_CODIMD_URL=https://localhost + +# optional +VITE_LOG_LEVEL=debug + +# optional +VITE_REPOSITORY_URL=https://localhost + +# optional +VITE_PUBLIC_ISSUE_TRACKER=https://localhost + +# optional +VITE_INTERNAL_ISSUE_TRACKER=https://localhost + +# optional +VITE_SECRET_GITLAB_TOKEN=notactuallyused + +##### OIDC Authentication ################# + +# required +VITE_GITLAB_CLIENT_ID=notactuallyused + +# required +VITE_GITLAB_AUTHORITY=http://localhost diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..f47f16a --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,145 @@ +// @ts-check +const { defineConfig } = require('eslint-define-config') + +module.exports = defineConfig({ + extends: [ + 'eslint:recommended', + 'plugin:lit/recommended', + 'plugin:wc/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:@typescript-eslint/recommended-type-checked', + 'plugin:@typescript-eslint/stylistic-type-checked', + ], + parserOptions: { + project: true, + __tsconfigRootDir: __dirname, + }, + settings: { + 'import/resolver': { + typescript: true, + node: true, + }, + }, + ignorePatterns: [ + '.eslintrc.cjs', + 'dist/*', + 'coverage/*', + 'public/*', + 'stylelint.config.js', + 'src/**/*.css', + ], + rules: { + // enable when refactoring to identify unused exports + // will falsely report litelement classes, not useful + // during development + // 'import/no-unused-modules': ['warn', { unusedExports: true }], + '@typescript-eslint/unbound-method': ['off'], + '@typescript-eslint/consistent-type-definitions': ['off'], + 'import/no-duplicates': ['error'], + 'import/no-self-import': ['error'], + 'import/no-import-module-exports': ['error'], + 'import/no-mutable-exports': ['error'], + 'import/no-extraneous-dependencies': ['error'], + 'import/no-empty-named-blocks': ['error'], + 'import/consistent-type-specifier-style': ['error', 'prefer-inline'], + 'import/no-default-export': [2], + 'import/order': [ + 'warn', + { + 'newlines-between': 'always', + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + pathGroups: [ + { + pattern: '@/config', + group: 'external', + position: 'after', + }, + { + pattern: '@/theme/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/pages/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/constants/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/routes', + group: 'external', + position: 'after', + }, + { + pattern: '@/api/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/modules/auth/types/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/modules/auth/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/modules/app/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/modules/projects/types/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/modules/projects/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/modules/user/types/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/modules/user/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/elements/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/features/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/utils/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/*', + group: 'external', + position: 'after', + }, + ], + }, + ], + }, +}) diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..db9ba41 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'weekly' diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml new file mode 100644 index 0000000..32c7d0b --- /dev/null +++ b/.github/workflows/pull-request.yml @@ -0,0 +1,62 @@ +name: Pull Request + +on: + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Setup cache + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node_modules- + + - name: Install dependencies + run: npm ci + + - name: Lint commits + env: + PR_HEAD: ${{ github.event.pull_request.head.sha }} + PR_COMMITS: ${{ github.event.pull_request.commits }} + run: | + npm run lint:commit -- \ + --from ${{ env.PR_HEAD }}~${{ env.PR_COMMITS }} \ + --to ${{ env.PR_HEAD }} --verbose + + - name: Lint files + run: npm run lint + + - name: Test + run: npm test + + - name: Build + env: + VITE_APP_URL: 'https://localhost' + VITE_ROCKETCHAT_URL: 'https://localhost' + VITE_CODIMD_URL: 'https://localhost' + VITE_GITLAB_CLIENT_ID: 'https://localhost' + VITE_GITLAB_AUTHORITY: 'https://localhost' + run: npm run build:ci diff --git a/.github/workflows/push-main.yml b/.github/workflows/push-main.yml new file mode 100644 index 0000000..dcdb376 --- /dev/null +++ b/.github/workflows/push-main.yml @@ -0,0 +1,99 @@ +name: Release + +on: + push: + branches: [main] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + version: + name: Determine version + runs-on: ubuntu-latest + outputs: + tag: ${{ steps.changelog.outputs.tag }} + version: ${{ steps.changelog.outputs.version }} + skipped: ${{ steps.changelog.outputs.skipped }} + steps: + - name: Checkout GitHub Action + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Tag and Commit + id: changelog + uses: TriPSs/conventional-changelog-action@v5 + with: + preset: 'conventionalcommits' + git-user-name: 'github-actions[bot]' + git-user-email: '41898282+github-actions[bot]@users.noreply.github.com' + skip-git-pull: true + git-push: false + skip-on-empty: ${{ github.event_name == 'push' }} + + build: + name: Build project + runs-on: ubuntu-latest + if: ${{ needs.version.outputs.skipped == 'false' }} + needs: version + steps: + - name: Checkout GitHub Action + uses: actions/checkout@v4 + + - name: Setup node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Setup cache + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node_modules- + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Test + run: npm test + + - name: Build + env: + VITE_APP_URL: 'https://localhost' + VITE_ROCKETCHAT_URL: 'https://localhost' + VITE_CODIMD_URL: 'https://localhost' + VITE_GITLAB_CLIENT_ID: 'https://localhost' + VITE_GITLAB_AUTHORITY: 'https://localhost' + run: npm run build:ci + + release: + name: Release + runs-on: ubuntu-latest + needs: [version, build] + if: ${{ needs.version.outputs.skipped == 'false' }} + steps: + - name: Checkout GitHub Action + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Bump Version + id: changelog + uses: TriPSs/conventional-changelog-action@v5 + with: + preset: 'conventionalcommits' + git-user-name: 'github-actions[bot]' + git-user-email: '41898282+github-actions[bot]@users.noreply.github.com' + skip-git-pull: true + git-push: true + skip-on-empty: true + release-count: 0 diff --git a/.gitignore b/.gitignore index 2cbcd48..53fac37 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ node_modules/ -web_modules/ -__snowpack__/ +dist/ +.env +coverage/ + +.internal/certs/* +!.internal/certs/generate-certificates.sh + diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..d7917c1 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,2 @@ +npm run lint:staged +npm run build:test diff --git a/.husky/prepare-commit-msg b/.husky/prepare-commit-msg new file mode 100755 index 0000000..3b30d90 --- /dev/null +++ b/.husky/prepare-commit-msg @@ -0,0 +1,3 @@ +#!/bin/sh + +exec < /dev/tty && npm run commit -- --hook || true diff --git a/.internal/certs/generate-certificates.sh b/.internal/certs/generate-certificates.sh new file mode 100644 index 0000000..b5ba24e --- /dev/null +++ b/.internal/certs/generate-certificates.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# Generate a private key for the root CA +openssl genrsa -out root-ca.key 2048 + +# Create and self-sign the root CA certificate +openssl req -x509 -new -nodes -key root-ca.key -sha256 -days 1024 -out root-ca.pem -subj "/CN=RootCA" + +# Generate a private key +openssl genrsa -out server.key 2048 + +# Create a certificate signing request (CSR) +openssl req -new -key server.key -out server.csr -subj "/CN=ros-dashboard.test" + +# Create a configuration file for server certificate usage +echo "authorityKeyIdentifier=keyid,issuer +basicConstraints=CA:FALSE +keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment +subjectAltName = @alt_names + +[alt_names] +DNS.1 = ros-dashboard.test" > server.ext + +# Generate the SSL certificate using the CA +openssl x509 -req -in server.csr -CA root-ca.pem -CAkey root-ca.key -CAcreateserial -out crt.pem -days 500 -sha256 -extfile server.ext + +# (Linux) Copy to certificate store and update +sudo cp root-ca.pem /usr/local/share/ca-certificates/root-ca.crt +sudo update-ca-certificates diff --git a/.internal/docker/npm-build.sh b/.internal/docker/npm-build.sh new file mode 100644 index 0000000..6f63799 --- /dev/null +++ b/.internal/docker/npm-build.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +docker run \ + -it \ + --rm \ + -v "$PWD":/app \ + -w /app \ + node:20.12.2 \ + npm run build diff --git a/.internal/docker/npm-cli.sh b/.internal/docker/npm-cli.sh new file mode 100644 index 0000000..3cc438a --- /dev/null +++ b/.internal/docker/npm-cli.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +docker run \ + -it \ + --rm \ + -p 3000:3000 \ + -v "$PWD":/app \ + -w /app \ + node:20.12.2 \ + /bin/bash diff --git a/.internal/docker/npm-dev.sh b/.internal/docker/npm-dev.sh new file mode 100644 index 0000000..95ef280 --- /dev/null +++ b/.internal/docker/npm-dev.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +docker run \ + -it \ + --rm \ + -p 3000:3000 \ + -v "$PWD":/app \ + -w /app \ + node:20.12.2 \ + npm run dev diff --git a/.internal/docker/npm-install.sh b/.internal/docker/npm-install.sh new file mode 100644 index 0000000..6004cb0 --- /dev/null +++ b/.internal/docker/npm-install.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +docker run \ + -it \ + --rm \ + -v "$PWD":/app \ + -w /app \ + node:20.12.2 \ + npm install diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 0000000..e1a131e --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,5 @@ +{ + "**/*.{ts,json,js,svg,html}": ["pretty-quick --write"], + "**/*.{ts,js}": ["eslint --fix"], + "**/*.{ts,css}": ["stylelint --fix"] +} diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..449691b --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-exact=true \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..07dc892 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +dist/ +coverage/ +node_modules/ +public/ \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..76f2137 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "semi": false, + "trailingComma": "es5", + "tabWidth": 2 +} diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index faa697b..0000000 --- a/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM node:14 - -ADD docker/build-entrypoint.sh /entrypoint.sh -VOLUME /source -VOLUME /target - -ENV REV=main - -WORKDIR /target -ENTRYPOINT /entrypoint.sh diff --git a/README.md b/README.md index 6a409fe..c770091 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,195 @@ -ROS Dashboard -============= +# ROS Dashboard -Staging Deployment ------------------- +## Dependencies -The staging system has a git hook to deploy updates on changes to a branch. +- OpenSSL (development) +- NodeJS >= 20.x +- npm => 10.x +- Docker >= 26.x -``` -git remote add staging ssh://dev@git.staging.radical.sexy:33222/dashboard.git -``` +It's recommended to use a version manager such as [Volta](https://volta.sh/) for `NodeJS` if running with host installed dependencies. -A new branch name can be created and pushed to the staging system: +App can be run through Docker without having `NodeJS` or `npm` installed. See the [Docker](#docker) section. -``` -git checkout -b my-cool-feature +## Build + +```sh +npm install +npm run build ``` -It will be deployed to https://git.staging.radical.sexy/ros-dashboard/my-cool-feature once pushed to staging: +See built files in the `dist` folder. -``` -git push staging -``` +## Deployment -Docker Build ------------- +The app connects to a GitLab instance and iFrames a RocketChat instance. These need to be configured to allow the connections. -A Docker image can be built from the Dockerfile to later on compile the frontend: +If you have an auth proxy in front of the app. That too needs to be configured. -``` -docker build -t dashboard-builder . -``` +And finally we recommend server running this app should be configured with `CSPs` and `HTTP` headers. -This builder then can be used in Git Hooks to compile the application assets: +### GitLab Configuration -``` -docker run --rm \ - -v /home/dev/dashboard.git:/source:ro \ - -v /var/www:/target \ - -e REV=main \ - dashboard-builder -``` +Create a new application in GitLab, make sure it has the scopes: `openid profile api`. -Setup Notes ------------ +As callback url set `https:{domain}/auth/callback` -The ROS dashboard VM is not exposed to the Internet. -For development purposes the SSH daemon is accessible via port-forwarding from the git VM on staging. +The GitLab instance must allow `CORS` for the domain the app runs, specifically these headers must be set: +- Access-Control-Allow-Origin: {domain} +- Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS +- Access-Control-Allow-Headers: authorization, content-type, private-token" + +In addition the GitLab instance should respond to `OPTIONS` requests with a 2xx response. + +### RocketChat Configuration + +`CORS` needs to be enabled in the admin panel. It can be found under `Settings -> General -> Enable Cors`. The origin of the app should be entered there as well. + +The server running RocketChat should respond with the following headers: + +- Access-Control-Allow-Origin: {domain} +- Access-Control-Allow-Credentials: true +- Content-Security-Policy: frame-ancestors https://{domain} + +### Auth Proxy Configuration + +If you have an authentication proxy before the app. Then it needs to allow CORS. + +> #TODO: IS THIS STILL TRUE? + +- Access-Control-Allow-Origin: {domain} +- Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS +- Access-Control-Allow-Headers: authorization, content-type, private-token" + +### App Server Configuration + +We recommend the following headers and `CSPs` + +Headers: + +- Access-Control-Allow-Origin: NONE + > #TODO: DOES THIS ACTUALLY WORK? +- Content-Security-Policy: script-src 'self'; default-src 'none'; connect-src 'self'; font-src 'self' data:; frame-src 'self'; img-src 'self' {gitlab} {rocketchat}; style-src 'self' + +## Test + +```sh +# run one time +npm run test +# run in watch mode +npm run test:watch +# run one time and get test coverage +npm run test:coverage ``` -iptables -t nat -A PREROUTING -p tcp --dport 33222 -j DNAT --to-destination 172.18.73.11:22222 -m comment --comment "expose ros-dashboard sshd" -iptables -t nat -A POSTROUTING -o enp7s0 -j MASQUERADE -m comment --comment "expose ros-dashboard sshd" -iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -P FORWARD DROP -iptables -A FORWARD -i enp7s0 -o enp1s0 -j ACCEPT -iptables -A FORWARD -i enp1s0 -o enp7s0 -p tcp --dport 22222 -j ACCEPT -echo 1 > /proc/sys/net/ipv4/ip_forward -``` -Within the VM a directory /dashboard.git contains a bare git repository configured with a post-receive hook [docker/post-receive.sh](docker/post-receive.sh). +## Lint + +There are several linters included to ensure consistent code style. The linter is run as a commit hook and is also tested as a GitHub actions on Pull Requests. +```sh +# lint with all linters +npm run lint +# lint with all linters and auto fix +npm run lint:fix + +# eslint +npm run eslint +npm run eslint:fix + +# stylelint +npm run lint:style +npm run lint:style:fix + +# prettier +npm run lint:prettier +npm run lint:prettier:fix ``` -git clone https://github.com/radicallyopensecurity/dashboard /dashboard.git -cp docker/post-receive.sh /dashboard.git/hooks/post-receive -chmod a+x /dashboard.git/hooks/post-receive + +## Development + +### HTTPS Certificates + +The dev server needs to run with `https`. This is because we use the [`window.crypto.subtle`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle) library, which is only available in secure contexts. + +You can use a tool such as [`mkcert`](https://github.com/FiloSottile/mkcert), or perform the steps manually. + +See the convenience script [`.internal/certs/generate-certificates.sh`](.internal/certs/generate-certificates.sh) for the manual steps on Linux. + +To connect to `ROS` staging, the domain name / common name must be `dashboard-local.staging.radical.sexy`. This is because the domain is registered as an OIDC application in GitLab + +The certificates are read by default from the [`.internal/certs`](.internal/certs) folder in `pem` format. To store the certificates somewhere else, set the environment variables `DEV_VITE_SERVER_CRT_PATH` and `DEV_VITE_SERVER_KEY_PATH`. + +### Install Dependencies + +```sh +npm install ``` -The directory is owned by the `dev` user, who also is the owner of `/var/www/html`: +### Run Development Server + +Copy `.env.sample` and fill in the required environment variables. +```sh +cp .env.sample .env ``` -adduser dev -chown dev:www-data /var/www/html -chmod g+s /var/www/html + +If running against `ROS` staging. Make sure your VPN connection is up. + +Then start the development server. + +```sh +npm run dev ``` -The original Debian index.html file can be deleted in favor of nginx directory listing: +Login to the Identity Provider in your browser. + +Then access the app at: + +## Docker + +You can use `Docker` to run and build the project, without installing `NodeJS` on your local machine. + +See or run the convenience scripts in `.internal/docker/`. + +- [`.internal/docker/npm-install.sh`](/.internal/docker/npm-install.sh) +- [`.internal/docker/npm-dev.sh`](/.internal/docker/npm-dev.sh) +- [`.internal/docker/npm-build.sh`](/.internal/docker/npm-build.sh) +- [`.internal/docker/npm-cli.sh`](/.internal/docker/npm-cli.sh) ```sh -rm /var/www/html/index.* -ROCKET_CHAT_DOMAIN=chat.example.com - -cat > /etc/nginx/sites-available/default < _url.searchParams.append(key, params[key])); - return _url; - } - - async post(url, params, options={}) { - options = { - ...options, - method: "POST" - } - return await this._fetch(url, params, options); - } - - async fetch(url, params, options) { - return await this._fetch(url, params, options); - } - - async _fetch(url, params, options={}) { - const _url = this.getUrl(url, params); - - options.method = options.method || "GET"; - switch (options.method.toUpperCase()) { - case "POST": - case "PUT": - options.headers = { - ...options.headers, - ...gitlabAuth.headers, - "Content-Type": "application/json" - }; - break; - } - - const response = await fetch(_url, options); - const data = await response.json(); - - if ((response.status < 200) || (response.status >= 300)) { - - let message = `HTTP Error ${response.status}`; - switch (response.status) { - case 400: - message += `: ${data}` - break; - case 401: - const sign_in_url = "/users/sign_in?redirect_to_referer=yes"; - message += `Redirecting to ${sign_in_url}`; - console.log(message); - window.location.href = sign_in_url; - break; - } - - throw new Error(message, { - status: response.status - }); - } - - return data; - } - - async fetchPaginated(key, url, mapResponse, filterResponse) { - mapResponse = mapResponse || ((x) => x); - filterResponse = filterResponse || ((x) => x); - const _url = this.getUrl(url, this.params); - let nextPage = 1; - this[key] = []; - - const currentBatch = this.batch = Symbol("paginatedRequestBatch"); - - _url.searchParams.set("per_page", this.perPage); - - let response; - let numberOfPagesFetched = 0; - - this.loading = true; - while (!Number.isNaN(nextPage) && this.maxPages !== numberOfPagesFetched && this.batch === currentBatch) { - _url.searchParams.set("page", nextPage); - const oldValue = this[key]; - response = await fetch(_url); - nextPage = parseInt(response.headers.get("x-next-page"), 10); - numberOfPagesFetched = parseInt(response.headers.get("x-page"), 10); - if(this.batch !== currentBatch) { - return; - } - const newItems = mapResponse(filterResponse(await response.json())) - .filter((item) => item !== undefined); - this[key] = this[key].concat(newItems); - this.requestUpdate(key, oldValue); - } - this.loading = false; - - } - -} diff --git a/elements/gitlab/issue.js b/elements/gitlab/issue.js deleted file mode 100644 index d326c85..0000000 --- a/elements/gitlab/issue.js +++ /dev/null @@ -1,78 +0,0 @@ -import { Gitlab } from './index.js'; - -export class GitlabIssue extends Gitlab { - - constructor() { - super(); - this.autoload = true; - this.fetched = false; - this.gitlabProjectId = null; - this.gitlabIssueIid = null; - this.gitlabIssueData = null; - this.gitlabIssueDiscussion = null; - } - - static get properties() { - return { - gitlabProjectId: { - type: Number, - reflect: true - }, - gitlabIssueIid: { - type: Number, - reflect: true - }, - gitlabIssueData: { - type: Object, - reflect: false - }, - gitlabIssueDiscussion: { - type: Object, - notify: true - }, - autoload: { - type: Boolean - }, - fetched: { - type: Boolean - } - }; - } - - get gitlabIssueComments() { - return this.gitlabIssueDiscussion - .filter((item) => item.notes[0].system === false); - } - - get labels() { - return this.gitlabProjectData ? this.gitlabProjectData.labels : []; - } - - async updated(changedProperties) { - super.updated(changedProperties); - if (this.autoload && changedProperties.has("gitlabProjectId")) { - await this.fetch(); - } - } - - get baseUrl() { - if (this.gitlabProjectId === null) { - throw new Error("Gitlab Project ID undefined"); - } - return super.baseUrl + `projects/${this.gitlabProjectId}/issues/${this.gitlabIssueIid}`; - } - - async fetch() { - this.fetched = false; - if (this.gitlabProjectId == null) { - return; - } - if (!this.gitlabIssueData || (this.gitlabIssueIid !== this.gitlabIssueData.iid)) { - // load gitlabIssueData - this.gitlabIssueData = await super.fetch(); - } - await this.fetchPaginated("gitlabIssueDiscussion", `${this.baseUrl}/discussions`); - this.fetched = true; - } - -} diff --git a/elements/gitlab/project.js b/elements/gitlab/project.js deleted file mode 100644 index 90c6945..0000000 --- a/elements/gitlab/project.js +++ /dev/null @@ -1,72 +0,0 @@ -import { Gitlab } from './index.js'; - -export class GitlabProject extends Gitlab { - - constructor() { - super(); - this.gitlabProjectData = null; - this.gitlabProjectEvents = []; - this.gitlabProjectLabels = []; - this.gitlabProjectIssues = []; - this.gitlabProjectMembers = []; - } - - static get properties() { - return { - gitlabProjectId: { - type: Number, - reflect: true - }, - gitlabProjectData: { - type: Object, - notify: true - }, - gitlabProjectEvents: { - type: Array, - notify: true - }, - gitlabProjectLabels: { - type: Array, - notify: true - }, - gitlabProjectMembers: { - type: Array, - notify: true - }, - gitlabProjectVariables: { - type: Array, - notify: true - }, - gitlabProjectIssues: { - type: Array, - notify: true - } - }; - } - - async updated(changedProperties) { - if (changedProperties.has("gitlabProjectId")) { - await this.fetch(); - } - } - - get baseUrl() { - if (this.gitlabProjectId === null) { - throw new Error("Gitlab Project ID undefined"); - } - return super.baseUrl + `projects/${this.gitlabProjectId}`; - } - - async fetch() { - if (this.gitlabProjectId == null) { - return; - } - this.gitlabProjectData = await super.fetch(); - await this.fetchPaginated("gitlabProjectMembers", `${this.baseUrl}/members`); - await this.fetchPaginated("gitlabProjectLabels", `${this.baseUrl}/labels`); - await this.fetchPaginated("gitlabProjectIssues", `${this.baseUrl}/issues`); - await this.fetchPaginated("gitlabProjectEvents", `${this.baseUrl}/events?target=issue`); - await this.fetchPaginated("gitlabProjectVariables", `${this.baseUrl}/variables`); - } - -} diff --git a/elements/gitlab/projects.js b/elements/gitlab/projects.js deleted file mode 100644 index b651006..0000000 --- a/elements/gitlab/projects.js +++ /dev/null @@ -1,95 +0,0 @@ -import { Gitlab } from './index.js'; -import { LitNotify } from '../../lib/lit-element-notify.js'; - -export class GitlabProjects extends LitNotify(Gitlab) { - - constructor() { - super(); - this.projects = []; - this.loading = false; - } - - static get autofetch() { - return true; - } - - connectedCallback() { - super.connectedCallback() - if (this.constructor.autofetch) { - this.fetch(); - } - } - - static get properties() { - return { - ... super.properties, - projects: { - type: Array, - notify: true - }, - sort: { - type: String, - reflect: true - }, - loading: { - type: Boolean, - notify: true - } - }; - } - - static sortProjectsByLastActivity(a, b) { - if (a.lastProjectActivity.isBefore(b.lastProjectActivity)) { - return 1; - } else if (b.lastProjectActivity.isBefore(a.lastProjectActivity)) { - return -1; - } else { - return 0; - } - } - - debounce(func, delay = 0) { - let timeoutId; - return function() { - clearTimeout(timeoutId); - timeoutId = setTimeout(() => { - func.apply(this, arguments); - }, delay); - } - }; - - debouncedSearch() { - this.debounce(this.fetch, 500); - } - - updated(changedProperties) { - const keys = [...changedProperties.keys()]; - if (keys.includes("params")) { - this.debouncedSearch(); - } - } - - get baseUrl() { - return super.baseUrl + `projects`; - } - - static mapProjects(projects) { - return projects; - } - - static filterProjects(projects) { - return projects; - } - - async fetch() { - this.loading = true; - await super.fetchPaginated( - "projects", - this.baseUrl, - this.constructor.mapProjects, - this.constructor.filterProjects - ); - this.loading = false; - } - -} diff --git a/elements/gitlab/users.js b/elements/gitlab/users.js deleted file mode 100644 index 668d8ea..0000000 --- a/elements/gitlab/users.js +++ /dev/null @@ -1,52 +0,0 @@ -import { Gitlab } from './index.js'; - -export class GitlabUsers extends Gitlab { - - constructor() { - super(); - this.projects = []; - } - - connectedCallback() { - super.connectedCallback() - this.fetch(); - } - - static get properties() { - return { - ... super.properties, - projects: { - type: Array - } - } - } - - debounce(func, delay = 0) { - let timeoutId; - return function() { - clearTimeout(timeoutId); - timeoutId = setTimeout(() => { - func.apply(this, arguments); - }, delay); - } - }; - - debouncedSearch = this.debounce(this.fetch, 500); - - updated(changedProperties) { - const keys = [...changedProperties.keys()]; - if (keys.includes("params") ) { - console.log(this.params.search); - this.debouncedSearch(); - } - } - - get baseUrl() { - return super.baseUrl + `projects`; - } - - async fetch() { - await super.fetchPaginated("projects", this.baseUrl); - } - -} \ No newline at end of file diff --git a/elements/pdf-password.js b/elements/pdf-password.js deleted file mode 100644 index b334381..0000000 --- a/elements/pdf-password.js +++ /dev/null @@ -1,86 +0,0 @@ -import { LitElement, html, css } from '../web_modules/lit.js'; -import './ui/icon.js'; - - -class PDFPassword extends LitElement { - - constructor() { - super(); - this.cleartext = ""; - this.visible = false; - } - - static get properties() { - return { - cleartext: { - type: String - }, - visible: { - type: Boolean - } - } - } - - static get styles() { - return css` - code { - cursor: pointer; - } - code:hover{ - text-decoration: underline; - } - `; - } - - get preview() { - if (this.visible) { - return this.cleartext; - } else { - return "********" - } - } - - get copyToClipboard() { - return (e) => { - console.log("copy to clipboard") - var data = [ - new ClipboardItem({ - "text/plain": new Blob( - [this.cleartext], - { - type: "text/plain" - } - ) - }) - ]; - navigator.clipboard.write(data); - - const tooltip = new bootstrap.Tooltip(e.currentTarget, { - placement: "right", - title: "copied", - trigger: "" - }); - tooltip.show(); - setTimeout(() => { - tooltip.dispose(); - }, 400); - } - } - - render() { - const toggleVisible = () => { - this.visible = !this.visible; - } - return html` - - - ${this.preview} - - `; - } - -} - -customElements.define("pdf-password", PDFPassword); diff --git a/elements/rocketchat/iframe.js b/elements/rocketchat/iframe.js deleted file mode 100644 index beadf3e..0000000 --- a/elements/rocketchat/iframe.js +++ /dev/null @@ -1,85 +0,0 @@ -import { LitElement, css } from '../../web_modules/lit.js'; -let $sharedIframe; - -export const chatHostname = window.location.hostname.replace(/^git\./, "chat."); - -export function getChannelPath(channel) { - return `/group/${channel}?layout=embedded`; -} - -export function getChannelUrl(channel) { - return `https://${chatHostname}${getChannelPath(channel)}`; -} - -class RocketchatFrame extends LitElement { - - constructor() { - super(); - this.channel = "ros"; - } - - static get properties() { - return { - channel: { - type: String, - reflect: true - } - }; - } - - get chatChannelPath() { - return getChannelPath(this.channel); - } - - get chatChannelUrl() { - return getChannelUrl(this.channel) - } - - updated(changedProperties) { - if (changedProperties.has("channel")) { - this.updateChannel(); - } - } - - updateChannel() { - if (!$sharedIframe.contentWindow) { - // frame is not loaded yet - $sharedIframe.src = this.chatChannelUrl; - } else { - $sharedIframe.contentWindow.postMessage({ - externalCommand: 'go', - path: this.chatChannelPath - }, "*"); - } - } - - get $iframe() { - if (!$sharedIframe) { - const $iframe = document.createElement("iframe"); - $iframe.setAttribute("id", "chat"); - $iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms allow-popups"); - $iframe.setAttribute("referrerpolicy" ,"origin"); - $iframe.src = this.chatChannelUrl; - $sharedIframe = $iframe; - } else { - this.updateChannel(); - } - return $sharedIframe; - } - - static get styles() { - return css` - iframe { - width: 100%; - height: 100%; - border: 0; - } - `; - } - - render() { - return this.$iframe; - } - -} -customElements.define("ros-rocketchat-frame", RocketchatFrame); diff --git a/elements/rocketchat/subscriptions.js b/elements/rocketchat/subscriptions.js deleted file mode 100644 index 4886931..0000000 --- a/elements/rocketchat/subscriptions.js +++ /dev/null @@ -1,111 +0,0 @@ -import { LitElement, html } from '../../web_modules/lit.js'; -import { LitNotify } from '../../lib/lit-element-notify.js'; - -class Rocketchat extends LitElement { - - constructor() { - super(); - this.lastUpdated = null; - } - - static get properties() { - return { - lastUpdated: { - type: Date, - notify: true - } - } - } - - get apiBaseUrl() { - return "https://chat.radicallyopensecurity.com/api/v1/"; - } - - async fetch(endpoint, queryParams) { - const url = new URL(endpoint, this.apiBaseUrl); - if (queryParams !== undefined) { - url.search = queryParams.toString(); - } - const data = await fetch(url, { - mode: "cors", - credentials: "include" - }).then((response) => response.json()); - this.lastUpdated = new Date(); - return data; - } - -} - -class RocketchatSubscriptions extends LitNotify(Rocketchat) { - - constructor() { - super(); - this.unread = []; - this.subscriptions = []; - this.query(); - } - - static get queryInterval() { - return 15 * 1000; // milliseconds - } - - get cronEvent() { - return async (e) => { - this.query(); - }; - } - - connectedCallback() { - super.connectedCallback(); - this._interval = setInterval(this.cronEvent, this.constructor.queryInterval); - } - - disconnectedCallback() { - clearInterval(this._interval); - } - - static get properties() { - return { - ...super.properties, - unread: { - type: Array, - notify: true - }, - subscriptions: { - type: Array, - notify: true - } - }; - } - - async query() { - if (this.querying === true) { - return; - } - this.querying = true; - const searchParams = new URLSearchParams(); - if (this.lastUpdated instanceof Date) { - searchParams.append("updatedSince", this.lastUpdated.toString()); - } - const rooms = (await this.fetch("subscriptions.get", searchParams)).update - .filter((update) => update.t === "p") // rooms only - - const oldSubscriptions = this.subscriptions; - const knownSubscriptions = this.subscriptions; - for (let updatedRoom of rooms) { - let currentSubscription = knownSubscriptions.findIndex((subscription) => subscription._id === updatedRoom._id); - if (currentSubscription !== -1) { - knownSubscriptions[currentSubscription] = updatedRoom; - } else { - knownSubscriptions.unshift(updatedRoom); - } - } - this.subscriptions = [...knownSubscriptions]; - this.unread = knownSubscriptions - .filter((update) => !!update.alert); - - this.querying = false; - } - -} -customElements.define("rocketchat-subscriptions", RocketchatSubscriptions); diff --git a/elements/ros/finding.js b/elements/ros/finding.js deleted file mode 100644 index cebe3e0..0000000 --- a/elements/ros/finding.js +++ /dev/null @@ -1,101 +0,0 @@ -import marked from '../../lib/marked.js'; -import { LitElement, html } from '../../web_modules/lit.js'; -import { GitlabIssue } from '../gitlab/issue.js'; -import '../ros/ui/unsafe-finding-content.js'; - -export class Finding extends GitlabIssue { - - static get properties() { - return { - ...super.properties, - gitlabProjectFullPath: { - type: String - } - }; - } - - get title() { - this.gitlabIssueData.title; - } - - get iid() { - return this.gitlabIssueIid; - } - - get description() { - return this.gitlabIssueData.description; - } - - get technicalDescription() { - const issueDiscussionComments = this.gitlabIssueComments; - if (!issueDiscussionComments || !issueDiscussionComments.length) { - return "ToDo"; - } - return issueDiscussionComments[0].notes[0].body; - } - - _findIssueComment(topic) { - const issueDiscussionComments = this.gitlabIssueComments - .filter((comment) => comment.notes[0].body.toLowerCase().startsWith(topic.toLowerCase())); - - if (!issueDiscussionComments || !issueDiscussionComments.length) { - return "ToDo"; - } - - const lines = issueDiscussionComments[0].notes[0].body - .split("\n") - .filter((line) => !line.match(/^\s*$/)); // remove empty lines - - lines.shift(); - return lines.join("\n"); - } - - get recommendation() { - return this._findIssueComment("recommendation"); - } - - get impact() { - return this._findIssueComment("impact"); - } - - render() { - - if (!this.fetched || !this.gitlabIssueData) { - return html` - -
-
- Loading... -
-
- `; - } - - const description = marked(this.description, { gfm: true }); - const technicalDescription = marked(this.technicalDescription, { gfm: true }); - const impact = marked(this.impact, { gfm: true }); - const recommendation = marked(this.recommendation, { gfm: true }); - - const body = ` - ${description} - -

Technical Description

- ${technicalDescription} - -

Impact

- ${impact} - -

Recommendation

- ${recommendation} - `; - - const finding = document.createElement("ros-ui-unsafe-finding-content"); - finding.baseUrl = this.gitlabProjectFullPath; - finding.unsafeHTML = body; - - return html`${finding}`; - } - -} - -customElements.define("ros-finding", Finding); diff --git a/elements/ros/non-finding.js b/elements/ros/non-finding.js deleted file mode 100644 index 759ee52..0000000 --- a/elements/ros/non-finding.js +++ /dev/null @@ -1,52 +0,0 @@ -import marked from '../../lib/marked.js'; -import { LitElement, html } from '../../web_modules/lit.js'; -import { GitlabIssue } from '../gitlab/issue.js'; -import '../ros/ui/unsafe-finding-content.js'; - -export class NonFinding extends GitlabIssue { - - static get properties() { - return { - ...super.properties, - gitlabProjectFullPath: { - type: String - } - }; - } - - get title() { - this.gitlabIssueData.title; - } - - get iid() { - return this.gitlabIssueIid; - } - - get description() { - return this.gitlabIssueData.description; - } - - render() { - - if (!this.gitlabIssueData) { - return html` - -
-
- Loading... -
-
- `; - } - - const description = marked(this.description, { gfm: true }); - const nonFinding = document.createElement("ros-ui-unsafe-finding-content"); - nonFinding.baseUrl = this.gitlabProjectFullPath; - nonFinding.unsafeHTML = description; - - return html`${nonFinding}`; - } - -} - -customElements.define("ros-non-finding", NonFinding); diff --git a/elements/ros/overview.js b/elements/ros/overview.js deleted file mode 100644 index a7a92cc..0000000 --- a/elements/ros/overview.js +++ /dev/null @@ -1,142 +0,0 @@ -import moment from '../../web_modules/moment.js'; -import { LitElement, html, css } from '../../web_modules/lit.js'; -import { classMap } from '../../web_modules/lit-html/directives/class-map.js'; -import { RosProjects } from '../ros/projects.js'; -import '../rocketchat/iframe.js'; -import '../ui/icon.js'; -import '../ui/breadcrumbs.js'; -import '../ui/content-card.js'; - -class Overview extends LitElement { - - constructor() { - super(); - this.gitlabUser = null; - this.projects = []; - this.loading = true; - } - - static getAvatarUrl(project) { - return project.avatar_url || project.namespace.avatar_url; - } - - static get properties() { - return { - projects: { - type: Array - }, - gitlabUser: { - type: Object - }, - loading: { - type: Boolean - } - }; - } - - static get styles() { - return css` - .small { - font-size: 0.75em; - } - - .avatar { - width: 48px; - height: 48px; - } - - #hiddenIframe { - position: absolute; - width: 0; - height: 0; - overflow: hidden; - opacity: 0; - } - `; - } - - renderSection(name, projects) { - return html` -
-

${name}

-
- - `; - } - - render() { - - const loadingIndicatorClass = classMap({ - "spinner-border": true, - "me-2": true, - "d-none": !this.loading - }); - - const pentests = this.projects.filter((project) => project.isPentest); - const offertes = this.projects.filter((project) => project.isOfferte); - - return html` - - -
-
- -
-

ROS Dashboard

-
- Loading... -
-
-
-
-

- Hi ${this.gitlabUser.name}, -

-

- You have access to ${pentests.length} pentest projects. -

-
-
-
- ${this.projects.length > 0 ? html` -
- - ${this.renderSection("Recently Updated", this.projects.slice(0, 5))} - -
- ` : (!this.loading) ? html` -

No projects found

- ` : ''} -
-
-
- -
- `; - } - -} -customElements.define("ros-overview", Overview); diff --git a/elements/ros/project-new.js b/elements/ros/project-new.js deleted file mode 100644 index 2293b27..0000000 --- a/elements/ros/project-new.js +++ /dev/null @@ -1,284 +0,0 @@ -import { LitElement, html } from '../../web_modules/lit.js'; -import { LitSync, LitNotify } from '../../lib/lit-element-notify.js'; -import { gitlabAuth } from '../gitlab/index.js'; -import { GitlabProject} from '../gitlab/project.js'; -import { DropdownInput } from '../ui/input/dropdown.js'; -import { generatePassword } from '../utils/generate-password.js' -import '../ui/breadcrumbs.js'; -import '../ui/content-card.js'; -import '../ros/project/member-chooser.js'; - -const PM_GROUP_PATH = "pm"; -const TEMPLATE_GROUP_PATH = "pentext"; - -class GitlabNamespaceChooser extends DropdownInput { - - constructor() { - super(); - this.path = "/api/v4/groups"; - this.params = { - all_available: true, - min_acces_level: 40 - } - } - - static get properties() { - return { - ...super.properties, - value: { - type: Number, - notify: true - } - } - } - - static mapOptions(item) { - return { - value: item.id, - label: item.full_path - } - } - - async query(url) { - const _url = (url === undefined) ? this.url : url; - const groups = await super.query(url); - const enhancedGroups = await Promise.all( - groups - .filter((group) => ![PM_GROUP_PATH, TEMPLATE_GROUP_PATH].includes(group.label)) - .map(async (group) => { // enhance data with group details - const groupUrl = new URL(`${this.path}/${group.value}`, window.location.toString()); - const response = await fetch(groupUrl.toString()); - const data = await response.json(); - return data; - }) - ); - - // groups listed must have the project topic/tag set - return enhancedGroups - .filter((group) => group.shared_with_groups.some((sharedGroup) => { - return sharedGroup.group_full_path == PM_GROUP_PATH; - })) - .map(this.constructor.mapOptions); // map again - } - -} -customElements.define("gitlab-namespace-chooser", GitlabNamespaceChooser); - -class GitlabTemplateChooser extends DropdownInput { - - static get properties() { - return { - ...super.properties, - topic: { - type: String, - notify: true - } - } - } - - willUpdate(changedProperties) { - super.willUpdate(changedProperties); - if (changedProperties.has("topic") || changedProperties.has("_options")) { - const options = this.options; - if (options !== undefined && !!this.options.length) { - this.value = options[0].value; - } - } - } - - get options() { - if (this.topic == undefined) { - return this._options; - } - return this._options.filter((option) => option.tag_list.includes(this.topic)); - } - - set options(options) { - this._options = options; - } - - constructor() { - super(); - this.path = `/api/v4/groups/${TEMPLATE_GROUP_PATH}/projects`; - this.params = { - scope: "projects", - per_page: 100 - }; - } - - static mapOptions(item) { - return { - value: item.http_url_to_repo, - label: item.name, - tag_list: item.tag_list - } - } - -} -customElements.define("gitlab-template-chooser", GitlabTemplateChooser); - -class NewRosProject extends LitSync(GitlabProject) { - - constructor() { - super(); - this.topic = "offerte"; - this.title = ""; - this.namespace_id = null; - this.import_url = null; - } - - static get properties() { - return { - title: { - type: String - }, - namespace_id: { - type: Number - }, - import_url: { - type: String - }, - topic: { - type: String // pentest, offerte, ... - } - }; - } - - get onSubmitForm() { - return async (e) => { - e.preventDefault(); - e.stopPropagation(); - - const import_url = new URL(this.import_url); - import_url.username = "gitlab-ci-token"; - import_url.password = gitlabAuth.token; - - const createOptions = { - import_url: import_url.toString(), - default_branch: "main", - wiki_access_level: "disabled", - pages_access_level: "disabled", - issues_access_level: "private", - path: this.slug, - packages_enabled: false, - namespace_id: this.namespace_id, - topics: [this.topic], - enforce_auth_checks_on_uploads: false - } - - const nextYear = new Date(); - nextYear.setDate(nextYear.getDate() + 365); - - const project = await this.post("/api/v4/projects", {}, { - body: JSON.stringify(createOptions) - }); - - const accessTokenResponse = await this.post(`/api/v4/projects/${project.id}/access_tokens`, {}, { - body: JSON.stringify({ - scopes: ["api"], - name: "CI", - expires_at: nextYear - }) - }); - - // ToDo after 2021-03-22 release fixing https://gitlab.com/gitlab-org/gitlab/-/merge_requests/55408 - const projectAccessTokenVariableResponse = await this.post(`/api/v4/projects/${project.id}/variables`, {}, { - body: JSON.stringify({ - key: "PROJECT_ACCESS_TOKEN", - value: accessTokenResponse.token, - protected: false, - masked: true - }) - }); - - await this.post(`/api/v4/projects/${project.id}/variables`, {}, { - body: JSON.stringify({ - key: "PDF_PASSWORD", - value: generatePassword(), - protected: false, - masked: true - }) - }); - - window.location.hash = project.id.toString(); - } - } - - get slug() { - return `${this.title}`; - } - - render() { - return html` - - - - -
-

Create New Project

-
- -
- -
-
-
- -
-
- -
-
-
-
- -
- -
-
-
- -
- -
-
-
-
- -
-
-
-
- `; - } - - get valid() { - if (this.title && this.title.length) { - return true; - } - return false; - } - - get onChangeInput() { - return (e) => { - e.stopPropagation(); - this[e.target.name] = e.currentTarget.value; - } - } - - get onChangeSelection() { - return (e) => { - e.stopPropagation(); - this[e.currentTarget.name] = e.currentTarget.value; - }; - } - -} -customElements.define("ros-project-new", NewRosProject); diff --git a/elements/ros/project.js b/elements/ros/project.js deleted file mode 100644 index 9c68a3d..0000000 --- a/elements/ros/project.js +++ /dev/null @@ -1,763 +0,0 @@ -import moment from '../../web_modules/moment.js'; -import marked from '../../lib/marked.js'; -import { LitElement, html, css } from '../../web_modules/lit.js'; -import { classMap } from '../../web_modules/lit-html/directives/class-map.js'; -import { repeat } from '../../web_modules/lit-html/directives/repeat.js'; -import { LitNotify } from '../../lib/lit-element-notify.js'; -import { GitlabProject } from '../gitlab/project.js'; -import { Finding } from '../ros/finding.js'; -import { getChannelUrl } from '../rocketchat/iframe.js'; -import { gitlabAuth } from '../gitlab/index.js'; -import '../gitlab/avatar.js'; -import '../pdf-password.js'; -import { ContentCard } from '../ui/content-card.js'; -import '../ui/accordion.js'; -import '../ros/finding.js'; -import '../ros/non-finding.js'; -import '../ros/ui/unsafe-finding-content.js'; -import '../ui/icon.js'; -import './project/projectActivity.js'; -import './project/projectRecentIssues.js'; - -const gitlabCiJobName = "build"; -const gitlabProjectPathPattern = /^(?[a-zA-Z]+)\/(?:(?pen|off)-)?(?[a-zA-Z0-9](?:-?[a-zA-Z0-9]+)*)$/; - -class ChatContentCard extends ContentCard { - - get _cardClasses() { - const classes = super._cardClasses; - delete classes.rounded; - classes["rounded-bottom"] = true; - return classes; - } - - static get styles() { - return css` - ${super.styles} - - #card { - height: auto; - } - - @media (min-width: 576px) { - #card { - height: 300px; - } - } - `; - } -} -customElements.define("project-ui-content-card-chat", ChatContentCard); - -export class Project extends LitNotify(GitlabProject) { - - constructor() { - super(); - this.fullscreen = true; - this.offerte = null; - } - - static get properties() { - return { - ...GitlabProject.properties, - subroute: { - type: String, - reflect: true - }, - availableSubroutes: { - type: Object, - notify: true - }, - fullscreen: { - type: Boolean, - notify: true, - reflect: true - }, - pageTitle: { - type: String, - notify: true - }, - - offerte: { - type: Object, - notify: true - }, - - archived: { - type: Boolean, - notify: true - }, - - _selectedChatTabState: { - type: Boolean - } - } - } - - willUpdate(changedProperties) { - this.availableSubroutes = this.constructor.subroutes; - if (changedProperties.has("gitlabProjectId")) { - this.queryOfferte(); - } - super.willUpdate(changedProperties); - } - - updated(changedProperties) { - super.updated(changedProperties); - const keys = [...changedProperties.keys()]; - if (keys.includes("gitlabProjectData")) { - if (this.gitlabProjectData instanceof Object) { - this.archived = this.isArchived - - if (this.gitlabProjectData.namespace.path === "ros") { - this.pageTitle = this.gitlabProjectData.name; - } else { - this.pageTitle = this.gitlabProjectData.name_with_namespace; - } - } else { - this.pageTitle = undefined; - } - } - } - - static get subroutes() { - return { - overview: { - title: "Overview", - icon: "pie-chart" - }, - findings: { - title: "Findings", - icon: "gitlab" - }, - chat: { - title: "Chat", - icon: "message-square" - } - }; - } - - get activeSubroute() { - if (this.subroute == undefined) { - return Object.entries(this.constructor.subroutes) - .filter(([name, options]) => options.default === true) - .map(([name, options]) => name) - .pop(); - } else { - return this.subroute; - } - } - - async queryOfferte() { - this.offerte = null; - this.offerte = await fetch(`/api/v4/projects/${this.gitlabProjectId}/repository/files/source%2Fofferte.xml?ref=main`) - .then(response => response.json()) - .then(filedata => atob(filedata.content)) - .then(text => (new window.DOMParser()).parseFromString(text, "text/xml")) - .then(xmldata => { - const planning = xmldata.getElementsByTagName("planning")[0]; - const start = planning.getElementsByTagName("start")[0].textContent; - const end = planning.getElementsByTagName("end")[0].textContent; - return { - start: moment(start, "YYYY-MM-DD"), - end: moment(end, "YYYY-MM-DD") - }; - }); - } - - get title() { - if (this.gitlabProjectData.name.startsWith("pen-") || this.gitlabProjectData.name.startsWith("off-")) { - return this.gitlabProjectData.name.substr(4); - } - return this.gitlabProjectData.path; - } - - get allFindings() { - return this.gitlabProjectIssues.filter((gitlabIssue) => gitlabIssue.labels.some((label) => label.toLowerCase() === "finding" || label.toLowerCase() === "non-finding")); - } - - get recentFindings() { - return this.allFindings.slice(0, 5); - } - - get findings() { - return this.gitlabProjectIssues - .filter((gitlabIssue) => gitlabIssue.state !== "closed") - .filter((gitlabIssue) => gitlabIssue.labels.some((label) => label.toLowerCase() === "finding")); - } - - get nonFindings() { - return this.gitlabProjectIssues - .filter((gitlabIssue) => gitlabIssue.state !== "closed") - .filter((gitlabIssue) => gitlabIssue.labels.some((label) => label.toLowerCase() === "non-finding")) - // .map((gitlabIssue) => html` - // - // `); - } - - get members() { - return this.gitlabProjectMembers - .filter((member) => member.username !== `project_${this.gitlabProjectId}_bot`) - .filter((member) => ["rosbot", "golem"].indexOf(member.username.toLowerCase()) === -1); - } - - get staff() { - return this.members - .filter((member) => member.access_level >= 40); - } - - get customers() { - return this.members - .filter((member) => member.access_level < 40); - } - - get states() { - - const states = { - offerte: { - enabled: true, - prefix: "off", - documentName: "Offerte" - }, - pentest: { - enabled: false, - prefix: "pen", - documentName: "Report" - } - }; - - this.gitlabProjectData.tag_list - .forEach((tag) => { - if (states[tag]) { - states[tag].enabled = true; - } - }); - - return states; - - } - - get enabledStates() { - const enabledStates = {}; - Object.entries(this.states) - .filter(([name, options]) => !!options.enabled) - .forEach(([name, options]) => enabledStates[name] = options); - return enabledStates; - } - - get selectedChatTabState() { - const states = this.enabledStates; - if (states.length === 0) { - return "offerte"; - } - return this._selectedChatTabState || Object.keys(states).pop(); - } - - set selectedChatTabState(value) { - if (!Object.keys(this.states).includes(value)) { - throw new Error("Invalid chat tab state"); - } - this._selectedChatTabState = value; - } - - get onClickChatTab() { - return (e) => { - if (e.shiftKey || e.altKey) { - return; - } - e.preventDefault(); - e.stopPropagation(); - const target = e.target; - this._selectedChatTabState = target.getAttribute("name"); - } - } - - static get severities() { - return [ - "ToDo", - "Extreme", - "High", - "Elevated", - "Moderate", - "Low", - "Unknown" - ]; - } - - static get severityLabelPrefix() { - return "ThreatLevel:"; - } - - get severityLabels() { - const prefix = this.constructor.severityLabelPrefix; - const severities = this.constructor.severities; - return this.gitlabProjectLabels - .filter((label) => { - return severities - .map((severity) => `${prefix}${severity}`.toLowerCase()) - .includes(label.name.toLowerCase()); - }) - .map((label) => { - return { - ...label, - severity: label.name.substr(prefix.length) - }; - }) - .sort((a, b) => severities.indexOf(a.name) - severities.indexOf(b.name)) - .reduce((curr, next) => { - curr[next.severity] = next; - return curr; - }, {}); - } - - get findingsBySeverity() { - const output = {}; - this.findings.forEach((finding) => { - let severity = "ToDo"; - for (let label of finding.labels) { - if (label.toLowerCase().startsWith(this.constructor.severityLabelPrefix.toLowerCase())) { - severity = label.substr(this.constructor.severityLabelPrefix.length); - break; - } - } - if (!output.hasOwnProperty(severity)) { - output[severity] = []; - } - output[severity].push(finding); - }); - - const orderedOutput = {}; - this.constructor.severities.forEach((severity) => { - if (output.hasOwnProperty(severity)) { - orderedOutput[severity] = output[severity]; - } - }); - - return orderedOutput; - } - - severityColorStyle(severity) { - const severityLabels = this.severityLabels; - if (!severityLabels.hasOwnProperty(severity)) { - return 'color: #212529 !important; background-color: #ffc107 !important;'; - } else { - const label = severityLabels[severity]; - return `color: ${label.text_color} !important; background-color: ${label.color}`; - } - } - - get eventsByDay() { - const out = {}; - this.gitlabProjectEvents.forEach((event) => { - const date = event.created_at; - const key = moment(date).format("YYYY-MM-DD"); - if (!out.hasOwnProperty(key)) { - out[key] = []; - } - out[key].push(event); - }); - return out; - } - - get branchName() { - return "main"; - } - - getChannelName(prefix) { - const namespace = this.gitlabProjectData.namespace.path; - const projectPath = this.gitlabProjectData.path_with_namespace; - const match = projectPath.match(gitlabProjectPathPattern); - - if (match === null) { - return; - } - - prefix = prefix || match.groups.prefix || this.states[this.selectedChatTabState].prefix; - - if (namespace === "ros") { - return `${prefix}-${match.groups.name}`; - } else { - return `${namespace}-${prefix}-${match.groups.name}`; - } - } - - get channelName() { - return this.getChannelName(); - } - - getArtifactDownloadUrl(documentType) { - return `/api/v4/projects/${this.gitlabProjectId}/jobs/artifacts/${this.branchName}/raw/target/${this.getAssetFileName(documentType)}?job=${gitlabCiJobName}`; - } - - getAssetFileName(documentType) { - const availableDocumentTypes = Object.values(this.states) - .map((state) => state.documentName.toLowerCase()); - - if (availableDocumentTypes.includes(documentType)) { - return `${documentType}_${this.title}.pdf`; - } - - throw new Error("Unsupported document type"); - } - - get pdfPassword() { - if (this.gitlabProjectVariables instanceof Array) { - const match = this.gitlabProjectVariables - .filter((data) => data.key.toUpperCase() === "PDF_PASSWORD"); - if (match.length === 1) { - return match[0].value; - } - } - } - - static getAvatarUrl(project) { - return project.avatar_url || project.namespace.avatar_url; - } - - static get styles() { - return css` - .avatar { - width: 138px; - height: 138px; - } - - @media (max-width: 767px) { - .avatar { - width: 192px; - height: 192px; - } - } - - .list-group-item { - cursor: pointer; - transition: background-color 0.2 ease; - } - - .nav-tabs { - border-bottom: none; - } - - .nav-tabs .nav-item { - border: none !important; - } - - .nav-tabs .nav-link.active { - font-weight: bold; - } - - #chat-card { - position: relative; - } - - :host-context(:host:not([subroute])) [subroute="overview"] { - display: block !important; - } - :host(:not([subroute])) [subroute="overview"] { - display: block !important; - } - - :host([subroute="overview"]) [subroute="overview"], - :host([subroute="chat"]) [subroute="chat"], - :host([subroute="findings"]) [subroute="findings"] { - display: block !important; - } - - .flex-item { - flex-grow: 1; - flex-shrink: 1; - flex-base: 0; - } - - a.member-link { - text-decoration: none; - } - - a.member-link span { - text-decoration: underline; - } - `; - } - - get isArchived() { - return this.gitlabProjectData.topics.includes('archive') - } - - async onClickArchive() { - const token = gitlabAuth.token; - - let topics = this.gitlabProjectData.topics - if (topics.includes('archive')) { - topics = topics.filter(x => x !== 'archive') - } else { - topics = topics.concat(['archive']) - } - - const res = await fetch(`/api/v4/projects/${this.gitlabProjectId}`, { - method: 'PUT', - headers: { - 'PRIVATE-TOKEN': token, - 'content-type': 'application/json' - }, - body: JSON.stringify({ - topics - }) - }) - - if (!res.ok) { - alert(`Failed: ${await res.text()}`) - return - } - - this.gitlabProjectData = { - ...this.gitlabProjectData, - topics - } - } - - render() { - if (this.gitlabProjectData === null) { - return html` - -
-
- Loading... -
-
-

loading ${this.gitlabProjectId}...

- `; - } - - const findings = this.findings; - const nonFindings = this.nonFindings; - - return html` - - - -
-
-
-
-
-
-
${this.gitlabProjectData.namespace.name}
-
-
-

${this.gitlabProjectData.name}

-
-
-
    -
  • ${findings.length} finding${(findings.length === 1) ? "" : "s"}
  • -
  • ${nonFindings.length} non-finding${(nonFindings.length === 1) ? "" : "s"}
  • -
-
-
-
- ${this.gitlabProjectData.web_url !== undefined ? html` - - - Git - - ` : ''} -
- ${Object.keys(this.enabledStates).includes("offerte") ? html` -
- ${this.gitlabProjectData.web_url !== undefined ? html` - - - Offerte - - ` : ''} -
- ` : ''} - ${Object.keys(this.enabledStates).includes("pentest") ? html` -
-
- ${!!this.pdfPassword ? html` - - ` : ``} - - Report - - -
-
- ` : ''} - ${html` -
-
- -
-
`} -
- ${this.offerte !== null ? html` -
-
Start: ${this.offerte.start.format("DD.MM.YYYY")}
-
End: ${this.offerte.end.format("DD.MM.YYYY")}
-
- ` : undefined} -
-
- -
-
-
- -
-
-
- -
- - - -
-
-
-
-
-
-
- -

Recent Changes

-
- ${this.recentFindings.map((finding) => html` - - `)} -
-
-
-
- -
-
-
Crew
-
- ${this.staff.map((member) => html` - - -
- `)} -
-
-
-
Stakeholder${this.customers.length > 1 ? "s" : ""}
-
- ${this.customers.map((member) => html` - - `)} -
-
-
- -
-
-
- -
-
- -

Findings ${findings.length}

- ${repeat(Object.entries(this.findingsBySeverity), ([severity, findings]) => severity, ([severity, findings]) => html` -
${severity} ${findings.length}
- ${finding.iid} - ${finding.title} - `; - const $rosFinding = document.createElement('ros-finding'); - $rosFinding.autoload = false; - $rosFinding.gitlabProjectId = this.gitlabProjectId; - $rosFinding.gitlabProjectFullPath = this.gitlabProjectData.web_url; - $rosFinding.gitlabIssueData = finding; - $rosFinding.gitlabIssueIid = finding.iid; - $rosFinding.onBecomeVisible = async function () { - await $rosFinding.fetch(); - $rosFinding.requestUpdate("gitlabIssueData"); - }; - return { title, content: $rosFinding, id: `${this.gitlabProjectId}::${finding.iid}` }; - })}"> - `)} -

Non-Findings ${nonFindings.length}

-
- ${nonFinding.iid} - ${nonFinding.title} - `; - const $rosNonFinding = document.createElement('ros-non-finding'); - $rosNonFinding.autoload = false; - $rosNonFinding.gitlabProjectId = this.gitlabProjectId; - $rosNonFinding.gitlabProjectFullPath = this.gitlabProjectData.web_url; - $rosNonFinding.gitlabIssueData = nonFinding; - $rosNonFinding.gitlabIssueIid = nonFinding.iid; - return { title, content: $rosNonFinding, id: `${this.gitlabProjectId}::${nonFinding.iid}` }; - })}"> -
-
-
-
- -

History

- ${dateString} - `; - - const content = events.map((eventData) => html` - - `); - return { title, content, id: dateString }; - })}"> -
-
-
- `; - } -}; - -customElements.define("ros-project", Project); \ No newline at end of file diff --git a/elements/ros/project/member-chooser.js b/elements/ros/project/member-chooser.js deleted file mode 100644 index ea16f9a..0000000 --- a/elements/ros/project/member-chooser.js +++ /dev/null @@ -1,42 +0,0 @@ -import { AutocompleteInput } from '../../ui/input/autocomplete.js'; - -class ProjectMemberChooser extends AutocompleteInput { - - constructor() { - super(); - this.path = "/api/v4/search"; - } - - static get properties() { - return { - ...super.properties, - gitlabProjectId: { - type: Number - } - } - } - - updated(changedProperties) { - super.updated(changedProperties); - const keys = [...changedProperties.keys()]; - if (keys.includes("value") && this.value !== undefined) { - addMember(this.value.id) - .then(() => { - this.value = undefined; - this.suggestions = null; - }); - } - } - - async addMember(user_id) { - const params = { - access_level: 30, - user_id: user_id - }; - const project = await this.post(`/api/v4/projects/${this.gitlabProjectId}/members`, {}, { - body: JSON.stringify(createOptions) - }); - } - -} -customElements.define("ros-project-member-chooser", ProjectMemberChooser); \ No newline at end of file diff --git a/elements/ros/project/projectActivity.js b/elements/ros/project/projectActivity.js deleted file mode 100644 index de85dc9..0000000 --- a/elements/ros/project/projectActivity.js +++ /dev/null @@ -1,72 +0,0 @@ -import moment from '../../../web_modules/moment.js'; -import { LitElement, html, css } from '../../../web_modules/lit.js'; - -export class ProjectActivity extends LitElement { - - constructor() { - super(); - this.data = null; - this.project = ""; - } - - static get properties() { - return { - data: { - type: Object - }, - project: { - type: Object - }, - dateFormat: { - type: String - } - }; - } - - render() { - if (!this.data) { - return html``; - } - - const created_at = new Date(this.data.created_at); - - let $message; - switch (this.data.action_name) { - case "pushed to": - $message = html`pushed ${this.data.push_data.commit_count} commits: ${this.data.push_data.commit_title}`; - break; - case "opened": - case "closed": - case "updated": - $message = html`${this.data.action_name} #${this.data.target_iid} ${this.data.target_title}`; - break; - case "commented on": - $message = html`${this.data.action_name} ${this.data.target_title} (#${this.data.target_iid})`; - break; - case "created": - $message = html`created the project`; - break; - default: - $message = html`${this.data.action_name}`; - break; - } - - return html` - -
- -
-

- ${this.data.author.name} - ${$message} -

- ${moment(created_at).format("HH:mm")} -
-
- `; - - - } - -} -customElements.define("ros-project-activity", ProjectActivity); \ No newline at end of file diff --git a/elements/ros/project/projectRecentIssues.js b/elements/ros/project/projectRecentIssues.js deleted file mode 100644 index de3124e..0000000 --- a/elements/ros/project/projectRecentIssues.js +++ /dev/null @@ -1,47 +0,0 @@ -import moment from '../../../web_modules/moment.js'; -import { LitElement, html, css } from '../../../web_modules/lit.js'; - -export class ProjectRecentIssues extends LitElement { - - constructor() { - super(); - this.finding = null; - this.project = null; - } - - static get properties() { - return { - finding: { - type: Object - }, - project: { - type: Object - } - }; - } - - render() { - if (!this.finding) { - return html``; - } - - return html` - -
-
- - ${this.finding.iid} - - - ${this.finding.title} - - - - ${moment(this.finding.updated_at).fromNow()} -
-
- `; - } - -} -customElements.define("ros-project-recent-issues", ProjectRecentIssues); \ No newline at end of file diff --git a/elements/ros/projects-table.js b/elements/ros/projects-table.js deleted file mode 100644 index af5f4d1..0000000 --- a/elements/ros/projects-table.js +++ /dev/null @@ -1,127 +0,0 @@ -import moment from '../../web_modules/moment.js'; -import { LitElement, html, css } from '../../web_modules/lit.js'; -import { classMap } from '../../web_modules/lit-html/directives/class-map.js'; - -class ProjectsTable extends LitElement { - - constructor() { - super(); - this.projects = []; - this.sortedProjects = []; - this.offertes = {}; - this.sort = undefined; - } - - static get properties() { - return { - projects: { - type: Array - }, - sortedProjects: { - type: Array - }, - sort: { - type: String - } - } - } - - get sortFunction() { - switch (this.sort) { - case "id": - return (a, b) => a.id > b.id ? 1 : -1; - case "namespace": - return (a, b) => a.namespace.name.localeCompare(b.namespace.name); - case "name": - return (a, b) => a.name.localeCompare(b.name); - case "start": - return (a, b) => { - if (a.offerte.xmlData == null) { - return 1; - } else if (b.offerte.xmlData == null) { - return -1; - } else { - return a.offerte.start.isBefore(b.offerte.start); - } - }; - default: - return undefined; - } - } - - static get styles() { - return css` - table[sort=id] th[name=id] a, - table[sort=namespace] th[name=namespace] a, - table[sort=name] th[name=name] a, - table[sort=start] th[name=start] a, - table[sort=end] th[name=end] a, - table[sort=report] th[name=report] a, - table[sort=report_date] th[name=report_date] a { - color: var(--ros-orange, red); - } - `; - } - - willUpdate(changedProperties) { - if (changedProperties.has("projects")) { - const sortFunction = this.sortFunction; - this.sortedProjects = (sortFunction !== undefined) ? [...this.projects.sort(sortFunction)] : this.projects; - } - } - - render() { - - if (this.projects.length === 0) { - return html` -
No Projects found.
- `; - } - - return html` - - - - - - - - - - - - ${this.sortedProjects.map((project) => { - - let columnClasses = { - foo: true - }; - const today = moment().hours(0).minutes(0).seconds(0); - - const offerte = project.offerte; - const report = project.report; - - const start = offerte.start; - - if (project.name === "homeshoppingcenter") { - debugger; - } - - return html` - - - - - - - - - - `; - })} -
IDNamespaceProjectStartEndReport DueLatest Report Version
${project.id}${project.namespace.name}${project.name}${offerte.start}${offerte.end}${offerte.report_due}${report.latest_version_date}
- ` - } - -} - -customElements.define("ros-projects-table", ProjectsTable); \ No newline at end of file diff --git a/elements/ros/projects.js b/elements/ros/projects.js deleted file mode 100644 index f524c74..0000000 --- a/elements/ros/projects.js +++ /dev/null @@ -1,367 +0,0 @@ -import moment from '../../web_modules/moment.js'; -import { LitElement, html, css } from '../../web_modules/lit.js'; -import { directive } from "../../web_modules/lit/directive.js"; -import { AsyncDirective } from "../../web_modules/lit/async-directive.js"; -import { GitlabProjects } from '../gitlab/projects.js'; - -const gitlabProjectPathPattern = /^(?[a-zA-Z]+)\/(?:(?pen|off)-)?(?[a-zA-Z0-9](?:-?[a-zA-Z0-9]+)*)$/; - -moment.fn.toString = function() { return this.format("YYYY-MM-DD"); } - -const isPentest = (project) => { - return project.name.startsWith("pen-") || project.tag_list.includes("pentest"); -}; - -const isOfferte = (project) => { - return project.name.startsWith("off-") || project.tag_list.includes("offerte"); -}; - -export const xmlDocumentsCache = {}; - -class CachedProjectXMLFile { - - constructor(gitlabProjectId, ref, sourceFile) { - this.gitlabProjectId = gitlabProjectId; - this.ref = ref || "main"; - this.sourceFile = sourceFile; - this.directive = directive(CachedXMLDirective); - } - - queryXMLFile() { - // fetch new document - const query = fetch(`/api/v4/projects/${this.gitlabProjectId}/repository/files/${encodeURIComponent(this.sourceFile)}?ref=${this.ref}`); - return query - .then((response) => { - if (response.status !== 200) { - this.xmlData = null; - throw new Error(`${this.sourceFile} in project ${this.gitlabProjectId}`); - } - return response.json(); - }) - //.then(filedata => atob(filedata.content)) - .then(filedata => new TextDecoder().decode(Uint8Array.from(atob(filedata.content), c => c.charCodeAt(0)))) - .then(text => (new window.DOMParser()).parseFromString(text, "text/xml")); - } - - get xmlData() { - const gitlabProjectId = Number(this.gitlabProjectId); - if ( - !xmlDocumentsCache.hasOwnProperty(gitlabProjectId) - || !xmlDocumentsCache[gitlabProjectId].hasOwnProperty([this.ref]) - || !xmlDocumentsCache[gitlabProjectId][this.ref].hasOwnProperty(this.sourceFile) - ) { - this.xmlData = this.queryXMLFile(); - } - return xmlDocumentsCache[gitlabProjectId][this.ref][this.sourceFile]; - } - - set xmlData(value) { - const gitlabProjectId = Number(this.gitlabProjectId); - if (!xmlDocumentsCache.hasOwnProperty(gitlabProjectId)) { - xmlDocumentsCache[gitlabProjectId] = {}; - } - if (!xmlDocumentsCache[gitlabProjectId].hasOwnProperty([this.ref])) { - xmlDocumentsCache[gitlabProjectId][this.ref] = {}; - } - xmlDocumentsCache[gitlabProjectId][this.ref][this.sourceFile] = value; - } - -} - -class CachedReportXMLFile extends CachedProjectXMLFile { - constructor(gitlabProjectId, ref) { - super(gitlabProjectId, ref, "source/report.xml"); - } - - get latest_version_date() { - return this.directive((xmlData) => { - const version_history = xmlData.getElementsByTagName("version_history")[0]; - const versions = version_history.getElementsByTagName("version"); - return versions[0].getAttribute("date"); - }, this.xmlData, "date"); - } - - get version_history() { - return this.directive((xmlData) => { - return xmlData.getElementsByTagName("version_history")[0].textContent; - }, this.xmlData); - } - -} - -class CachedOfferteXMLFile extends CachedProjectXMLFile { - - constructor(gitlabProjectId, ref) { - super(gitlabProjectId, ref, "source/offerte.xml"); - } - - get start() { - return this.directive(["planning", "start"], this.xmlData, "date"); - } - - get end() { - return this.directive(["planning", "end"], this.xmlData, "date"); - } - - get report_due() { - return this.directive(["report_due"], this.xmlData, "date"); - } - -} - -class CachedXMLDirective extends AsyncDirective { - - constructor() { - super(); - this.value = undefined; - } - - setValue(value) { - super.setValue(value); - this.value = value; - } - - render(selector, xmlData, type) { - // selector can be array of tag names or a map function - - if (xmlData === null) { - return "N/A"; - } - xmlData.then((data) => { - if (data) { - let value; - if (typeof selector === "function") { - value = selector(data); - } else { - for (let key of selector) { - data = data.getElementsByTagName(key)[0]; - } - value = data.textContent; - } - switch (type) { - case "date": - switch (value) { - case "TBD": - this.setValue(html`TBD`); - break; - default: - this.setValue(moment(value, "YYYY-MM-DD")); - break; - } - break; - default: - this.setValue(value); - break; - } - } - }); - return null; - } - -} - -class RosProjectData extends LitElement { - - constructor() { - super(); - this.id = null; - } - - static get properties() { - return { - id: { - type: Number, - reflect: true - } - } - } - - get offerte() { - return new CachedOfferteXMLFile(this.id, this.default_branch, "source/offerte.xml"); - } - - - get report() { - return new CachedReportXMLFile(this.id, this.default_branch, "source/report.xml"); - } - -} -customElements.define("ros-project-data", RosProjectData); - - -export class RosProjects extends GitlabProjects { - - constructor() { - super(); - this.chatSubscriptions = []; - } - - static get properties() { - return { - ...GitlabProjects.properties, - chatSubscriptions: { - type: Array - } - }; - } - - updateProjects() { - const oldProjectsValue = this.projects; - this.projects.forEach((project) => { - const subscriptions = this.chatSubscriptions - .filter((subscription) => project.rocketchatChannelNames.has(subscription.name)); - if (subscriptions) { - project.hasUnreadMessages = subscriptions.some((subscription) => subscription.alert); - project.mentions = subscriptions.reduce((curr, subscription) => (curr + subscription.userMentions), 0); - project.lastChatActivity = subscriptions.reduce((curr, next) => { - const updatedAt = moment(next._updatedAt); - if (!curr || updatedAt.isAfter(curr)) { - return updatedAt; - } else { - return curr; - } - }, null); - if (project.lastChatActivity && project.lastChatActivity.isAfter(project.lastGitActivity)) { - project.lastProjectActivity = project.lastChatActivity; - } else { - project.lastProjectActivity = project.lastGitActivity - } - } else { - project.hasUnreadMessages = false; - project.mentions = 0; - project.lastChatActivity = null; - } - }); - this.projects = [...this.projects.sort(GitlabProjects.sortProjectsByLastActivity)]; - } - - willUpdate(changedProperties) { - super.willUpdate(changedProperties); - if (changedProperties.has("chatSubscriptions") || changedProperties.has("projects")) { - this.updateProjects(); - } - } - - get pentests() { - return this.projects - .filter((project) => isPentest(project)); - } - - get offertes() { - return this.projects - .filter((project) => isOfferte(project)); - } - - static filterProjects(projects) { - return projects - .filter((project) => { - if (project.archived === true) { - return false; - } else if (!isPentest(project) && !isOfferte(project)) { - return false; - } else if (project.namespace.path === "pentext") { - return false; - } - return true; - }); - } - - static mapProjects(projects) { - return projects - .map((inputProject) => { - - const project = document.createElement("ros-project-data"); - Object.entries(inputProject).forEach(([key, value]) => project[key] = value); - - project.isPentest = isPentest(project); - project.isOfferte = isOfferte(project); - - const prefix = project.isPentest ? "pen" : "off"; - const namespace = project.namespace.path; - const projectPath = project.path_with_namespace; - const match = projectPath.match(gitlabProjectPathPattern); - - if (match === null) { - return; - } - - project.rocketchatChannelNames = new Set(); - if (namespace === "ros") { - if (project.isPentext) { - project.rocketchatChannelNames.add(`pen-${match.groups.name}`); - } - if (project.isOfferte) { - project.rocketchatChannelNames.add(`off-${match.groups.name}`); - } - } else { - if (project.isPentext) { - project.rocketchatChannelNames.add(`${namespace}-pen-${match.groups.name}`); - } - if (project.isOfferte) { - project.rocketchatChannelNames.add(`${namespace}-off-${match.groups.name}`); - } - } - - project.lastGitActivity = moment(project.last_activity_at); - project.lastProjectActivity = project.lastGitActivity; - return project; - }) - .filter((project) => (project !== undefined)); - } - - static getAvatarUrl(project) { - return project.avatar_url || project.namespace.avatar_url; - } - -} -customElements.define("ros-projects", RosProjects); - -class RosProjectsList extends RosProjects { - render() { - return html` - - -
-
- -
-

Projects

- ${this.loading ? html` -
- Loading... -
- `: ''} -
-
-
- ${this.projects.length > 0 ? html` - - `: (!this.loading) ? - html`

No projects found

`: ''} -
-
-
-
`; - } -}; -customElements.define("ros-projects-list", RosProjectsList); diff --git a/elements/ros/ui/unsafe-finding-content.css b/elements/ros/ui/unsafe-finding-content.css deleted file mode 100644 index 054ec34..0000000 --- a/elements/ros/ui/unsafe-finding-content.css +++ /dev/null @@ -1,6 +0,0 @@ -img { - width: 100%; -} -h2 { - border-bottom: 1px solid var(--bs-gray); -} \ No newline at end of file diff --git a/elements/ros/ui/unsafe-finding-content.js b/elements/ros/ui/unsafe-finding-content.js deleted file mode 100644 index ebefe04..0000000 --- a/elements/ros/ui/unsafe-finding-content.js +++ /dev/null @@ -1,17 +0,0 @@ -import { UnsafeContent } from '../../ui/unsafe-content.js'; - -class UnsafeFindingContent extends UnsafeContent { - - get contentStyle() { - return ` - - `; - } - - get renderedUnsafeHTML() { - return this.unsafeHTML - .replaceAll(/ { - const hash = window.location.hash.substring(1); - const hashFragments = hash.split("/", 2); - - let view = "default"; - if (hashFragments[0] === "new") { - this.gitlabProjectId = "new"; - } else if (hashFragments[0] === "table") { - view = "table"; - } else { - this.gitlabProjectId = parseInt(hashFragments[0], 10) || null; - } - this.view = view; - this.subroute = hashFragments[1]; - this.forceSidebarVisible = false; - } - } - - willUpdate(changedProperties) { - super.willUpdate(changedProperties); - if (changedProperties.has("gitlabProjectId")) { - if (this.gitlabProjectId !== null) { - this.search = ""; - } else { - this.gitlabProjectData = null; - } - this.availableSubroutes = {}; - } - } - - updated(changedProperties) { - super.updated(changedProperties); - if (changedProperties.has("pageTitle")) { - let pageTitle = "R♡S"; - if (this.pageTitle !== undefined) { - pageTitle += ` _ ${this.pageTitle}`; - } else { - pageTitle += ' _board'; - } - document.title = pageTitle; - } - } - - get onClickSidebarToggle() { - return (e) => { - this.forceSidebarVisible = !this.forceSidebarVisible; - } - } - - get baseUrl() { - return super.baseUrl + `user`; - } - - async fetch() { - await super.fetch(this.baseUrl, undefined, { - redirect: "error" - }).then((data) => { - this.gitlabUser = data; - }).catch((e) => { - this.gitlabUser = null; - }).finally(() => { - this.initialized = true; - }) - } - - static get styles() { - return css` - header { - height: 60px; - } - - a.gitlab-user { - --line-height: 24px; - line-height: var(--line-height); - text-decoration: none; - } - - main { - overflow-y: scroll; - } - - footer .nav-item { - flex-grow: 1; - flex-shrink: 1; - flex-basis: 0; - text-align: center; - } - `; - } - - render() { - - let view, layout = "sidebar"; - - if (!this.initialized) { - view = html` -
- ROS Logo -

Dashboard

-
-
- Loading... -
-
-
- `; - layout = "plain"; - } else if (!this.gitlabUser) { - const loginUrl = `https://login.${window.location.host.split(".").splice(1).join(".")}/users/sign_in?redirect_to=${encodeURIComponent(window.location.href)}`; - view = html` -
- ROS Logo -

Dashboard

-
-

- Welcome to Radically Open Security! Please authenticate to proceed to your project dashboard or get in touch with us to get started. -

- -
-
- `; - layout = "plain"; - } else if(this.view === "table") { - view = html` -

Projects Table

- - `; - } else if (this.gitlabProjectId === "new") { - view = html``; - } else if (this.gitlabProjectId !== null) { - view = html``; - } else { - this.pageTitle = undefined; - view = html``; - } - - const stylesheetIncludes = html` - - `; - - const header = html` - - - `; - - const footerHeight = "55px"; - const footerNavigationItemCount = Object.keys(this.availableSubroutes).length; - const footerHeightStyles = styleMap({ - height: footerHeight - }); - const footerStyles = styleMap({ - height: `calc(${footerHeight} + env(safe-area-inset-bottom))`, - maxHeight: (footerNavigationItemCount > 0) ? `calc(${footerHeight} + env(safe-area-inset-bottom))` : 0, - transition: 'max-height 0s ease-out', - transitionDelay: '0.2s', - position: 'relative', - display: 'block' - }); - const footerAnimationStyles = styleMap({ - height: footerHeight, - maxHeight: (footerNavigationItemCount > 0) ? footerHeight : 0, - transition: 'max-height 0.2s ease-out', - position: 'absolute', - right: 0, - top: 0, - left: 0 - }); - - const footer = html` -
-
- -
-
`; - - switch (layout) { - case "plain": - return html`${stylesheetIncludes} -
- ${header} -
${view}
- ${footer} -
`; - default: // sidebar - return html`${stylesheetIncludes}
- ${header} - ${view} - ${footer} -
`; - } - - } - -} -customElements.define("app-router", AuthenticatedRouter); diff --git a/elements/ui/accordion.js b/elements/ui/accordion.js deleted file mode 100644 index 869aeac..0000000 --- a/elements/ui/accordion.js +++ /dev/null @@ -1,83 +0,0 @@ -import { LitElement, html, css } from '../../web_modules/lit.js'; -import { repeat } from '../../web_modules/lit-html/directives/repeat.js'; - -class Accordion extends LitElement { - - constructor() { - super(); - this.items = []; - } - - static get properties() { - return { - items: { - type: Array, - notify: true - } - } - } - - get onClickButton() { - return (e) => { - const $button = e.target; - const $item = $button.parentElement.parentElement; - - const $content = $item.querySelector(".content"); - const $body = $content.querySelector(".accordion-body"); - - if ($button.classList.contains("collapsed")) { - $button.classList.remove("collapsed"); - $item.classList.remove("content-hidden"); - } else { - $button.classList.add("collapsed"); - $item.classList.add("content-hidden"); - } - - const $contentElement = $item.querySelector(".content > .accordion-body > *"); - if ($contentElement.onBecomeVisible && !$button.classList.contains("collapsed")) { - $contentElement.onBecomeVisible(); - } - }; - } - - static get styles() { - return css` - .content { - overflow: hidden; - transition: max-height 0.3s ease; - } - - .content iframe { - width: 100%; - } - - .content-hidden .content { - max-height: 0px !important; - } - - .accordion-button * { - pointer-events: none; - } - `; - } - - render() { - return html` - -
- ${repeat(this.items, (item) => (item.id || item), (item) => html` -
-

- -

-
-
${item.content}
-
-
- `)} -
- `; - } - -} -customElements.define("ui-accordion", Accordion); \ No newline at end of file diff --git a/elements/ui/breadcrumbs.js b/elements/ui/breadcrumbs.js deleted file mode 100644 index 67a6a0c..0000000 --- a/elements/ui/breadcrumbs.js +++ /dev/null @@ -1,27 +0,0 @@ -import { LitElement, html } from '../../web_modules/lit.js'; - -class Breadcrumbs extends LitElement { - - constructor() { - super(); - } - - render() { - return html` - - - `; - } - -} -customElements.define("ui-breadcrumbs", Breadcrumbs); \ No newline at end of file diff --git a/elements/ui/content-card.js b/elements/ui/content-card.js deleted file mode 100644 index 95c355c..0000000 --- a/elements/ui/content-card.js +++ /dev/null @@ -1,85 +0,0 @@ -import { LitElement, html, css } from '../../web_modules/lit.js'; -import { classMap } from '../../web_modules/lit-html/directives/class-map.js'; -import { styleMap } from '../../web_modules/lit-html/directives/style-map.js'; - -export class ContentCard extends LitElement { - - constructor() { - super(); - this.resize = "none"; - this.height = "auto"; - this.seamless = false; - this.cardClasses = ""; - } - - static get properties() { - return { - resize: { - type: String, - reflect: true - }, - height: { - type: String, - reflect: true - }, - seamless: { - type: Boolean - } - } - } - - get cardStyles() { - return styleMap({ - "resize": this.resize, - "height": this.height !== "auto" - }); - } - - get _cardClasses() { - return { - "mb-0": true, - "mb-sm-3": true, - "bg-body": true, - "rounded": true, - "shadow-sm": true - }; - } - - get _cardClassMap() { - const extraClasses = {}; - if (!this.seamless) { - extraClasses["p-3"] = true; - } - return classMap({ - ...extraClasses, - ...this._cardClasses - }); - } - - static get styles() { - return css` - #card { - overflow: auto; - height: auto; - } - - :host { - top: 0; - left: 0; - right: 0; - bottom: 0; - } - `; - } - - render() { - return html` - -
- -
- `; - } - -} -customElements.define("ui-content-card", ContentCard); \ No newline at end of file diff --git a/elements/ui/icon.js b/elements/ui/icon.js deleted file mode 100644 index 4af4faf..0000000 --- a/elements/ui/icon.js +++ /dev/null @@ -1,55 +0,0 @@ -import { LitElement, svg, css } from '../../web_modules/lit.js'; - -class FeatherIcon extends LitElement { - - constructor() { - super(); - this.icon = undefined; - this.width = 16; - this.height = 16; - } - - static get properties() { - return { - icon: { - type: String, - notify: true - }, - width: { - type: Number - }, - height: { - type: Number - } - } - } - - static get styles() { - return css` - .feather { - vertical-align: text-bottom; - }`; - } - - render() { - return svg` - - - - ` - } - - createRenderRoot() { - return this; - } - -} -customElements.define("ui-icon", FeatherIcon); \ No newline at end of file diff --git a/elements/ui/input/autocomplete.js b/elements/ui/input/autocomplete.js deleted file mode 100644 index 19614f4..0000000 --- a/elements/ui/input/autocomplete.js +++ /dev/null @@ -1,147 +0,0 @@ -import { LitElement, html } from '../../../web_modules/lit.js'; -import { LitNotify } from '../../../lib/lit-element-notify.js'; - -export class AutocompleteInput extends LitNotify(LitElement) { - - constructor() { - super(); - this.path = undefined; - this.value = undefined; - this.params = { - scope: "users" - }; - this._options = []; - this.search = ""; - this.suggestions = null; - } - - static get properties() { - return { - path: { - type: String - }, - params: { - type: Object - }, - _options: { - type: Object, - notify: true - }, - value: { - type: Object, - notify: true - }, - search: { - type: String, - notify: true - }, - suggestions: { - type: Array - } - } - } - - get options() { - return this._options; - } - - set options(options) { - this._options = options; - } - - get url() { - if (this.path == undefined) { - return; - } - const url = new URL(this.path, window.location.toString()); - Object.entries(this.params) - .forEach(([key, value]) => url.searchParams.append(key, value)); - url.searchParams.append("search", this.search); - return url; - } - - async queryDebounced(duration=50) { - - if (this.__queryTimeout !== undefined) { - clearTimeout(this.__queryTimeout); - } - - this.__queryTimeout = setTimeout(async () => { - this.options = await this.query(); - }, duration); - - } - - - updated(changedProperties) { - const keys = [...changedProperties.keys()]; - if (keys.includes("path") || keys.includes("params")) { - this.queryDebounced(0); - } else if (keys.includes("search")) { - this.queryDebounced(); - } - if (keys.includes("_options") && !keys.includes("value")) { - if ((this.value === undefined) && (this.options.length > 0)) { - this.value = this.options[0].value - } - } - } - - async query(url) { - if ((this.search === undefined) || !this.search.length) { - return []; - } - if (url === undefined) { - url = this.url; - } - if (url === undefined) { - return; - } else { - const response = await fetch(url.toString()); - const data = await response.json(); - - switch (response.status) { - case 401: - case 403: - alert(data.message); - break; - case 400: - alert(data.message.name); - break; - } - - this.options = data.map(this.constructor.mapOptions); - } - } - - static mapOptions(item) { - return item; - } - - get onChangeInput() { - return (e) => { - e.stopPropagation(); - this[e.currentTarget.name] = e.currentTarget.value; - }; - } - - select(option) { - return (e) => { - this.value = option; - }; - } - - render() { - return html` - - ${this.options !== null ? html` -
    - ${this.options.map((suggestion) => html` -
  • ${this.suggestion.name}
  • - `)} -
- ` : ''} - `; - } - -} \ No newline at end of file diff --git a/elements/ui/input/dropdown.js b/elements/ui/input/dropdown.js deleted file mode 100644 index c48f2a5..0000000 --- a/elements/ui/input/dropdown.js +++ /dev/null @@ -1,129 +0,0 @@ -import { LitElement, html } from '../../../web_modules/lit.js'; -import { LitNotify } from '../../../lib/lit-element-notify.js'; - -export class DropdownInput extends LitNotify(LitElement) { - - constructor() { - super(); - this.path = undefined; - this.label = undefined; - this.params = {}; - this.value = null; - this._options = []; - } - - static get properties() { - return { - path: { - type: String - }, - label: { - type: String - }, - params: { - type: Object - }, - _options: { - type: Object, - notify: true - }, - value: { - type: String, - notify: true - } - } - } - - get options() { - return this._options; - } - - set options(options) { - const oldValue = this._options; - this._options = options; - this.requestUpdate("options", oldValue); - } - - get url() { - if (this.path == undefined) { - return; - } - const url = new URL(this.path, window.location.toString()); - Object.entries(this.params).forEach(([key, value]) => url.searchParams.append(key, value)); - return url; - } - - async willUpdate(changedProperties) { - super.willUpdate(changedProperties); - if (changedProperties.has("path") || changedProperties.has("params")) { - this.options = await this.query(); - } - if (changedProperties.has("_options") && !changedProperties.has("value")) { - if ((this.value == undefined) && (this.options.length > 0)) { - this.value = this.options[0].value - } - } - } - - async query(url) { - if (url === undefined) { - url = this.url; - } - if (url === undefined) { - return; - } else { - const response = await fetch(url.toString()); - const data = await response.json(); - - switch (response.status) { - case 401: - case 403: - alert(data.message); - break; - case 400: - alert(data.message.name); - break; - } - - return data.map(this.constructor.mapOptions); - } - } - - static mapOptions(item) { - return item; - } - - get onChangeSelection() { - return (e) => { - e.stopPropagation(); - - if (this.constructor.properties.value.type === Number) { - this.value = parseInt(e.currentTarget.value, 10); - } else { - this.value = e.currentTarget.value; - } - } - } - - render() { - return html` - - - -
- - ${(this.label !== undefined) ? html`` : ''} -
- `; - } - -} \ No newline at end of file diff --git a/elements/ui/unsafe-content.js b/elements/ui/unsafe-content.js deleted file mode 100644 index 2653a71..0000000 --- a/elements/ui/unsafe-content.js +++ /dev/null @@ -1,95 +0,0 @@ -import { LitElement, html, css } from '../../web_modules/lit.js'; - -export class UnsafeContent extends LitElement { - - constructor() { - super(); - this.visible = false; - this.unsafeHTML = null; - this.baseUrl = ""; - } - - static get properties() { - return { - unsafeHTML: { - type: String, - notify: true - }, - visible: { - type: Boolean, - reflect: true - } - } - } - - get renderedUnsafeHTML() { - return this.unsafeHTML; - } - - get contentStyle() { - return; - } - - update(changedProperties) { - this.visible = !!this.unsafeHTML; - this.$iframe.srcdoc = !this.visible ? '' : ` - - - - ${this.contentStyle} - - - ${this.renderedUnsafeHTML} - - - `; - } - - updateHeight = () => { - if (!this.visible) { - this.$iframe.style.height = 0; - } else { - this.$iframe.style.height = this.$iframe.contentDocument.documentElement.offsetHeight + "px"; - } - } - - createRenderRoot() { - return this; - } - - get $iframe() { - if (!this._$iframe) { - const $iframe = document.createElement("iframe"); - $iframe.style.boxSizing = "content-box"; - $iframe.style.width = "100%"; - $iframe.style.border = "none"; - $iframe.setAttribute("sandbox", "allow-same-origin"); - this._$iframe = $iframe; - } - return this._$iframe; - } - - get onResize() { - if (!this._onResize) { - this._onResize = () => { - setTimeout(() => this.updateHeight(), 0); - } - } - return this._onResize; - } - - connectedCallback() { - super.connectedCallback(); - this.renderRoot.appendChild(this.$iframe); - this.$iframe.addEventListener("load", this.onResize); - window.addEventListener("resize", this.onResize); - } - - disconnectedCallback() { - super.disconnectedCallback(); - this.$iframe.removeEventListener("load", this.onResize); - window.removeEventListener("resize", this.onResize); - } - -} -customElements.define("ui-unsafe-content", UnsafeContent); \ No newline at end of file diff --git a/elements/utils/generate-password.js b/elements/utils/generate-password.js deleted file mode 100644 index 4ed0b44..0000000 --- a/elements/utils/generate-password.js +++ /dev/null @@ -1,33 +0,0 @@ -const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -const NUMBERS = "0123456789"; -// not all special characters are supported in GL when masking -// https://git.staging.radical.sexy/help/ci/variables/index#mask-a-cicd-variable -const SYMBOLS = "@:.~-+/="; - -const PASSWORD_CHARACTERS = [ - ALPHABET.toUpperCase(), - ALPHABET.toLowerCase(), - NUMBERS, - SYMBOLS, -].join(); - -/** - * Generate password - * @param {number} length - * @returns string - */ -export const generatePassword = (length = 20, characters = PASSWORD_CHARACTERS) => { - let password = ""; - - // Monte Carlo - while (password.length < length) { - const array = new Uint8Array(1); - const [randomInt] = crypto.getRandomValues(array); - const char = characters[randomInt]; - if (char) { - password += char; - } - } - - return password; -}; diff --git a/elements/views/sidebar.js b/elements/views/sidebar.js deleted file mode 100644 index c5e0978..0000000 --- a/elements/views/sidebar.js +++ /dev/null @@ -1,263 +0,0 @@ -import moment from '../../web_modules/moment.js'; -import { LitElement, html, css } from '../../web_modules/lit.js'; -import { LitNotify } from '../../lib/lit-element-notify.js'; -import "../ros/projects.js"; - -const matchSearch = (text, search) => { - if (!search || !search.length) { - return true; - } - - text = text.toLowerCase(); - search = search.toLowerCase(); - - for (let t=0, s=0; (t < text.length); t++) { - if (text[t] === search[s]) { - s++; - } - if (s === search.length) { - // entire text found - return true; - } - } - return false; -}; - -class SidebarView extends LitNotify(LitElement) { - - constructor() { - super(); - this.projects = []; - this.search = ""; - this.forceSidebarVisible = false; - this.selectedProjectId = null; - } - - static get styles() { - return css` - .small { - font-size: 0.75em; - } - - main { - overflow-y: scroll; - overflow-x: hidden; - } - - nav { - overflow-y: scroll; - } - - #sidebar { - position: absolute; - z-index: 999; - width: 350px; - min-width: 350px; - max-width: 350px; - } - - #sidebar .avatar { - width: 32px; - height: 32px; - } - #sidebar .mentions { - height: 32px; - } - - #sidebar .nav-link { - padding: 0.5rem; - } - - #sidebar .nav-item[active=true] .nav-link { - color: var(--ros-orange); - } - - #sidebar .nav-item[active=true] .nav-link span { - text-decoration: underline; - } - - #sidebar .nav-item[unread=true] .nav-link span { - font-weight: bold; - } - - #sidebar form { - margin-block-end: 0.5em; - } - - @media (max-width: 575px) { - #sidebar { - width: 100%; - min-width: 100%; - max-width: 100%; - } - } - - @media (min-width: 576px) and (max-width: 991px) { - #sidebar { - width: auto; - } - } - - @media (min-width: 992px) { - #sidebar{ - position: relative !important; - } - } - `; - } - - static get properties() { - return { - ... super.properties, - search: { - type: String - }, - projects: { - type: Array, - notify: false - }, - selectedProjectId: { - type: Number, - reflect: true - }, - forceSidebarVisible: { - type: Boolean, - notify: false, - reflect: true - } - } - } - - updated(changedProperties) { - super.updated(changedProperties); - if (changedProperties.has("forceSidebarVisible")) { - if (this.forceSidebarVisible === true) { - this.shadowRoot.getElementById("sidebar").classList.add("show"); - } else { - this.shadowRoot.getElementById("sidebar").classList.remove("show"); - } - } - } - - get onSearch() { - return (e) => { - this.search = e.target.value; - e.preventDefault(); - e.stopPropagation(); - }; - } - - static getAvatarUrl(project) { - return project.avatar_url || project.namespace.avatar_url; - } - - get filteredProjects() { - return this.projects - .filter((project) => { - // one of this slugs must match to pass the filter - const projectSlugs = [ - `${project.namespace.name} / ${project.name}`, - `${project.namespace.path} / ${project.name}`, - `${project.namespace.name} / ${project.path}`, - `${project.namespace.path} / ${project.path}` - ]; - for (let slug of projectSlugs) { - if (matchSearch(slug, this.search)) { - return true; - } - } - return false; - }); - } - - render() { - return html` - - - -
-
- - -
-
- -
-
-
-
- `; - } - -} -customElements.define("sidebar-view", SidebarView); diff --git a/favicon.ico b/favicon.ico deleted file mode 120000 index 45399c8..0000000 --- a/favicon.ico +++ /dev/null @@ -1 +0,0 @@ -images/favicon.ico \ No newline at end of file diff --git a/flexboxgrid.css b/flexboxgrid.css deleted file mode 120000 index 3a396e6..0000000 --- a/flexboxgrid.css +++ /dev/null @@ -1 +0,0 @@ -node_modules/flexboxgrid/dist/flexboxgrid.min.css \ No newline at end of file diff --git a/images/.DS_Store b/images/.DS_Store deleted file mode 100644 index 611e579..0000000 Binary files a/images/.DS_Store and /dev/null differ diff --git a/index.html b/index.html index e67afb4..9b61da4 100644 --- a/index.html +++ b/index.html @@ -1,21 +1,16 @@ - + + - R♡S _board - - - - - - - - - - - - - + + + + R♡S Dashboard + + - - + + + + diff --git a/lib/lit-element-notify.js b/lib/lit-element-notify.js deleted file mode 100644 index 2bdccd8..0000000 --- a/lib/lit-element-notify.js +++ /dev/null @@ -1,138 +0,0 @@ -/* -MIT License by https://github.com/morbidick -see https://github.com/morbidick/lit-element-notify/blob/master/package.json - -Modified by Stefan Grönke -*/ - -/* - * Returns the event name for the given property. - * @param {string} name property name - * @param {PropertyDeclaration} options property declaration - * @return event name to fire - */ -function eventNameForProperty(name, { notify, attribute } = {}) { - if (notify && typeof notify === 'string') { - return notify; - } else if (attribute && typeof attribute === 'string') { - return `${attribute}-changed`; - } else { - return `${name.toLowerCase()}-changed`; - } -} - -// eslint-disable-next-line valid-jsdoc -/** - * Enables the nofity option for properties to fire change notification events - * - * @template TBase - * @param {Constructor} baseElement - */ -const LitNotify = (baseElement) => class NotifyingElement extends baseElement { - /** - * check for changed properties with notify option and fire the events - */ - update(changedProps) { - super.update(changedProps); - for (const prop of changedProps.keys()) { - const declaration = this.constructor.properties[prop]; - if (!declaration || !declaration.notify) continue; - const type = eventNameForProperty(prop, declaration); - const value = this[prop]; - this.dispatchEvent(new CustomEvent(type, { detail: { value } })); - } - } -}; - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const t={ATTRIBUTE:1,CHILD:2,PROPERTY:3,BOOLEAN_ATTRIBUTE:4,EVENT:5,ELEMENT:6},i=t=>(...i)=>({_$litDirective$:t,values:i});class s{constructor(t){}T(t,i,s){this.Σdt=t,this.M=i,this.Σct=s;}S(t,i){return this.update(t,i)}update(t,i){return this.render(...i)}} - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var t$1,i$1,s$1,e;const o=globalThis.trustedTypes,l=o?o.createPolicy("lit-html",{createHTML:t=>t}):void 0,n=`lit$${(Math.random()+"").slice(9)}$`,h="?"+n,r=`<${h}>`,u=document,c=(t="")=>u.createComment(t),d=t=>null===t||"object"!=typeof t&&"function"!=typeof t,v=Array.isArray,a=t=>{var i;return v(t)||"function"==typeof(null===(i=t)||void 0===i?void 0:i[Symbol.iterator])},f=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,_=/-->/g,m=/>/g,p=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,$=/'/g,g=/"/g,y=/^(?:script|style|textarea)$/i,w=Symbol.for("lit-noChange"),A=Symbol.for("lit-nothing"),P=new WeakMap,E=u.createTreeWalker(u,129,null,!1),M=(t,i)=>{const s=t.length-1,e=[];let o,h=2===i?"":"",u=f;for(let i=0;i"===c[0]?(u=null!=o?o:f,d=-1):void 0===c[1]?d=-2:(d=u.lastIndex-c[2].length,l=c[1],u=void 0===c[3]?p:'"'===c[3]?g:$):u===g||u===$?u=p:u===_||u===m?u=f:(u=p,o=void 0);const a=u===p&&t[i+1].startsWith("/>")?" ":"";h+=u===f?s+r:d>=0?(e.push(l),s.slice(0,d)+"$lit$"+s.slice(d)+n+a):s+n+(-2===d?(e.push(void 0),i):a);}const c=h+(t[s]||"")+(2===i?"":"");return [void 0!==l?l.createHTML(c):c,e]};class N{constructor({strings:t,_$litType$:i},s){let e;this.parts=[];let l=0,r=0;const u=t.length-1,d=this.parts,[v,a]=M(t,i);if(this.el=N.createElement(v,s),E.currentNode=this.el.content,2===i){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(e=E.nextNode())&&d.length0){e.textContent=o?o.emptyScript:"";for(let s=0;s2||""!==s[0]||""!==s[1]?(this.H=Array(s.length-1).fill(A),this.strings=s):this.H=A;}get tagName(){return this.element.tagName}I(t,i=this,s,e){const o=this.strings;let l=!1;if(void 0===o)t=S(this,t,i,0),l=!d(t)||t!==this.H&&t!==w,l&&(this.H=t);else {const e=t;let n,h;for(t=o[0],n=0;nvoid 0===o.strings; - -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const r$1=(i,t)=>{var s,e;const o=i.N;if(void 0===o)return !1;for(const i of o)null===(e=(s=i).O)||void 0===e||e.call(s,t,!1),r$1(i,t);return !0},o$1=i=>{let t,s;do{if(void 0===(t=i.M))break;s=t.N,s.delete(i),i=t;}while(0===(null==s?void 0:s.size))},h$1=i=>{for(let t;t=i.M;i=t){let s=t.N;if(void 0===s)t.N=s=new Set;else if(s.has(i))break;s.add(i),d$2(t);}};function n$1(i){void 0!==this.N?(o$1(this),this.M=i,h$1(this)):this.M=i;}function l$1(i,t=!1,s=0){const e=this.H,h=this.N;if(void 0!==h&&0!==h.size)if(t)if(Array.isArray(e))for(let i=s;i{var t$1,e,r,o;i.type==t.CHILD&&(null!==(t$1=(r=i).P)&&void 0!==t$1||(r.P=l$1),null!==(e=(o=i).Q)&&void 0!==e||(o.Q=n$1));};class c$1 extends s{constructor(){super(...arguments),this.isConnected=!0,this.ut=w,this.N=void 0;}T(i,t,s){super.T(i,t,s),h$1(this);}O(i,t=!0){this.at(i),t&&(r$1(this,i),o$1(this));}at(t){var s,e;t!==this.isConnected&&(t?(this.isConnected=!0,this.ut!==w&&(this.setValue(this.ut),this.ut=w),null===(s=this.reconnected)||void 0===s||s.call(this)):(this.isConnected=!1,null===(e=this.disconnected)||void 0===e||e.call(this)));}S(i,t){if(!this.isConnected)throw Error(`AsyncDirective ${this.constructor.name} was rendered while its tree was disconnected.`);return super.S(i,t)}setValue(i){if(this.isConnected)if(d$1(this.Σdt))this.Σdt.I(i,this);else {const t=[...this.Σdt.H];t[this.Σct]=i,this.Σdt.I(t,this,0);}else this.ut=i;}disconnected(){}reconnected(){}} - -class LitSyncDirective extends c$1 { - get property() { - throw new Error("not implemented"); - } - constructor(part) { - super(); - const eventName = eventNameForProperty(part.name); - - //const notifyingElement = part.options.host; - this.notifyingElement = part.element; - this.notifiedElement = part.options.host; - this.notifyingProperty = part.name; - - let syncInitialized = false; - - this.notifyingElement.addEventListener(eventName, (e) => { - - if (!syncInitialized) { - syncInitialized = true; - return; - } - - const oldValue = this.notifiedElement[this.property]; - this.notifiedElement[this.property] = e.detail.value; - if (this.notifiedElement.__lookupSetter__(this.property) === undefined) { - this.notifiedElement.updated(new Map([[this.property, oldValue]])); - } - }); - } - update(part) { - part.element[part.name] = this.notifiedElement[this.property]; - return this.render(); - } - render() { - return this.notifiedElement[this.property]; - } -} - -// eslint-disable-next-line valid-jsdoc -/** - * Mixin that provides a lit-html directive to sync a property to a child property - * - * @template TBase - * @param {Constructor} baseElement - */ -const LitSync = (baseElement) => class extends baseElement { - constructor() { - super(); - - /** - * lit-html directive to sync a property to a child property - * - * @param {string} property - The property name - */ - this.sync = (property, foo) => { - class D extends LitSyncDirective { - get property() { - return property; - } - } - return i(D)(); - }; - } -}; - -export { LitNotify, LitSync }; diff --git a/lib/marked.js b/lib/marked.js deleted file mode 100644 index 7f6f9d3..0000000 --- a/lib/marked.js +++ /dev/null @@ -1,43 +0,0 @@ -import marked from '../web_modules/marked.js'; - -// tweak marked renderer -const headingLevelOffset = 2; -marked.use({ - renderer: { - heading(text, level) { - level += headingLevelOffset; - return `${text}`; - }, - code(code, infostring, escaped) { - const lang = (infostring || '').match(/\S*/)[0]; - if (this.options.highlight) { - const out = this.options.highlight(code, lang); - if (out != null && out !== code) { - escaped = true; - code = out; - } - } - - code = code.replace(/\n$/, '') + '\n'; - - const $pre = document.createElement("pre"); - const $code = document.createElement("code"); - $pre.appendChild($code); - $pre.classList.add("bg-light", "p-3"); - - if (escaped) { - $code.innerHTML = code; - } else { - $code.innerText = code; - } - - if (lang) { - $code.classList.add(`${this.options.langPrefix}${escape(lang, true)}`); - } - - return $pre.outerHTML; - } - } -}); - -export default marked; \ No newline at end of file diff --git a/mock/data/discussions.mock.ts b/mock/data/discussions.mock.ts new file mode 100644 index 0000000..7c8dc3b --- /dev/null +++ b/mock/data/discussions.mock.ts @@ -0,0 +1,147 @@ +export const discussionsMock = [ + [ + { + notes: [ + { + body: 'Some source code from our website:\n\n```html\n\u003chead\u003e\n \u003cmeta charset="utf-8"\u003e\n \u003cmeta name="viewport" content="width=device-width initial-scale=1" /\u003e\n \u003cmeta http-equiv="X-UA-Compatible" content="IE=edge"\u003e\n\n \n\n \u003ctitle\u003eNon-Profit Computer Security Consultancy\u003c/title\u003e\n \u003cmeta name="description" content="We\'re an idealistic bunch of security researchers, networking/forensics geeks, and Capture The Flag winners that are passionate about making the world more secure."\u003e\n\n \u003clink rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon"\u003e\n\n \u003clink rel="stylesheet" href="/assets/css/main.css"\u003e\n\n \u003clink rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"\u003e\n \u003clink rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"\u003e\n \u003clink rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"\u003e\n \u003clink rel="manifest" href="/site.webmanifest"\u003e\n \u003clink rel="mask-icon" href="/safari-pinned-tab.svg" color="#db6a26"\u003e\n \u003cmeta name="msapplication-TileColor" content="#db6a26"\u003e\n \u003cmeta name="theme-color" content="#db6a26"\u003e\n\n \u003cmeta property="og:url" content="https://radicallyopensecurity.com"\u003e\n \u003cmeta property="og:type" content="website"\u003e\n \u003cmeta property="og:title" content="Non-Profit Computer Security Consultancy"\u003e\n \u003cmeta property="og:description" content="We\'re an idealistic bunch of security researchers, networking/forensics geeks, and Capture The Flag winners that are passionate about making the world more secure."\u003e\n \u003cmeta property="og:image" content="https://radicallyopensecurity.com/assets/images/ros-og-image.png"\u003e\n\n \u003cmeta name="twitter:card" content="https://radicallyopensecurity.com/assets/images/ros-twitter-card.png"\u003e\n \u003cmeta property="twitter:domain" content="radicallyopensecurity.com"\u003e\n \u003cmeta property="twitter:url" content="https://radicallyopensecurity.com"\u003e\n \u003cmeta name="twitter:title" content="Non-Profit Computer Security Consultancy"\u003e\n \u003cmeta name="twitter:description" content="We\'re an idealistic bunch of security researchers, networking/forensics geeks, and Capture The Flag winners that are passionate about making the world more secure."\u003e\n \u003cmeta name="twitter:image" content="https://radicallyopensecurity.com/assets/images/ros-twitter-summary.png"\u003e\n\n \u003cmeta http-equiv="Content-Security-Policy" content="script-src \'self\'"\u003e\n\u003c/head\u003e\n```', + }, + ], + }, + ], + [ + { + notes: [ + { + body: 'Type\n\nXSS', + }, + ], + }, + { + notes: [ + { + body: "Technical Description \n\nThe XSS vulnerability was discovered in the user input field of the application's login page. Our team was able to inject a script that could steal user login credentials and transmit them to a remote server controlled by the attacker. We were also able to inject other scripts that could execute arbitrary code on the user's browser and redirect them to a malicious website.\n\n![account_self_xss-2](/uploads/54c86d8ea813d8d0b2b317c435284d89/account_self_xss-2.png)", + }, + ], + }, + { + notes: [ + { + body: 'Impact\n\nIf left unaddressed, this XSS vulnerability could have severe consequences for the organization and its users. An attacker could steal sensitive user data such as login credentials, credit card information, and personal identifiable information. This could result in financial loss, reputational damage, and legal ramifications for the organization.', + }, + ], + }, + { + notes: [ + { + body: "Recommendation\n\nOur team recommends immediate remediation of this XSS vulnerability. This can be achieved by implementing proper input validation and sanitization techniques on the user input fields. We also recommend implementing a Content Security Policy (CSP) to prevent the execution of malicious scripts on the user's browser. Regular security assessments should be conducted to ensure the application's security posture is maintained over time.", + }, + ], + }, + ], + [ + { + notes: [ + { + body: "The SQL injection attack on the database was identified through logs and network monitoring tools, which detected the use of malicious SQL commands in user input fields.\n\n`$username = $_POST['username'];`\n`$password = $_POST['password'];`\n\n// Connect to database\n`$conn = mysqli_connect('localhost', 'db_username', 'db_password', 'db_name');`\n\n// Create SQL query\n`$query = \"SELECT * FROM users WHERE username = '$username' AND password = '$password'\";`\n\n// Execute query\n`$result = mysqli_query($conn, $query);`\n\n// Check if user exists\n`if(mysqli_num_rows($result) \u003e 0){`\n // User exists\n // Login user\n`}else{`\n // User does not exist\n // Show error message\n`}`\n\n// Close connection\n`mysqli_close($conn);`\n\n\u003e In this example, the values of $username and $password are directly inserted into the SQL query without any validation or sanitization. This makes the code vulnerable to SQL injection attacks where the attacker can manipulate the input fields to inject malicious SQL code into the query, allowing them to gain unauthorized access to the database. To prevent this, the code should use parameterized queries or input validation mechanisms to ensure that only safe and expected values are passed to the database.\n\n![image](/uploads/e711692915783d425e07a4ade5a0d988/image.png)", + }, + ], + }, + { + notes: [ + { + body: "impact\n\nData Breaches: SQL injection attacks can allow attackers to access and extract sensitive data stored in the database. This can include customer information such as names, addresses, email addresses, and credit card details, as well as employee data such as social security numbers and payroll information.\n\nFinancial Losses: In addition to the costs associated with remediation and legal fees, companies may also face financial losses due to the theft of sensitive data. This can result in decreased revenue, loss of customers, and damage to the company's reputation.\n\nLegal Consequences: Depending on the nature of the data that was compromised, organizations may face legal consequences for failing to adequately protect sensitive information. This can result in fines, lawsuits, and other legal penalties.", + }, + ], + }, + { + notes: [ + { + body: 'recommendation\n\nUse Prepared Statements or Parameterized Queries: One of the most effective ways to prevent SQL injection attacks is to use prepared statements or parameterized queries instead of concatenating user input directly into SQL statements. Prepared statements allow you to separate the SQL code from the user input, ensuring that malicious code cannot be injected into the SQL statement. This approach can be used with a variety of programming languages and database systems.\n\nImplement Input Validation and Sanitization: Another way to prevent SQL injection attacks is to implement input validation and sanitization. This involves checking user input to ensure that it conforms to expected formats and types, as well as removing any potentially malicious characters or commands. This can be done using a variety of techniques, including regular expressions, input masks, and whitelist filtering.', + }, + ], + }, + { + notes: [ + { + body: 'type\n\nSQL injection vulnerability', + }, + ], + }, + ], + [ + { + notes: [ + { + body: 'The attack was detected through abnormal network activity, which indicated that an unauthorized third party was intercepting and manipulating network traffic.', + }, + ], + }, + ], + [ + { + notes: [ + { + body: 'Type\n\nInformation Leak', + }, + ], + }, + { + notes: [ + { + body: 'Technical Description\n\ndwqdwqddwq\n\u003eEW\ndwqdqdwq\ndwqdwqdwq', + }, + ], + }, + { + notes: [ + { + body: 'Impact\n\ndwqdwdwdwqdwqdwq', + }, + ], + }, + { + notes: [ + { + body: 'Recommendation\n\ndwdwqdwqdwq', + }, + ], + }, + { + notes: [ + { + body: 'Update\n\nThis finding was re-tested and is solved now!', + }, + ], + }, + ], + [ + { + notes: [ + { + body: 'type\n\nflooding', + }, + ], + }, + { + notes: [ + { + body: 'impact\n\nWhere possible to enter a chat or group conversation, flooding it with content could make communication impossible as well as overwhelm the network with data.', + }, + ], + }, + { + notes: [ + { + body: 'recommendation\n\nWe recommend to ensure throttling or proof-of-work is in place for individual users to make sure individuals cannot flood the network with arbitrary data.', + }, + ], + }, + { + notes: [ + { + body: 'technical description\n\nWe managed to flood a group channel as a proof of concept using expect.\n\nRun the following expect script on a VPS with a connected GNUnet instance with gnunet-messenger to very easily flood a (group) chat channel.\n\n```expect\nexpect flooding.exp\n```\n\nflooding.exp\n\n```plaintext\n#!/usr/bin/expect -f\nset timeout -1\nspawn gnunet-messenger -d 4CA361EJ98F43FWT1S6KQZPYWSHNVFB2PKRZAGQFFZKXVXVGXXNG -r 4CA361EJ98F43FWT1S6KQZPYWSHNVFB2PKRZAGQFFZKXVXVGXXNG -e \'PENTEST\'\nmatch_max 100000\nexpect -exact "* You try to entry a room...\\r\\n* You joined the room.\\r\\n"\nsleep 0.5\nset i 1\nwhile {$i\u003c10000} {\n send -- "$i:FLOOD_FLOOD_FLOOD_FLOOD_FLOOD_\\r"\n sleep 0.01\n expect -exact "$i:FLOOD_FLOOD_FLOOD_FLOOD_FLOOD_\\r"\n incr i\n}\nsleep 0.5\nsend -- "^C"\nexpect eof\n```', + }, + ], + }, + ], +] diff --git a/mock/data/events-base.mock.ts b/mock/data/events-base.mock.ts new file mode 100644 index 0000000..806ebba --- /dev/null +++ b/mock/data/events-base.mock.ts @@ -0,0 +1,9 @@ +export const EVENTS_BASE = { + project_id: 1, + target_id: 1, + target_iid: 1, + target_type: 'Issue', + author_id: 1, + imported: false, + imported_from: 'none', +} diff --git a/mock/data/events.mock.ts b/mock/data/events.mock.ts new file mode 100644 index 0000000..2d0adb7 --- /dev/null +++ b/mock/data/events.mock.ts @@ -0,0 +1,46 @@ +import { generateEventDetails } from '../utils' + +import { EVENTS_BASE } from './events-base.mock' + +export const eventsMock = [ + { + ...EVENTS_BASE, + ...generateEventDetails( + 1, + 'opened', + 'FINDING', + 'user-1', + '2024-04-28T23:54:52.838Z' + ), + }, + { + ...EVENTS_BASE, + ...generateEventDetails( + 2, + 'opened', + 'FINDING', + 'user-2', + '2024-03-28T23:54:52.838Z' + ), + }, + { + ...EVENTS_BASE, + ...generateEventDetails( + 3, + 'joined', + 'FINDING', + 'user-2', + '2024-03-24T23:54:52.838Z' + ), + }, + { + ...EVENTS_BASE, + ...generateEventDetails( + 4, + 'created', + 'FINDING', + 'user-2', + '2024-03-22T23:54:52.838Z' + ), + }, +] diff --git a/mock/data/groups-base.mock.ts b/mock/data/groups-base.mock.ts new file mode 100644 index 0000000..50a8a69 --- /dev/null +++ b/mock/data/groups-base.mock.ts @@ -0,0 +1,52 @@ +import { capitalize } from '@/utils/string/capitalize' + +const GROUPS_BASE = { + id: 1, + description: '', + visibility: 'private', + share_with_group_lock: false, + require_two_factor_authentication: false, + two_factor_grace_period: 48, + project_creation_level: 'developer', + auto_devops_enabled: null, + subgroup_creation_level: 'maintainer', + emails_disabled: false, + emails_enabled: true, + mentions_disabled: null, + lfs_enabled: false, + math_rendering_limits_enabled: true, + lock_math_rendering_limits_enabled: false, + default_branch: null, + default_branch_protection: 2, + default_branch_protection_defaults: { + allowed_to_push: [ + { + access_level: 30, + }, + ], + allow_force_push: true, + allowed_to_merge: [ + { + access_level: 30, + }, + ], + }, + avatar_url: null, + request_access_enabled: true, + created_at: '2023-06-07T10:08:53.593Z', + parent_id: null, + organization_id: 1, + shared_runners_setting: 'enabled', +} + +export const buildGroup = (id: number, name: string) => { + return { + ...GROUPS_BASE, + id, + web_url: `https://localhost/git/groups/${name}`, + name: capitalize(name), + path: name, + full_name: capitalize(name), + full_path: name, + } +} diff --git a/mock/data/groups.mock.ts b/mock/data/groups.mock.ts new file mode 100644 index 0000000..14e10dd --- /dev/null +++ b/mock/data/groups.mock.ts @@ -0,0 +1,8 @@ +import { buildGroup } from './groups-base.mock' + +export const groupsMock = [ + buildGroup(1, 'namespace-1'), + buildGroup(2, 'namespace-2'), + buildGroup(3, 'namespace-3'), + buildGroup(4, 'namespace-4'), +] diff --git a/mock/data/issues-base.mock.ts b/mock/data/issues-base.mock.ts new file mode 100644 index 0000000..86b3f58 --- /dev/null +++ b/mock/data/issues-base.mock.ts @@ -0,0 +1,83 @@ +export const ISSUE_BASE = { + project_id: 1, + state: 'opened', + closed_at: null, + closed_by: null, + milestone: null, + assignees: [], + author: { + id: 1, + username: 'user-1', + name: 'user-1', + state: 'active', + locked: false, + avatar_url: null, + web_url: 'https://localhost/git/user-1', + }, + type: 'ISSUE', + assignee: null, + user_notes_count: 0, + merge_requests_count: 0, + upvotes: 0, + downvotes: 0, + due_date: null, + confidential: false, + discussion_locked: null, + issue_type: 'issue', + web_url: 'https://localhost/git/user-1/project-1/-/issues/1', + time_stats: { + time_estimate: 0, + total_time_spent: 0, + human_time_estimate: null, + human_total_time_spent: null, + }, + task_completion_status: { + count: 0, + completed_count: 0, + }, + has_tasks: true, + task_status: '0 of 0 checklist items completed', + references: { + short: '#1', + relative: '#1', + full: 'user-1/project-1#9', + }, + severity: 'UNKNOWN', + moved_to_id: null, + imported: false, + imported_from: 'none', + service_desk_reply_to: null, +} + +export const buildIssue = ( + id: number, + title: string, + description: string, + date: string, + label: + | 'non-finding' + | 'finding' + | 'Unknown' + | 'Low' + | 'Moderate' + | 'High' + | 'Elevated' + | 'Extreme' + | 'other' +) => { + return { + ...ISSUE_BASE, + id, + iid: id, + title, + description, + created_at: date, + updated_at: date, + labels: + label === 'non-finding' + ? ['non-finding'] + : label === 'finding' + ? ['finding'] + : ['finding', `threatLevel:${label}`], + } +} diff --git a/mock/data/issues.mock.ts b/mock/data/issues.mock.ts new file mode 100644 index 0000000..b221b89 --- /dev/null +++ b/mock/data/issues.mock.ts @@ -0,0 +1,60 @@ +import { buildIssue } from './issues-base.mock' + +export const issuesMock = [ + buildIssue( + 1, + 'Man-in-the-middle attack', + `The organization's network was found to be compromised by a man-in-the-middle attack, allowing the attacker to intercept and modify network traffic between two endpoints.\n\n![image](/uploads/673aefc37c61cf082820cb6f17acbbb6/image.png)`, + '2024-05-31T15:57:44.513Z', + 'Moderate' + ), + buildIssue( + 2, + 'SQL Injection', + 'The contact form is vulnerable to SQL injection and as a result can retrieve sensitive information from the DB.', + '2024-04-28T23:54:52.792Z', + 'High' + ), + buildIssue( + 3, + 'Reusing Cookie', + 'Penetration testers have identified several findings related to the reuse of cookies. These findings highlight the potential security risks associated with the reuse of cookies on websites and applications.', + '2024-04-28T23:54:52.792Z', + 'non-finding' + ), + buildIssue( + 4, + 'Info Leak - LOW', + 'Info Leak', + '2024-04-28T23:54:52.792Z', + 'finding' + ), + buildIssue( + 5, + 'libgnunetchat: Chats and/or group channels can be flooded', + 'It is possible to flood chats and (group) channels using a dedicated controller script, or custom C program.', + '2024-04-28T23:54:52.792Z', + 'Low' + ), + buildIssue( + 6, + 'Hello Redacted', + 'Just showing something to @pentext', + '2024-04-28T23:54:52.792Z', + 'finding' + ), + buildIssue( + 7, + `XSS vulnerability in the application's user input field`, + `Our pen testing team conducted an assessment on the web application hosted by XYZ Corp.\n\nDuring the assessment, we discovered an XSS vulnerability in the application's user input field. This vulnerability could allow an attacker to execute malicious code and steal sensitive user data.\n\nWe strongly recommend immediate remediation of this issue to prevent any potential exploitation.`, + '2024-04-28T23:54:52.792Z', + 'Extreme' + ), + buildIssue( + 8, + 'DDOS protection', + 'In the future you could do xyz', + '2024-04-28T23:54:52.792Z', + 'other' + ), +] diff --git a/mock/data/jobs-base.mock.ts b/mock/data/jobs-base.mock.ts new file mode 100644 index 0000000..465c43c --- /dev/null +++ b/mock/data/jobs-base.mock.ts @@ -0,0 +1,91 @@ +const JOBS_MOCK = { + stage: 'build', + name: 'build', + ref: 'main', + tag: false, + coverage: null, + allow_failure: false, + created_at: '2024-06-05T10:51:30.005Z', + started_at: '2024-06-05T10:51:39.352Z', + finished_at: '2024-06-05T10:51:49.722Z', + erased_at: null, + duration: 10.370452, + queued_duration: 2.502081, + user: { + id: 1, + username: 'user-1', + name: 'user-1', + state: 'active', + locked: false, + avatar_url: null, + web_url: 'https://localhost/git/user-1', + created_at: '2023-06-02T04:53:59.767Z', + }, + commit: { + id: 'fasdfafawef', + short_id: 'fawefawef', + created_at: '2024-06-04T15:05:12.000+00:00', + parent_ids: ['fasfasf'], + title: 'fasdfasf', + message: 'fasfasf\n', + author_name: 'GitLab CI', + author_email: 'fasdfa@fasdfa.com', + authored_date: '2024-06-04T15:05:12.000+00:00', + committer_name: 'GitLab CI', + committer_email: 'fasfa@fasdfa.com', + committed_date: '2024-06-04T15:05:12.000+00:00', + trailers: {}, + extended_trailers: {}, + web_url: 'https://localhost/git/namespace-1/project-1/-/commit/fasdfasf', + }, + web_url: 'https://localhost/git/namespace-1/project-1/-/jobs/1', + project: { + ci_job_token_scope_enabled: false, + }, + artifacts: [ + { + file_type: 'archive', + size: 121, + filename: 'artifacts.zip', + file_format: 'zip', + }, + { + file_type: 'metadata', + size: 273, + filename: 'metadata.gz', + file_format: 'gzip', + }, + { + file_type: 'trace', + size: 9630, + filename: 'job.log', + file_format: null, + }, + ], + runner: { + id: 31, + description: 'fasfasdf', + ip_address: null, + active: true, + paused: false, + is_shared: false, + runner_type: 'group_type', + name: 'gitlab-runner', + online: true, + status: 'online', + }, + artifacts_expire_at: '2024-06-05T11:51:49.121Z', + archived: false, + tag_list: ['fasfasf'], +} + +export const buildJob = ( + id: number, + status: 'success' | 'pending' | 'running' +) => { + return { + ...JOBS_MOCK, + id, + status, + } +} diff --git a/mock/data/jobs.mock.ts b/mock/data/jobs.mock.ts new file mode 100644 index 0000000..fc84ff1 --- /dev/null +++ b/mock/data/jobs.mock.ts @@ -0,0 +1,8 @@ +import { buildJob } from './jobs-base.mock' + +export const jobsMock = [ + buildJob(1, 'success'), + buildJob(2, 'success'), + buildJob(3, 'running'), + buildJob(4, 'pending'), +] diff --git a/mock/data/labels.mock.ts b/mock/data/labels.mock.ts new file mode 100644 index 0000000..3a7279f --- /dev/null +++ b/mock/data/labels.mock.ts @@ -0,0 +1,90 @@ +export const labelsMock = [ + { + id: 1, + name: 'finding', + description: null, + description_html: '', + text_color: '#FFFFFF', + color: '#009966', + subscribed: false, + priority: null, + is_project_label: true, + }, + { + id: 2, + name: 'non-finding', + description: null, + description_html: '', + text_color: '#FFFFFF', + color: '#6699cc', + subscribed: false, + priority: null, + is_project_label: true, + }, + { + id: 3, + name: 'threatLevel:Elevated', + description: null, + description_html: '', + text_color: '#FFFFFF', + color: '#6699cc', + subscribed: false, + priority: null, + is_project_label: true, + }, + { + id: 4, + name: 'threatLevel:Extreme', + description: null, + description_html: '', + text_color: '#FFFFFF', + color: '#6699cc', + subscribed: false, + priority: null, + is_project_label: true, + }, + { + id: 5, + name: 'threatLevel:High', + description: null, + description_html: '', + text_color: '#FFFFFF', + color: '#6699cc', + subscribed: false, + priority: null, + is_project_label: true, + }, + { + id: 6, + name: 'threatLevel:Low', + description: null, + description_html: '', + text_color: '#FFFFFF', + color: '#6699cc', + subscribed: false, + priority: null, + is_project_label: true, + }, + { + id: 7, + name: 'threatLevel:Moderate', + description: null, + description_html: '', + text_color: '#FFFFFF', + color: '#6699cc', + subscribed: false, + priority: null, + is_project_label: true, + }, + { + id: 8, + name: 'threatLevel:Unknown', + description: null, + description_html: '', + text_color: '#FFFFFF', + color: '#6699cc', + subscribed: false, + priority: null, + is_project_label: true, + }, +] diff --git a/mock/data/members-base.mock.ts b/mock/data/members-base.mock.ts new file mode 100644 index 0000000..bdf0261 --- /dev/null +++ b/mock/data/members-base.mock.ts @@ -0,0 +1,27 @@ +const MEMBERS_BASE = { + id: 1, + state: 'active', + locked: false, + avatar_url: null, + access_level: 50, + created_at: '2023-10-05T22:37:19.422Z', + created_by: { + id: 1, + username: 'user-1', + name: 'User 1', + state: 'active', + locked: false, + avatar_url: null, + web_url: 'https://localhost/git/user-1', + }, + expires_at: null, +} + +export const buildMember = (name: string) => { + return { + ...MEMBERS_BASE, + username: name, + name: name, + web_url: `https://localhost/git/${name}`, + } +} diff --git a/mock/data/members.mock.ts b/mock/data/members.mock.ts new file mode 100644 index 0000000..86885ff --- /dev/null +++ b/mock/data/members.mock.ts @@ -0,0 +1,9 @@ +import { buildMember } from './members-base.mock' + +export const membersMock = [ + buildMember('user-1'), + buildMember('user-2'), + buildMember('user-3'), + buildMember('user-4'), + buildMember('user-5'), +] diff --git a/mock/data/project-base.mock.ts b/mock/data/project-base.mock.ts new file mode 100644 index 0000000..df2bfb5 --- /dev/null +++ b/mock/data/project-base.mock.ts @@ -0,0 +1,181 @@ +import { capitalize } from '@/utils/string/capitalize' + +export const PROJECT_BASE = { + id: 1, + description: null, + name: 'pen-kian-9', + name_with_namespace: 'Playground / pen-kian-9', + path: 'pen-kian-9', + path_with_namespace: 'playground/pen-kian-9', + created_at: '2024-05-30T14:29:42.697Z', + default_branch: 'main', + tag_list: ['offerte'], + topics: ['offerte'], + ssh_url_to_repo: 'git@git.staging.radical.sexy:playground/pen-kian-9.git', + http_url_to_repo: + 'https://git.staging.radical.sexy/playground/pen-kian-9.git', + web_url: 'https://git.staging.radical.sexy/playground/pen-kian-9', + readme_url: + 'https://git.staging.radical.sexy/playground/pen-kian-9/-/blob/main/INDEX.md', + forks_count: 0, + avatar_url: null, + star_count: 0, + last_activity_at: '2024-05-30T14:29:42.669Z', + namespace: { + id: 3599, + name: 'Playground', + path: 'playground', + kind: 'group', + full_path: 'playground', + parent_id: null, + avatar_url: null, + web_url: 'https://git.staging.radical.sexy/groups/playground', + }, + repository_storage: 'default', + _links: { + self: 'https://git.staging.radical.sexy/api/v4/projects/1254', + issues: 'https://git.staging.radical.sexy/api/v4/projects/1254/issues', + repo_branches: + 'https://git.staging.radical.sexy/api/v4/projects/1254/repository/branches', + labels: 'https://git.staging.radical.sexy/api/v4/projects/1254/labels', + events: 'https://git.staging.radical.sexy/api/v4/projects/1254/events', + members: 'https://git.staging.radical.sexy/api/v4/projects/1254/members', + cluster_agents: + 'https://git.staging.radical.sexy/api/v4/projects/1254/cluster_agents', + }, + packages_enabled: false, + empty_repo: false, + archived: false, + visibility: 'private', + resolve_outdated_diff_discussions: false, + container_expiration_policy: { + cadence: '1d', + enabled: false, + keep_n: 10, + older_than: '90d', + name_regex: '.*', + name_regex_keep: null, + next_run_at: '2024-05-31T14:29:42.705Z', + }, + repository_object_format: 'sha1', + issues_enabled: true, + merge_requests_enabled: false, + wiki_enabled: false, + jobs_enabled: true, + snippets_enabled: false, + container_registry_enabled: false, + service_desk_enabled: true, + service_desk_address: + 'git+playground-pen-kian-9-1254-issue-@service.staging.radical.sexy', + can_create_merge_request_in: false, + issues_access_level: 'private', + repository_access_level: 'enabled', + merge_requests_access_level: 'disabled', + forking_access_level: 'enabled', + wiki_access_level: 'disabled', + builds_access_level: 'enabled', + snippets_access_level: 'disabled', + pages_access_level: 'disabled', + analytics_access_level: 'enabled', + container_registry_access_level: 'disabled', + security_and_compliance_access_level: 'private', + releases_access_level: 'enabled', + environments_access_level: 'enabled', + feature_flags_access_level: 'enabled', + infrastructure_access_level: 'enabled', + monitor_access_level: 'enabled', + model_experiments_access_level: 'enabled', + model_registry_access_level: 'enabled', + emails_disabled: false, + emails_enabled: true, + shared_runners_enabled: false, + lfs_enabled: false, + creator_id: 8, + import_url: 'https://git.staging.radical.sexy/pentext/offerte.git', + import_type: 'git', + import_status: 'finished', + open_issues_count: 0, + description_html: '', + updated_at: '2024-05-30T14:29:42.697Z', + ci_default_git_depth: 20, + ci_forward_deployment_enabled: true, + ci_forward_deployment_rollback_allowed: true, + ci_job_token_scope_enabled: false, + ci_separated_caches: true, + ci_allow_fork_pipelines_to_run_in_parent_project: true, + build_git_strategy: 'fetch', + keep_latest_artifact: true, + restrict_user_defined_variables: false, + runners_token: null, + runner_token_expiration_interval: null, + group_runners_enabled: true, + auto_cancel_pending_pipelines: 'enabled', + build_timeout: 3600, + auto_devops_enabled: false, + auto_devops_deploy_strategy: 'continuous', + ci_config_path: '', + public_jobs: true, + shared_with_groups: [], + only_allow_merge_if_pipeline_succeeds: false, + allow_merge_on_skipped_pipeline: null, + request_access_enabled: true, + only_allow_merge_if_all_discussions_are_resolved: false, + remove_source_branch_after_merge: true, + printing_merge_request_link_enabled: true, + merge_method: 'merge', + squash_option: 'default_off', + enforce_auth_checks_on_uploads: false, + suggestion_commit_message: null, + merge_commit_template: null, + squash_commit_template: null, + issue_branch_template: null, + warn_about_potentially_unwanted_characters: true, + autoclose_referenced_issues: true, + permissions: { + project_access: null, + group_access: { + access_level: 50, + notification_level: 3, + }, + }, +} + +export const buildProject = ( + id: number, + namespace: string, + name: string, + type: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + namespaceObject?: any +) => { + const pathWithNamespace = `${namespace}/${name}` + const capitalizedNamespace = capitalize(namespace) + + return { + ...PROJECT_BASE, + id, + name, + name_with_namespace: `${capitalizedNamespace} / ${name}`, + path: name, + path_with_namespace: pathWithNamespace, + tag_list: [type], + topics: [type], + ssh_url_to_repo: `git@localhost:${pathWithNamespace}`, + http_url_to_repo: `https://localhost/git/${pathWithNamespace}.git`, + web_url: `https://localhost/git/${pathWithNamespace}`, + readme_url: `https://localhost/git/${pathWithNamespace}/-/blob/main/INDEX.md`, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + namespace: namespaceObject + ? namespaceObject + : { + id: 1, + name: capitalizedNamespace, + path: namespace, + kind: 'group', + full_path: namespace, + parent_id: null, + avatar_url: null, + web_url: `https://localhost/git/groups/${namespace}`, + }, + } +} diff --git a/mock/data/project.mock.ts b/mock/data/project.mock.ts new file mode 100644 index 0000000..4e62ad1 --- /dev/null +++ b/mock/data/project.mock.ts @@ -0,0 +1,13 @@ +import { buildProject } from './project-base.mock' + +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +const quotes = [...Array(5)].map((_, i) => + buildProject(i, 'main-namespace', `off-quote-${i}`, 'offerte') +) + +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +const pentests = [...Array(5)].map((_, i) => + buildProject(i + 5, 'main-namespace', `pen-report-${i}`, 'pentest') +) + +export const projectsMock = [...quotes, ...pentests] diff --git a/mock/data/user.mock.ts b/mock/data/user.mock.ts new file mode 100644 index 0000000..4dac6fd --- /dev/null +++ b/mock/data/user.mock.ts @@ -0,0 +1,49 @@ +export const userMock = { + id: 1, + username: 'mockuser', + name: 'Mock', + state: 'active', + locked: false, + avatar_url: null, + web_url: 'https://github.com/mock-user', + created_at: '2023-06-02T04:53:59.767Z', + bio: '', + location: '', + public_email: '', + skype: '', + linkedin: '', + twitter: '', + discord: '', + website_url: '', + organization: '', + job_title: '', + pronouns: '', + bot: false, + work_information: null, + local_time: null, + last_sign_in_at: '2024-05-30T12:58:20.868Z', + confirmed_at: '2023-06-02T04:53:59.744Z', + last_activity_on: '2024-05-31', + email: 'mock@example.org', + theme_id: 2, + color_scheme_id: 1, + projects_limit: 100000, + current_sign_in_at: '2024-05-30T14:18:19.780Z', + identities: [ + { + provider: 'saml', + extern_uid: 'mock@example.org', + }, + ], + can_create_group: true, + can_create_project: true, + two_factor_enabled: false, + external: false, + private_profile: false, + commit_email: 'mock@example.org', + is_admin: true, + note: '', + namespace_id: 1, + created_by: null, + email_reset_offered_at: null, +} diff --git a/mock/data/variables.mock.ts b/mock/data/variables.mock.ts new file mode 100644 index 0000000..2312a3f --- /dev/null +++ b/mock/data/variables.mock.ts @@ -0,0 +1,13 @@ +export const variablesMock = [ + { + variable_type: 'env_var', + key: 'PDF_PASSWORD', + value: 'mypdfpassword', + hidden: false, + protected: true, + masked: true, + raw: false, + environment_scope: '*', + description: null, + }, +] diff --git a/mock/mock-database.ts b/mock/mock-database.ts new file mode 100644 index 0000000..4bef692 --- /dev/null +++ b/mock/mock-database.ts @@ -0,0 +1,76 @@ +import { discussionsMock } from './data/discussions.mock' +import { eventsMock } from './data/events.mock' +import { groupsMock } from './data/groups.mock' +import { issuesMock } from './data/issues.mock' +import { jobsMock } from './data/jobs.mock' +import { labelsMock } from './data/labels.mock' +import { membersMock } from './data/members.mock' +import { buildProject } from './data/project-base.mock' +import { projectsMock } from './data/project.mock' +import { userMock } from './data/user.mock' +import { variablesMock } from './data/variables.mock' + +const data = { + user: userMock, + projects: projectsMock, + events: eventsMock, + issues: issuesMock, + labels: labelsMock, + members: membersMock, + variables: variablesMock, + groups: groupsMock, + discussions: discussionsMock, + jobs: jobsMock, +} + +export const mockDatabase = { + user: () => data.user, + events: () => data.events, + issues: () => data.issues, + labels: () => data.labels, + members: () => data.members, + variables: () => data.variables, + groups: () => data.groups, + discussions: (issueId: number, page: number) => + page === 1 ? data.discussions[issueId - 1] ?? [] : [], + jobs: (page: number) => (page === 1 || Number.isNaN(page) ? data.jobs : []), + projects: (page: number) => + page === 1 || Number.isNaN(page) ? data.projects : [], + createProject: (name: string, namespaceId: number) => { + const namespace = data.groups.find((x) => x.id === namespaceId) + + if (!namespace) { + throw new Error('namespace not found') + } + + const projectId = data.projects.reduce( + (acc, curr) => Math.max(acc, curr.id), + 0 + ) + + const project = buildProject( + projectId + 1, + namespace.name, + name, + 'offerte', + namespace + ) + + data.projects = [...data.projects, project] + + return project + }, + updateProject: (id: number, topics: string[]) => { + const updated = data.projects.find((x) => x.id === id) + + if (!updated) { + throw new Error('project not found') + } + + updated.topics = topics + + data.projects = [...data.projects.filter((x) => x.id !== id), updated] + + return updated + }, +} diff --git a/mock/mock-gitlab-server.ts b/mock/mock-gitlab-server.ts new file mode 100644 index 0000000..a7adc55 --- /dev/null +++ b/mock/mock-gitlab-server.ts @@ -0,0 +1,120 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import proxy from '@fastify/http-proxy' +// eslint-disable-next-line import/no-extraneous-dependencies +import Fastify from 'fastify' +// eslint-disable-next-line import/no-extraneous-dependencies, import/order +import cors from '@fastify/cors' + +import { mockDatabase } from './mock-database' + +const fastify = Fastify({ + logger: true, +}) + +await fastify.register(cors, {}) + +const OIDC_PREFIXES = [ + '/.well-known', + '/jwks', + '/token', + '/authorize', + '/userinfo', + '/revoke', + '/endsession', + '/introspect', +] + +for (const prefix of OIDC_PREFIXES) { + await fastify.register(proxy, { + upstream: 'http://localhost:3001', + prefix, + rewritePrefix: prefix, + }) +} + +fastify.get('/api/v4/user', async (_, res) => { + await res.send(mockDatabase.user()) +}) + +fastify.get('/api/v4/groups', async (_, res) => { + await res.send(mockDatabase.groups()) +}) + +fastify.get('/api/v4/projects/:id/jobs', async (req, res) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + const page = Number((req.query as any).page) + await res.send(mockDatabase.jobs(page)) +}) + +fastify.get( + '/api/v4/projects/:id/issues/:iid/discussions', + async (req, res) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + const iid = Number((req.params as any).iid) + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + const page = Number((req.query as any).page) + await res.send(mockDatabase.discussions(iid, page)) + } +) + +fastify.get('/api/v4/projects/:id/events', async (_, res) => { + await res.send(mockDatabase.events()) +}) + +fastify.get('/api/v4/projects/:id/issues', async (_, res) => { + await res.send(mockDatabase.issues()) +}) + +fastify.get('/api/v4/projects/:id/labels', async (_, res) => { + await res.send(mockDatabase.labels()) +}) + +fastify.get('/api/v4/projects/:id/members', async (_, res) => { + await res.send(mockDatabase.members()) +}) + +fastify.get('/api/v4/projects/:id/variables', async (_, res) => { + await res.send(mockDatabase.variables()) +}) + +fastify.get('/api/v4/projects', async (req, res) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + const page = Number((req.query as any).page) + await res.send(mockDatabase.projects(page)) +}) + +fastify.post('/api/v4/projects/:id/variables', async (req, res) => { + await res.send(req.body) +}) + +fastify.post('/api/v4/projects/:id/access_tokens', async (req, res) => { + await res.send(req.body) +}) + +fastify.put('/api/v4/projects/:id', async (req, res) => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + const id = Number((req.params as any).id) + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + const topics = (req.body as any).topics as string[] + await res.send(mockDatabase.updateProject(id, topics)) +}) + +fastify.post('/api/v4/projects', async (req, res) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any + const { path, namespace_id } = req.body as any + await res.send( + mockDatabase.createProject(path as string, Number(namespace_id)) + ) +}) + +const start = async () => { + try { + await fastify.listen({ port: 3000 }) + fastify.log.info(`Server running on http://localhost:3000`) + } catch (err) { + fastify.log.error(err) + process.exit(1) + } +} + +await start() diff --git a/mock/mock-oidc-server.ts b/mock/mock-oidc-server.ts new file mode 100644 index 0000000..1f79cfe --- /dev/null +++ b/mock/mock-oidc-server.ts @@ -0,0 +1,22 @@ +import { OAuth2Server } from 'oauth2-mock-server' + +const server = new OAuth2Server() + +// Generate a new RSA key and add it to the keystore +await server.issuer.keys.generate('RS256') + +// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars +server.service.on('beforeUserinfo', (response: any, _: any) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + response.body = { + preferred_username: 'Mock User', + } +}) + +// Start the server +await server.start(3001, 'localhost') + +console.log( + 'Mock OIDC Server Running:', + `${server.issuer.url}/.well-known/openid-configuration` +) diff --git a/mock/run-mock.ts b/mock/run-mock.ts new file mode 100644 index 0000000..05785ce --- /dev/null +++ b/mock/run-mock.ts @@ -0,0 +1,29 @@ +// eslint-disable-next-line import/no-extraneous-dependencies, import/no-named-as-default +import concurrently from 'concurrently' + +const { result } = concurrently([ + { + name: 'mock-oidc', + command: `npm run nodemon -- \ + -e ts \ + --watch ./mock/mock-oidc-server.ts \ + --exec npm run dev:mock:oidc`, + prefixColor: 'blue', + }, + { + name: 'mock-gitlab', + command: `npm run nodemon -- \ + -e ts \ + --watch ./mock \ + --ignore ./mock/mock-oidc-server.ts \ + --exec npm run dev:mock:gitlab`, + prefixColor: 'magenta', + }, + { + name: 'app', + command: 'npm run dev -- --mode mock', + prefixColor: '#FFA500', + }, +]) + +await result diff --git a/mock/utils.ts b/mock/utils.ts new file mode 100644 index 0000000..568c99a --- /dev/null +++ b/mock/utils.ts @@ -0,0 +1,24 @@ +export const generateEventDetails = ( + id: number, + action: string, + title: string, + author: string, + createdAt: string +) => { + return { + id, + action_name: action, + target_title: title, + author: { + id: 1, + username: author, + name: author, + state: 'active', + locked: false, + avatar_url: null, + web_url: `https://localhost/git/${author}`, + }, + author_username: author, + created_at: createdAt, + } +} diff --git a/package-lock.json b/package-lock.json index 599f98e..19f3dc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,375 +1,14213 @@ { - "name": "ros-dashboard", + "name": "@radically-open-security/dashboard", + "version": "1.0.0", + "lockfileVersion": 3, "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@jridgewell/sourcemap-codec": { + "packages": { + "": { + "name": "@radically-open-security/dashboard", + "version": "1.0.0", + "dependencies": { + "@axa-fr/oidc-client": "7.22.6", + "@lit-labs/preact-signals": "1.0.2", + "@lit-labs/router": "0.1.3", + "@lit/context": "1.1.1", + "@shoelace-style/shoelace": "2.15.1", + "@tanstack/lit-table": "8.18.0", + "date-fns": "3.6.0", + "lit": "3.1.3", + "marked": "12.0.2", + "pdfjs-dist": "4.3.136", + "urlpattern-polyfill": "10.0.0" + }, + "devDependencies": { + "@commitlint/cli": "19.3.0", + "@commitlint/config-conventional": "19.2.2", + "@eslint/eslintrc": "3.1.0", + "@eslint/js": "9.3.0", + "@fastify/cors": "9.0.1", + "@fastify/http-proxy": "9.5.0", + "@types/node": "20.12.13", + "@typescript-eslint/eslint-plugin": "7.11.0", + "@typescript-eslint/parser": "7.11.0", + "@vitest/coverage-v8": "1.6.0", + "commitizen": "4.3.0", + "concurrently": "8.2.2", + "cz-conventional-changelog": "3.3.0", + "eslint": "8.57.0", + "eslint-define-config": "2.1.0", + "eslint-import-resolver-typescript": "3.6.1", + "eslint-plugin-import": "2.29.1", + "eslint-plugin-lit": "1.14.0", + "eslint-plugin-wc": "2.1.0", + "fastify": "4.27.0", + "husky": "9.0.11", + "lint-staged": "15.2.5", + "nodemon": "3.1.2", + "npm-check-updates": "16.14.20", + "oauth2-mock-server": "7.1.2", + "postcss-lit": "1.1.1", + "prettier": "3.2.5", + "pretty-quick": "4.0.0", + "stylelint": "16.6.1", + "stylelint-config-recess-order": "5.0.1", + "stylelint-config-standard": "36.0.0", + "ts-lit-plugin": "2.0.2", + "tsx": "4.11.0", + "typescript": "5.4.5", + "typescript-eslint": "7.11.0", + "vite": "5.2.12", + "vite-plugin-static-copy": "1.0.5", + "vite-tsconfig-paths": "4.3.2", + "vitest": "1.6.0" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@axa-fr/oidc-client": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@axa-fr/oidc-client/-/oidc-client-7.22.6.tgz", + "integrity": "sha512-xDMfVjjhGYvfEiIei4iNqbjAMWDTOtgunBdxF9SNtJMZPT/m1vUUubrgZtLmRlZqIvgD3Nni/xBME8nqt8WPvw==", + "hasInstallScript": true, + "dependencies": { + "@axa-fr/oidc-client-service-worker": "7.22.6" + } + }, + "node_modules/@axa-fr/oidc-client-service-worker": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@axa-fr/oidc-client-service-worker/-/oidc-client-service-worker-7.22.6.tgz", + "integrity": "sha512-8DcyQ7NYXjq23nS+ptvbOOEqvpjNlqrkZMJWEvtQgYDnHaZ5GVNCuqDEAOldRrfADaZU3P1wjIhC5W+Y+xoIbA==" + }, + "node_modules/@babel/code-frame": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.6.tgz", + "integrity": "sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.6", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.6.tgz", + "integrity": "sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz", + "integrity": "sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz", + "integrity": "sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.6", + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz", + "integrity": "sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz", + "integrity": "sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz", + "integrity": "sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz", + "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.6.tgz", + "integrity": "sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.6", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.6.tgz", + "integrity": "sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz", + "integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.6.tgz", + "integrity": "sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.6.tgz", + "integrity": "sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.6", + "@babel/generator": "^7.24.6", + "@babel/helper-environment-visitor": "^7.24.6", + "@babel/helper-function-name": "^7.24.6", + "@babel/helper-hoist-variables": "^7.24.6", + "@babel/helper-split-export-declaration": "^7.24.6", + "@babel/parser": "^7.24.6", + "@babel/types": "^7.24.6", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.6.tgz", + "integrity": "sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.6", + "@babel/helper-validator-identifier": "^7.24.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@commitlint/cli": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.3.0.tgz", + "integrity": "sha512-LgYWOwuDR7BSTQ9OLZ12m7F/qhNY+NpAyPBgo4YNMkACE7lGuUnuQq1yi9hz1KA4+3VqpOYl8H1rY/LYK43v7g==", + "dev": true, + "dependencies": { + "@commitlint/format": "^19.3.0", + "@commitlint/lint": "^19.2.2", + "@commitlint/load": "^19.2.0", + "@commitlint/read": "^19.2.1", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.2.2.tgz", + "integrity": "sha512-mLXjsxUVLYEGgzbxbxicGPggDuyWNkf25Ht23owXIH+zV2pv1eJuzLK3t1gDY5Gp6pxdE60jZnWUY5cvgL3ufw==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "conventional-changelog-conventionalcommits": "^7.0.2" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.0.3.tgz", + "integrity": "sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "ajv": "^8.11.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/config-validator/node_modules/ajv": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", + "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/@commitlint/ensure": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.0.3.tgz", + "integrity": "sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.0.0.tgz", + "integrity": "sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.3.0.tgz", + "integrity": "sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.2.2.tgz", + "integrity": "sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "semver": "^7.6.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/lint": { + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.2.2.tgz", + "integrity": "sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==", + "dev": true, + "dependencies": { + "@commitlint/is-ignored": "^19.2.2", + "@commitlint/parse": "^19.0.3", + "@commitlint/rules": "^19.0.3", + "@commitlint/types": "^19.0.3" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load": { + "version": "19.2.0", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.2.0.tgz", + "integrity": "sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/execute-rule": "^19.0.0", + "@commitlint/resolve-extends": "^19.1.0", + "@commitlint/types": "^19.0.3", + "chalk": "^5.3.0", + "cosmiconfig": "^9.0.0", + "cosmiconfig-typescript-loader": "^5.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/message": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.0.0.tgz", + "integrity": "sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.0.3.tgz", + "integrity": "sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA==", + "dev": true, + "dependencies": { + "@commitlint/types": "^19.0.3", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/read": { + "version": "19.2.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.2.1.tgz", + "integrity": "sha512-qETc4+PL0EUv7Q36lJbPG+NJiBOGg7SSC7B5BsPWOmei+Dyif80ErfWQ0qXoW9oCh7GTpTNRoaVhiI8RbhuaNw==", + "dev": true, + "dependencies": { + "@commitlint/top-level": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1", + "git-raw-commits": "^4.0.0", + "minimist": "^1.2.8" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.1.0.tgz", + "integrity": "sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==", + "dev": true, + "dependencies": { + "@commitlint/config-validator": "^19.0.3", + "@commitlint/types": "^19.0.3", + "global-directory": "^4.0.1", + "import-meta-resolve": "^4.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/rules": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.0.3.tgz", + "integrity": "sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==", + "dev": true, + "dependencies": { + "@commitlint/ensure": "^19.0.3", + "@commitlint/message": "^19.0.0", + "@commitlint/to-lines": "^19.0.0", + "@commitlint/types": "^19.0.3", + "execa": "^8.0.1" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.0.0.tgz", + "integrity": "sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==", + "dev": true, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.0.0.tgz", + "integrity": "sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==", + "dev": true, + "dependencies": { + "find-up": "^7.0.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz", + "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==", + "dev": true, + "dependencies": { + "locate-path": "^7.2.0", + "path-exists": "^5.0.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/@commitlint/types": { + "version": "19.0.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.0.3.tgz", + "integrity": "sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA==", + "dev": true, + "dependencies": { + "@types/conventional-commits-parser": "^5.0.0", + "chalk": "^5.3.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.6.3.tgz", + "integrity": "sha512-xI/tL2zxzEbESvnSxwFgwvy5HS00oCXxL4MLs6HUiDcYfwowsoQaABKxUElp1ARITrINzBnsECOc1q0eg2GOrA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^2.3.1" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.3.1.tgz", + "integrity": "sha512-iMNHTyxLbBlWIfGtabT157LH9DUx9X8+Y3oymFEuMj8HNc+rpE3dPFGFgHjpKfjeFDjLjYIAIhXPGvS2lKxL9g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.11.tgz", + "integrity": "sha512-uox5MVhvNHqitPP+SynrB1o8oPxPMt2JLgp5ghJOWf54WGQ5OKu47efne49r1SWqs3wRP8xSWjnO9MBKxhB1dA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^2.6.3", + "@csstools/css-tokenizer": "^2.3.1" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-3.1.1.tgz", + "integrity": "sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.13" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-4.1.0.tgz", + "integrity": "sha512-WyOx8cJQ+FQus4Mm4uPIZA64gbk3Wxh0so5Lcii0aJifqwoVOlfFtorjLE0Hen4OYyHZMXDWqMmaQemBhgxFRQ==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@dual-bundle/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "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": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.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": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.3.0.tgz", + "integrity": "sha512-niBqk8iwv96+yuTwjM6bWg8ovzAPF9qkICsGtcoa5/dmqcEMfdwNAX7+/OHcJHc7wj7XqPxH98oAHytFYlw6Sw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@fastify/ajv-compiler": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", + "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", + "dev": true, + "dependencies": { + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/ajv": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@fastify/ajv-compiler/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@fastify/cors": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-9.0.1.tgz", + "integrity": "sha512-YY9Ho3ovI+QHIL2hW+9X4XqQjXLjJqsU+sMV/xFsxZkE8p3GNnYVFpoOxF7SsP5ZL76gwvbo3V9L+FIekBGU4Q==", + "dev": true, + "dependencies": { + "fastify-plugin": "^4.0.0", + "mnemonist": "0.39.6" + } + }, + "node_modules/@fastify/error": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==", + "dev": true + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "dev": true, + "dependencies": { + "fast-json-stringify": "^5.7.0" + } + }, + "node_modules/@fastify/http-proxy": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@fastify/http-proxy/-/http-proxy-9.5.0.tgz", + "integrity": "sha512-1iqIdV10d5k9YtfHq9ylX5zt1NiM50fG+rIX40qt00R694sqWso3ukyTFZVk33SDoSiBW8roB7n11RUVUoN+Ag==", + "dev": true, + "dependencies": { + "@fastify/reply-from": "^9.0.0", + "fast-querystring": "^1.1.2", + "fastify-plugin": "^4.5.0", + "ws": "^8.4.2" + } + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@fastify/reply-from": { + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/@fastify/reply-from/-/reply-from-9.8.0.tgz", + "integrity": "sha512-bPNVaFhEeNI0Lyl6404YZaPFokudCplidE3QoOcr78yOy6H9sYw97p5KPYvY/NJNUHfFtvxOaSAHnK+YSiv/Mg==", + "dev": true, + "dependencies": { + "@fastify/error": "^3.0.0", + "end-of-stream": "^1.4.4", + "fast-content-type-parse": "^1.1.0", + "fast-querystring": "^1.0.0", + "fastify-plugin": "^4.0.0", + "toad-cache": "^3.7.0", + "undici": "^5.19.1" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "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/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.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "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/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/@isaacs/cliui/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/@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/@isaacs/cliui/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/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", "dev": true }, - "@lit/reactive-element": { - "version": "1.0.0-rc.2", - "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.0.0-rc.2.tgz", - "integrity": "sha512-cujeIl5Ei8FC7UHf4/4Q3bRJOtdTe1vpJV/JEBYCggedmQ+2P8A2oz7eE+Vxi6OJ4nc0X+KZxXnBoH4QrEbmEQ==" - }, - "@popperjs/core": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.1.tgz", - "integrity": "sha512-DvJbbn3dUgMxDnJLH+RZQPnXak1h4ZVYQ7CWiFWjQwBFkVajT4rfw2PdpHLTSTwxrYfnoEXkuBiwkDm6tPMQeA==" - }, - "@rollup/plugin-commonjs": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.1.0.tgz", - "integrity": "sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^5.0.1", - "commondir": "^1.0.1", - "estree-walker": "^2.0.2", - "glob": "^8.0.3", - "is-reference": "1.2.1", - "magic-string": "^0.27.0" - }, - "dependencies": { - "@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", - "dev": true, - "requires": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - } - }, - "@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", - "dev": true - }, - "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==", - "dev": true - } - } - }, - "@rollup/plugin-node-resolve": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz", - "integrity": "sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw==", - "requires": { - "@rollup/pluginutils": "^3.1.0", - "@types/resolve": "1.17.1", - "deepmerge": "^4.2.2", - "is-builtin-module": "^3.1.0", - "is-module": "^1.0.0", - "resolve": "^1.19.0" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lit-labs/preact-signals": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lit-labs/preact-signals/-/preact-signals-1.0.2.tgz", + "integrity": "sha512-HFgIhqLB5IiNbvJxEN3+o6n9x/fNZo7pqfElG56NHrOFBsIFW7wswbp6hHeoGzATQDOB2ZmrH/VrRGYdcgl29g==", + "dependencies": { + "@preact/signals-core": "^1.3.0", + "lit": "^3.1.2" + } + }, + "node_modules/@lit-labs/router": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@lit-labs/router/-/router-0.1.3.tgz", + "integrity": "sha512-G+HHo57KsArG58LOI8DLtipFfC9tVV4lGaDy2I8hYQvS2P/pV5wQObrpFYPZswse8D47y8VuHNXNdVPQOVc5MA==", + "dependencies": { + "lit": "^2.0.0 || ^3.0.0" + } + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz", + "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==" + }, + "node_modules/@lit/context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.1.tgz", + "integrity": "sha512-q/Rw7oWSJidUP43f/RUPwqZ6f5VlY8HzinTWxL/gW1Hvm2S5q2hZvV+qM8WFcC+oLNNknc3JKsd5TwxLk1hbdg==", + "dependencies": { + "@lit/reactive-element": "^1.6.2 || ^2.0.0" + } + }, + "node_modules/@lit/react": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@lit/react/-/react-1.0.4.tgz", + "integrity": "sha512-6HBvk3AwF46z17fTkZp5F7/EdCJW9xqqQgYKr3sQGgoEJv0TKV1voWydG4UQQA2RWkoD4SHjy08snSpzyoyd0w==", + "peerDependencies": { + "@types/react": "17 || 18" + } + }, + "node_modules/@lit/reactive-element": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", + "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0" + } + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/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==", + "optional": true + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@mapbox/node-pre-gyp/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==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", + "optional": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "node_modules/@mapbox/node-pre-gyp/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==", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/@npmcli/fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", + "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", + "dev": true, + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", + "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "dev": true, + "dependencies": { + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", + "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", + "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "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/@pnpm/config.env-replace": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", + "integrity": "sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==", + "dev": true, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/network.ca-file/-/network.ca-file-1.0.2.tgz", + "integrity": "sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==", + "dev": true, + "dependencies": { + "graceful-fs": "4.2.10" + }, + "engines": { + "node": ">=12.22.0" + } + }, + "node_modules/@pnpm/network.ca-file/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/@pnpm/npm-conf": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.2.2.tgz", + "integrity": "sha512-UA91GwWPhFExt3IizW6bOeY/pQ0BkuNwKjk9iQW9KqxluGCrg4VenZ0/L+2Y0+ZOtme72EVvg6v0zo3AMQRCeA==", + "dev": true, + "dependencies": { + "@pnpm/config.env-replace": "^1.1.0", + "@pnpm/network.ca-file": "^1.0.1", + "config-chain": "^1.1.11" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.6.0.tgz", + "integrity": "sha512-O/XGxwP85h1F7+ouqTMOIZ3+V1whfaV9ToIVcuyGriD4JkSD00cQo54BKdqjvBJxbenvp7ynfqRHEwI6e+NIhw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz", + "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz", + "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz", + "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz", + "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz", + "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz", + "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz", + "integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz", + "integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz", + "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz", + "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz", + "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz", + "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz", + "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz", + "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz", + "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", + "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@shoelace-style/animations": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@shoelace-style/animations/-/animations-1.1.0.tgz", + "integrity": "sha512-Be+cahtZyI2dPKRm8EZSx3YJQ+jLvEcn3xzRP7tM4tqBnvd/eW/64Xh0iOf0t2w5P8iJKfdBbpVNE9naCaOf2g==", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/claviska" + } + }, + "node_modules/@shoelace-style/localize": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@shoelace-style/localize/-/localize-3.1.2.tgz", + "integrity": "sha512-Hf45HeO+vdQblabpyZOTxJ4ZeZsmIUYXXPmoYrrR4OJ5OKxL+bhMz5mK8JXgl7HsoEowfz7+e248UGi861de9Q==" + }, + "node_modules/@shoelace-style/shoelace": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/@shoelace-style/shoelace/-/shoelace-2.15.1.tgz", + "integrity": "sha512-3ecUw8gRwOtcZQ8kWWkjk4FTfObYQ/XIl3aRhxprESoOYV1cYhloYPsmQY38UoL3+pwJiZb5+LzX0l3u3Zl0GA==", + "dependencies": { + "@ctrl/tinycolor": "^4.0.2", + "@floating-ui/dom": "^1.5.3", + "@lit/react": "^1.0.0", + "@shoelace-style/animations": "^1.1.0", + "@shoelace-style/localize": "^3.1.2", + "composed-offset-position": "^0.0.4", + "lit": "^3.0.0", + "qr-creator": "^1.0.0" + }, + "engines": { + "node": ">=14.17.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/claviska" + } + }, + "node_modules/@sigstore/bundle": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.1.0.tgz", + "integrity": "sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz", + "integrity": "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-1.0.0.tgz", + "integrity": "sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "make-fetch-happen": "^11.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz", + "integrity": "sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0", + "tuf-js": "^1.1.7" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "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/@sindresorhus/is": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", + "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", + "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.1" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@tanstack/lit-table": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/@tanstack/lit-table/-/lit-table-8.18.0.tgz", + "integrity": "sha512-97eM+LRipPFLm7Gptc8yH8YeqQVhwmqq1tB43ld3iPCSRdm/6pM+LhwikhPlsWg4LJuK4sHD5yrT9CSXuFvLmQ==", + "dependencies": { + "@tanstack/table-core": "8.17.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "lit": "^3.1.3" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.17.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.17.3.tgz", + "integrity": "sha512-mPBodDGVL+fl6d90wUREepHa/7lhsghg2A3vFpakEhrhtbIlgNAZiMr7ccTgak5qbHqF14Fwy+W1yFWQt+WmYQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tufjs/canonical-json": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", + "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", + "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", + "dev": true, + "dependencies": { + "@tufjs/canonical-json": "1.0.0", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/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/@tufjs/models/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "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/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.12.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.13.tgz", + "integrity": "sha512-gBGeanV41c1L171rR7wjbMiEpEI/l5XFQdLLfhr/REwpgDy/4U8y89+i8kRiLzDyZdOkXh+cRaTetUnCYutoXA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "peer": true + }, + "node_modules/@types/react": { + "version": "18.2.79", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", + "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", + "peer": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/semver-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/semver-utils/-/semver-utils-1.1.3.tgz", + "integrity": "sha512-T+YwkslhsM+CeuhYUxyAjWm7mJ5am/K10UX40RuA6k6Lc7eGtq8iY2xOzy7Vq0GOqhl/xZl5l2FwURZMTPTUww==", + "dev": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.11.0.tgz", + "integrity": "sha512-P+qEahbgeHW4JQ/87FuItjBj8O3MYv5gELDzr8QaQ7fsll1gSMTYb6j87MYyxwf3DtD7uGFB9ShwgmCJB5KmaQ==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.11.0", + "@typescript-eslint/type-utils": "7.11.0", + "@typescript-eslint/utils": "7.11.0", + "@typescript-eslint/visitor-keys": "7.11.0", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.11.0.tgz", + "integrity": "sha512-yimw99teuaXVWsBcPO1Ais02kwJ1jmNA1KxE7ng0aT7ndr1pT1wqj0OJnsYVGKKlc4QJai86l/025L6z8CljOg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.11.0", + "@typescript-eslint/types": "7.11.0", + "@typescript-eslint/typescript-estree": "7.11.0", + "@typescript-eslint/visitor-keys": "7.11.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.11.0.tgz", + "integrity": "sha512-27tGdVEiutD4POirLZX4YzT180vevUURJl4wJGmm6TrQoiYwuxTIY98PBp6L2oN+JQxzE0URvYlzJaBHIekXAw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.11.0", + "@typescript-eslint/visitor-keys": "7.11.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.11.0.tgz", + "integrity": "sha512-WmppUEgYy+y1NTseNMJ6mCFxt03/7jTOy08bcg7bxJJdsM4nuhnchyBbE8vryveaJUf62noH7LodPSo5Z0WUCg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "7.11.0", + "@typescript-eslint/utils": "7.11.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.11.0.tgz", + "integrity": "sha512-MPEsDRZTyCiXkD4vd3zywDCifi7tatc4K37KqTprCvaXptP7Xlpdw0NR2hRJTetG5TxbWDB79Ys4kLmHliEo/w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.11.0.tgz", + "integrity": "sha512-cxkhZ2C/iyi3/6U9EPc5y+a6csqHItndvN/CzbNXTNrsC3/ASoYQZEt9uMaEp+xFNjasqQyszp5TumAVKKvJeQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.11.0", + "@typescript-eslint/visitor-keys": "7.11.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.11.0.tgz", + "integrity": "sha512-xlAWwPleNRHwF37AhrZurOxA1wyXowW4PqVXZVUNCLjB48CqdPJoJWkrpH2nij9Q3Lb7rtWindtoXwxjxlKKCA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.11.0", + "@typescript-eslint/types": "7.11.0", + "@typescript-eslint/typescript-estree": "7.11.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.11.0.tgz", + "integrity": "sha512-7syYk4MzjxTEk0g/w3iqtgxnFQspDJfn6QKD36xMuuhTzjcxY7F8EmBLnALjVyaOF1/bVocu3bS/2/F7rXrveQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.11.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.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/@vitest/coverage-v8": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", + "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@bcoe/v8-coverage": "^0.2.3", + "debug": "^4.3.4", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-lib-source-maps": "^5.0.4", + "istanbul-reports": "^3.1.6", + "magic-string": "^0.30.5", + "magicast": "^0.3.3", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "test-exclude": "^6.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "1.6.0" + } + }, + "node_modules/@vitest/expect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "dev": true, + "dependencies": { + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "dev": true, + "dependencies": { + "@vitest/utils": "1.6.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "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.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "dev": true, + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "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/@vscode/web-custom-data": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@vscode/web-custom-data/-/web-custom-data-0.4.9.tgz", + "integrity": "sha512-QeCJFISE/RiTG0NECX6DYmVRPVb0jdyaUrhY0JqNMv9ruUYtYqxxQfv3PSjogb+zNghmwgXLSYuQKk6G+Xnaig==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "devOptional": true + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.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": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "devOptional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "dev": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/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/ansi-align/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/ansi-align/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/ansi-escapes": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", + "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "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/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/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "devOptional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/avvio": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.3.2.tgz", + "integrity": "sha512-st8e519GWHa/azv8S87mcJvZs4WsgTBjOw/Ih1CP6u+8SZvcOeAYNG6JbsIrAUUJJ7JfmrnOkR8ipDS+u9SIRQ==", + "dev": true, + "dependencies": { + "@fastify/error": "^3.3.0", + "fastq": "^1.17.1" + } + }, + "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==", + "devOptional": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "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/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "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/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/boxen": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.1.1.tgz", + "integrity": "sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==", + "dev": true, + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.1", + "chalk": "^5.2.0", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/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/boxen/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/boxen/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/boxen/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/boxen/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/boxen/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/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/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "devOptional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.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/cacache": { + "version": "17.1.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz", + "integrity": "sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^7.0.3", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/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/cacache/node_modules/glob": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request": { + "version": "10.2.14", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz", + "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "^4.0.2", + "get-stream": "^6.0.1", + "http-cache-semantics": "^4.1.1", + "keyv": "^4.5.3", + "mimic-response": "^4.0.0", + "normalize-url": "^8.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/canvas": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz", + "integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "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/chalk/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/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "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/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "devOptional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", + "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", + "dev": true, + "dependencies": { + "restore-cursor": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/cli-table3/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/cli-table3/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/cli-table3/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/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/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/cliui/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/cliui/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/cliui/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/cliui/node_modules/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/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "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/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "devOptional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/commitizen": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.3.0.tgz", + "integrity": "sha512-H0iNtClNEhT0fotHvGV3E9tDejDeS04sN1veIebsKYGMuGscFaswRoYJKmT3eW85eIJAs0F28bG2+a/9wCOfPw==", + "dev": true, + "dependencies": { + "cachedir": "2.3.0", + "cz-conventional-changelog": "3.3.0", + "dedent": "0.7.0", + "detect-indent": "6.1.0", + "find-node-modules": "^2.1.2", + "find-root": "1.1.0", + "fs-extra": "9.1.0", + "glob": "7.2.3", + "inquirer": "8.2.5", + "is-utf8": "^0.2.1", + "lodash": "4.17.21", + "minimist": "1.2.7", + "strip-bom": "4.0.0", + "strip-json-comments": "3.1.1" + }, + "bin": { + "commitizen": "bin/commitizen", + "cz": "bin/git-cz", + "git-cz": "bin/git-cz" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/commitizen/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/commitizen/node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/commitizen/node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/compare-func/node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/composed-offset-position": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/composed-offset-position/-/composed-offset-position-0.0.4.tgz", + "integrity": "sha512-vMlvu1RuNegVE0YsCDSV/X4X10j56mq7PCIyOKK74FxkXzGLwhOUmdkJLSdOBOMwWycobGUMgft2lp+YgTe8hw==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "devOptional": true + }, + "node_modules/concurrently": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz", + "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/concurrently/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "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/configstore": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-6.0.0.tgz", + "integrity": "sha512-cD31W1v3GqUlQvbBCGcXmd2Nj9SvLDOP1oQ0YFuLETufzSPaKp11rYBsSOm7rCsW3OnIRAFM3OxRhceaXNYHkA==", + "dev": true, + "dependencies": { + "dot-prop": "^6.0.1", + "graceful-fs": "^4.2.6", + "unique-string": "^3.0.0", + "write-file-atomic": "^3.0.3", + "xdg-basedir": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/yeoman/configstore?sponsor=1" + } + }, + "node_modules/configstore/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/configstore/node_modules/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "devOptional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-commit-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/conventional-commit-types/-/conventional-commit-types-3.0.0.tgz", + "integrity": "sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==", + "dev": true + }, + "node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/conventional-commits-parser/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig-typescript-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", + "dev": true, + "dependencies": { + "jiti": "^1.19.1" + }, + "engines": { + "node": ">=v16" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=8.2", + "typescript": ">=4" + } + }, + "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/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/css-functions-list": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", + "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", + "dev": true, + "engines": { + "node": ">=12 || >=16" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "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/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "peer": true + }, + "node_modules/cz-conventional-changelog": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cz-conventional-changelog/-/cz-conventional-changelog-3.3.0.tgz", + "integrity": "sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==", + "dev": true, + "dependencies": { + "chalk": "^2.4.1", + "commitizen": "^4.0.3", + "conventional-commit-types": "^3.0.0", + "lodash.map": "^4.5.1", + "longest": "^2.0.1", + "word-wrap": "^1.0.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@commitlint/load": ">6.1.1" + } + }, + "node_modules/cz-conventional-changelog/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/cz-conventional-changelog/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/cz-conventional-changelog/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/cz-conventional-changelog/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/cz-conventional-changelog/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dargs": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", + "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "devOptional": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "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-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "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/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "devOptional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/didyoumean2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/didyoumean2/-/didyoumean2-4.1.0.tgz", + "integrity": "sha512-qTBmfQoXvhKO75D/05C8m+fteQmn4U46FWYiLhXtZQInzitXLWY0EQ/2oKnpAz9g2lQWW8jYcLcT+hPJGT+kig==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.10.2", + "leven": "^3.1.0", + "lodash.deburr": "^4.1.0" + }, + "engines": { + "node": ">=10.13" + } + }, + "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/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-goat": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz", + "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "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-define-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-define-config/-/eslint-define-config-2.1.0.tgz", + "integrity": "sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/Shinigami92" + }, + { + "type": "paypal", + "url": "https://www.paypal.com/donate/?hosted_button_id=L7GY729FBKTZY" + } + ], + "engines": { + "node": ">=18.0.0", + "npm": ">=9.0.0", + "pnpm": ">=8.6.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz", + "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "enhanced-resolve": "^5.12.0", + "eslint-module-utils": "^2.7.4", + "fast-glob": "^3.3.1", + "get-tsconfig": "^4.5.0", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", + "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-lit": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-lit/-/eslint-plugin-lit-1.14.0.tgz", + "integrity": "sha512-J4w+CgO31621GreLFCdTUbTr5yeV2/RJ/M0myw0dykD5p9FGGIRLityQiNa6SG+JpVbmeQTQPJy4pNFmiurJ/w==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "requireindex": "^1.2.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "eslint": ">= 5" + } + }, + "node_modules/eslint-plugin-lit/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/eslint-plugin-wc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.1.0.tgz", + "integrity": "sha512-s/BGOtmpgQ2yifR6EC1OM9t0DwYLgg4ZAL07Kw4eXvBb5TYaPafI+65tswvnZvhH8FqcjERLbBZPPvYsvinkfg==", + "dev": true, + "dependencies": { + "is-valid-element-name": "^1.0.0", + "js-levenshtein-esm": "^1.2.0" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "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/@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/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/eslint/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/eslint/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/espree": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.0.1.tgz", + "integrity": "sha512-MWkrWZbJsL2UwnjxTX3gG8FneachS/Mwg7tdGXce011sJd5b0JG54vat5KHnfSBODZ3Wvzd2WnjxyzsRoVv+ww==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.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/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "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/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-content-type-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==", + "dev": true + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-json-stringify": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.0.tgz", + "integrity": "sha512-A4bg6E15QrkuVO3f0SwIASgzMzR6XC4qTyTqhf3hYXy0iazbAdZKwkE+ox4WgzKyzM6ygvbdq3r134UjOaaAnA==", + "dev": true, + "dependencies": { + "@fastify/merge-json-schemas": "^0.1.0", + "ajv": "^8.10.0", + "ajv-formats": "^3.0.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.1.0", + "json-schema-ref-resolver": "^1.0.1", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.14.0.tgz", + "integrity": "sha512-oYs1UUtO97ZO2lJ4bwnWeQW8/zvOIQLGKcvPTsWmvc2SYgBb+upuNS5NxoLaMU4h8Ju3Nbj6Cq8mD2LQoqVKFA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.4.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/fast-json-stringify/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "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/fast-memoize": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", + "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==", + "dev": true + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "dev": true, + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-uri": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", + "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastify": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.27.0.tgz", + "integrity": "sha512-ci9IXzbigB8dyi0mSy3faa3Bsj0xWAPb9JeT4KRzubdSb6pNhcADRUaXCBml6V1Ss/a05kbtQls5LBmhHydoTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "dependencies": { + "@fastify/ajv-compiler": "^3.5.0", + "@fastify/error": "^3.4.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.3.0", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^8.0.0", + "light-my-request": "^5.11.0", + "pino": "^9.0.0", + "process-warning": "^3.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" + } + }, + "node_modules/fastify-plugin": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", + "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==", + "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/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-my-way": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.0.tgz", + "integrity": "sha512-HdWXgFYc6b1BJcOBDBwjqWuHJj1WYiqrxSh25qtU4DabpMFdj/gSunNBQb83t+8Zt67D7CXEzJWTkxaShMTMOA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^3.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/find-node-modules": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/find-node-modules/-/find-node-modules-2.1.3.tgz", + "integrity": "sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==", + "dev": true, + "dependencies": { + "findup-sync": "^4.0.0", + "merge": "^2.1.1" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "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/findup-sync": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", + "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", + "dev": true, + "dependencies": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^4.0.2", + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/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-encoder": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", + "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", + "dev": true, + "engines": { + "node": ">= 14.17" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fp-and-or": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/fp-and-or/-/fp-and-or-0.1.4.tgz", + "integrity": "sha512-+yRYRhpnFPWXSly/6V4Lw9IfOV26uu30kynGJ03PW+MnjOEQe45RZ141QcS0aJehYBYA50GfCDnsRbFJdhssRw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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==", + "devOptional": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/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/gauge/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/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/gauge/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/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/get-symbol-description": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.5.tgz", + "integrity": "sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==", + "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/git-raw-commits": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz", + "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==", + "dev": true, + "dependencies": { + "dargs": "^8.0.0", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.mjs" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/git-raw-commits/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, + "engines": { + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "devOptional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "dev": true, + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "12.6.1", + "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", + "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^5.2.0", + "@szmarczak/http-timer": "^5.0.1", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^10.2.8", + "decompress-response": "^6.0.0", + "form-data-encoder": "^2.1.2", + "get-stream": "^6.0.1", + "http2-wrapper": "^2.1.10", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^3.0.0", + "responselike": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "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-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "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/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "devOptional": true + }, + "node_modules/has-yarn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", + "integrity": "sha512-IrsVwUHhEULx3R8f/aA8AHuEzAorplsab/v8HBzEiIukwq5i/EC+xmOW+HfP1OaDP+2JkgT1yILHN2O3UFIbcA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "devOptional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "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/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "dev": true, + "bin": { + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "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==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/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/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/ignore-walk": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", + "dev": true, + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ignore-walk/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/ignore-walk/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "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==", + "devOptional": 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==", + "devOptional": 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/inquirer": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", + "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/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/inquirer/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/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/inquirer/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/inquirer/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/inquirer/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/inquirer/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/inquirer/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inquirer/node_modules/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/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-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-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-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": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-npm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-6.0.0.tgz", + "integrity": "sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "dependencies": { + "text-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "node_modules/is-valid-element-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-element-name/-/is-valid-element-name-1.0.0.tgz", + "integrity": "sha512-GZITEJY2LkSjQfaIPBha7eyZv+ge0PhBR7KITeCCWvy7VBQrCUdFkvpI+HrAPQjVtVjy1LvlEkqQTHckoszruw==", + "dev": true, + "dependencies": { + "is-potential-custom-element-name": "^1.0.0" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-yarn-global": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.4.1.tgz", + "integrity": "sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", + "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", + "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/jiti": { + "version": "1.21.3", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.3.tgz", + "integrity": "sha512-uy2bNX5zQ+tESe+TiC7ilGRz8AtRGmnJH55NC5S0nSUjvvvM2hJHmefHErugGXN4pNv4Qx7vLsnNw9qJ9mtIsw==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "node_modules/jose": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.3.0.tgz", + "integrity": "sha512-IChe9AtAE79ru084ow8jzkN2lNrG3Ntfiv65Cvj9uOCE2m5LNsdHG+9EbxWxAoWRF9TgDOqLN5jm08++owDVRg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/js-levenshtein-esm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz", + "integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "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/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-parse-helpfulerror": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", + "integrity": "sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==", + "dev": true, + "dependencies": { + "jju": "^1.1.0" + } + }, + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonlines": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsonlines/-/jsonlines-0.1.1.tgz", + "integrity": "sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==", + "dev": true + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "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/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/known-css-properties": { + "version": "0.31.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.31.0.tgz", + "integrity": "sha512-sBPIUGTNF0czz0mwGGUoKKJC8Q7On1GPbCSFPfyEsfHb2DyBG0Y4QtV+EVWpINSaiGKZblDNuF5AezxSgOhesQ==", + "dev": true + }, + "node_modules/latest-version": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-7.0.0.tgz", + "integrity": "sha512-KvNT4XqAMzdcL6ka6Tl3i2lYeFDgXNCuIX+xNx6ZMVR1dFq+idXd9FLKNMOIx0t9mJ9/HudyX4oZWXZQ0UJHeg==", + "dev": true, + "dependencies": { + "package-json": "^8.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/light-my-request": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.13.0.tgz", + "integrity": "sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==", + "dev": true, + "dependencies": { + "cookie": "^0.6.0", + "process-warning": "^3.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "node_modules/lilconfig": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", + "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lint-staged": { + "version": "15.2.5", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.5.tgz", + "integrity": "sha512-j+DfX7W9YUvdzEZl3Rk47FhDF6xwDBV5wwsCPw6BwWZVPYJemusQmvb9bRsW23Sqsaa+vRloAWogbK4BUuU2zA==", + "dev": true, + "dependencies": { + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.4", + "execa": "~8.0.1", + "lilconfig": "~3.1.1", + "listr2": "~8.2.1", + "micromatch": "~4.0.7", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.4.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/listr2": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz", + "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.0.0", + "rfdc": "^1.3.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/lit": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.3.tgz", + "integrity": "sha512-l4slfspEsnCcHVRTvaP7YnkTZEZggNFywLEIhQaGhYDczG+tu/vlgm/KaWIEjIp+ZyV20r2JnZctMb8LeLCG7Q==", + "dependencies": { + "@lit/reactive-element": "^2.0.4", + "lit-element": "^4.0.4", + "lit-html": "^3.1.2" + } + }, + "node_modules/lit-analyzer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/lit-analyzer/-/lit-analyzer-2.0.3.tgz", + "integrity": "sha512-XiAjnwVipNrKav7r3CSEZpWt+mwYxrhPRVC7h8knDmn/HWTzzWJvPe+mwBcL2brn4xhItAMzZhFC8tzzqHKmiQ==", + "dev": true, + "dependencies": { + "@vscode/web-custom-data": "^0.4.2", + "chalk": "^2.4.2", + "didyoumean2": "4.1.0", + "fast-glob": "^3.2.11", + "parse5": "5.1.0", + "ts-simple-type": "~2.0.0-next.0", + "vscode-css-languageservice": "4.3.0", + "vscode-html-languageservice": "3.1.0", + "web-component-analyzer": "^2.0.0" + }, + "bin": { + "lit-analyzer": "cli.js" + } + }, + "node_modules/lit-analyzer/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lit-analyzer/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lit-analyzer/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/lit-analyzer/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/lit-analyzer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/lit-analyzer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/lit-analyzer/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lit-element": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.5.tgz", + "integrity": "sha512-iTWskWZEtn9SyEf4aBG6rKT8GABZMrTWop1+jopsEOgEcugcXJGKuX5bEbkq9qfzY+XB4MAgCaSPwnNpdsNQ3Q==", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit/reactive-element": "^2.0.4", + "lit-html": "^3.1.2" + } + }, + "node_modules/lit-html": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.3.tgz", + "integrity": "sha512-FwIbqDD8O/8lM4vUZ4KvQZjPPNx7V1VhT7vmRB8RBAO0AU6wuTVdoXiu2CivVjEGdugvcbPNBLtPE1y0ifplHA==", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "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.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.deburr": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.deburr/-/lodash.deburr-4.1.0.tgz", + "integrity": "sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, + "node_modules/lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==", + "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/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz", + "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==", + "dev": true, + "dependencies": { + "ansi-escapes": "^6.2.0", + "cli-cursor": "^4.0.0", + "slice-ansi": "^7.0.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/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/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/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/longest": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-2.0.1.tgz", + "integrity": "sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "devOptional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/magic-string": { + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + }, + "node_modules/magicast": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.4.tgz", + "integrity": "sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.24.4", + "@babel/types": "^7.24.0", + "source-map-js": "^1.2.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", + "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/marked": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.2.tgz", + "integrity": "sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "dev": true + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", + "integrity": "sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==", + "dev": true + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "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/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "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==", + "dev": true, + "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/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "devOptional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "devOptional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "devOptional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "devOptional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "devOptional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mlly": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.0.tgz", + "integrity": "sha512-U9SDaXGEREBYQgfejV97coK0UL1r+qnF2SyO9A3qcI8MzKnsIFKHNVEkrDyNncQTKQQumsasmeq84eNMdBfsNQ==", + "dev": true, + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.0", + "ufo": "^1.5.3" + } + }, + "node_modules/mnemonist": { + "version": "0.39.6", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", + "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==", + "dev": true, + "dependencies": { + "obliterator": "^2.0.1" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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==", + "devOptional": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nan": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", + "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", + "optional": true + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/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/node-gyp/node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-gyp/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/node-gyp/node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nodemon": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.2.tgz", + "integrity": "sha512-/Ib/kloefDy+N0iRTxIUzyGcdW9lzlnca2Jsa5w73bs3npXjg+WInmiX6VY13mIb6SykkthYX/U5t0ukryGqBw==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", + "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "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/normalize-url": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", + "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-bundled": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-check-updates": { + "version": "16.14.20", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.20.tgz", + "integrity": "sha512-sYbIhun4DrjO7NFOTdvs11nCar0etEhZTsEjL47eM0TuiGMhmYughRCxG2SpGRmGAQ7AkwN7bw2lWzoE7q6yOQ==", + "dev": true, + "dependencies": { + "@types/semver-utils": "^1.1.1", + "chalk": "^5.3.0", + "cli-table3": "^0.6.3", + "commander": "^10.0.1", + "fast-memoize": "^2.5.2", + "find-up": "5.0.0", + "fp-and-or": "^0.1.4", + "get-stdin": "^8.0.0", + "globby": "^11.0.4", + "hosted-git-info": "^5.1.0", + "ini": "^4.1.1", + "js-yaml": "^4.1.0", + "json-parse-helpfulerror": "^1.0.3", + "jsonlines": "^0.1.1", + "lodash": "^4.17.21", + "make-fetch-happen": "^11.1.1", + "minimatch": "^9.0.3", + "p-map": "^4.0.0", + "pacote": "15.2.0", + "parse-github-url": "^1.0.2", + "progress": "^2.0.3", + "prompts-ncu": "^3.0.0", + "rc-config-loader": "^4.1.3", + "remote-git-tags": "^3.0.0", + "rimraf": "^5.0.5", + "semver": "^7.5.4", + "semver-utils": "^1.1.4", + "source-map-support": "^0.5.21", + "spawn-please": "^2.0.2", + "strip-ansi": "^7.1.0", + "strip-json-comments": "^5.0.1", + "untildify": "^4.0.0", + "update-notifier": "^6.0.2" + }, + "bin": { + "ncu": "build/src/bin/cli.js", + "npm-check-updates": "build/src/bin/cli.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/npm-check-updates/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/npm-check-updates/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/npm-check-updates/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm-check-updates/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/npm-check-updates/node_modules/glob": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-check-updates/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-check-updates/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-check-updates/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm-check-updates/node_modules/rimraf": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm-check-updates/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/npm-check-updates/node_modules/strip-json-comments": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", + "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.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-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-packlist": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", + "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", + "dev": true, + "dependencies": { + "ignore-walk": "^6.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz", + "integrity": "sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==", + "dev": true, + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", + "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "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/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/oauth2-mock-server": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/oauth2-mock-server/-/oauth2-mock-server-7.1.2.tgz", + "integrity": "sha512-xUg/YOTcMRe8W+q2jphecq1fB1BAjlAPbeeA9lvqwGaQSPJKxI2e8JUnDXHrrKGNJAVXQdHgE/9h4RpCtOfYOA==", + "dev": true, + "dependencies": { + "basic-auth": "^2.0.1", + "cors": "^2.8.5", + "express": "^4.18.2", + "is-plain-object": "^5.0.0", + "jose": "^5.3.0" + }, + "bin": { + "oauth2-mock-server": "dist/oauth2-mock-server.js" + }, + "engines": { + "node": "^18.12 || ^20 || ^22", + "yarn": "^1.15.2" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==", + "dev": true + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "devOptional": 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/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", + "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "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/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/p-locate/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/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-8.1.1.tgz", + "integrity": "sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==", + "dev": true, + "dependencies": { + "got": "^12.1.0", + "registry-auth-token": "^5.0.1", + "registry-url": "^6.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pacote": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", + "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", + "dev": true, + "dependencies": { + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^5.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^1.3.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-github-url": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", + "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", + "dev": true, + "bin": { + "parse-github-url": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "devOptional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path2d": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/path2d/-/path2d-0.2.0.tgz", + "integrity": "sha512-KdPAykQX6kmLSOO6Jpu2KNcCED7CKjmaBNGGNuctOsG0hgYO1OdYQaan6cYXJiG0WmXOwZZPILPBimu5QAIw3A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "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/pdfjs-dist": { + "version": "4.3.136", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-4.3.136.tgz", + "integrity": "sha512-gzfnt1qc4yA+U46golPGYtU4WM2ssqP2MvFjKga8GEKOrEnzRPrA/9jogLLPYHiA3sGBPJ+p7BdAq+ytmw3jEg==", + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "canvas": "^2.11.2", + "path2d": "^0.2.0" + } + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/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/pino": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.1.0.tgz", + "integrity": "sha512-qUcgfrlyOtjwhNLdbhoL7NR4NkHjzykAPw0V2QLFbvu/zss29h4NkRnibyFzBrNCbzCOY3WZ9hhKSwfOkNggYA==", + "dev": true, + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dev": true, + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "dev": true + }, + "node_modules/pkg-types": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.1.1.tgz", + "integrity": "sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==", + "dev": true, + "dependencies": { + "confbox": "^0.1.7", + "mlly": "^1.7.0", + "pathe": "^1.1.2" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-lit": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/postcss-lit/-/postcss-lit-1.1.1.tgz", + "integrity": "sha512-zbOUUDmnHj9y/FINVARaSKE/gtPDpn/qT/B26NzQRFK9Z0yB3tUYnyn6PbSlNndKu3RnaTB8Q9bVO9UJwd/omg==", + "dev": true, + "dependencies": { + "@babel/generator": "^7.16.5", + "@babel/parser": "^7.16.2", + "@babel/traverse": "^7.16.0", + "lilconfig": "^2.0.6" + }, + "peerDependencies": { + "postcss": "^8.3.11" + } + }, + "node_modules/postcss-lit/node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-7.0.0.tgz", + "integrity": "sha512-ovehqRNVCpuFzbXoTb4qLtyzK3xn3t/CUBxOs8LsnQjQrShaB4lKiHoVqY8ANaC0hBMHq5QVWk77rwGklFUDrg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-safe-parser" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": ">=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz", + "integrity": "sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-sorting": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-8.0.2.tgz", + "integrity": "sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==", + "dev": true, + "peerDependencies": { + "postcss": "^8.4.20" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "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/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/pretty-quick": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-4.0.0.tgz", + "integrity": "sha512-M+2MmeufXb/M7Xw3Afh1gxcYpj+sK0AxEfnfF958ktFeAyi5MsKY5brymVURQLgPLV1QaF5P4pb2oFJ54H3yzQ==", + "dev": true, + "dependencies": { + "execa": "^5.1.1", + "find-up": "^5.0.0", + "ignore": "^5.3.0", + "mri": "^1.2.0", + "picocolors": "^1.0.0", + "picomatch": "^3.0.1", + "tslib": "^2.6.2" + }, + "bin": { + "pretty-quick": "lib/cli.mjs" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "prettier": "^3.0.0" + } + }, + "node_modules/pretty-quick/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/pretty-quick/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-quick/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/pretty-quick/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-quick/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pretty-quick/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-quick/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-quick/node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pretty-quick/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/pretty-quick/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prompts-ncu": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/prompts-ncu/-/prompts-ncu-3.0.0.tgz", + "integrity": "sha512-qyz9UxZ5MlPKWVhWrCmSZ1ahm2GVYdjLb8og2sg0IPth1KRuhcggHGuijz0e41dkx35p1t1q3GRISGH7QGALFA==", + "dev": true, + "dependencies": { + "kleur": "^4.0.1", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "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-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "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/pupa": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-3.1.0.tgz", + "integrity": "sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==", + "dev": true, + "dependencies": { + "escape-goat": "^4.0.0" + }, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/qr-creator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/qr-creator/-/qr-creator-1.0.0.tgz", + "integrity": "sha512-C0cqfbS1P5hfqN4NhsYsUXePlk9BO+a45bAQ3xLYjBL3bOIFzoVEjs79Fado9u9BPBD3buHi3+vY+C8tHh4qMQ==" + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "dev": true + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", + "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, + "node_modules/rc-config-loader/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/read-package-json": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", + "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", + "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", + "dev": true, + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "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/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/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/read-package-json/node_modules/glob": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz", + "integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/read-package-json/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "devOptional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/registry-auth-token": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.0.2.tgz", + "integrity": "sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==", + "dev": true, + "dependencies": { + "@pnpm/npm-conf": "^2.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/registry-url": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-6.0.1.tgz", + "integrity": "sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==", + "dev": true, + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/remote-git-tags": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remote-git-tags/-/remote-git-tags-3.0.0.tgz", + "integrity": "sha512-C9hAO4eoEsX+OXA4rla66pXZQ+TLQ8T9dttgQj18yuKlPMTVkIkdYXvlMC55IuUsIkV6DpmQYi10JKFLaU+l7w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true, + "engines": { + "node": ">=0.10.5" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "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/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/responselike": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", + "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", + "dev": true, + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", + "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/ret": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", + "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "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/rfdc": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", + "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "devOptional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", + "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", + "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.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "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/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "devOptional": 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/safe-regex-test": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", + "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", + "dev": true, + "dependencies": { + "ret": "~0.4.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "devOptional": true + }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "dev": true + }, + "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==", + "devOptional": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz", + "integrity": "sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver-utils": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/semver-utils/-/semver-utils-1.1.4.tgz", + "integrity": "sha512-EjnoLE5OGmDAVV/8YDoN5KiajNadjzIp9BAHOhYeQHt7j0UWxjmgsx4YD48wp4Ue1Qogq38F1GNUJNqF1kKKxA==", + "dev": true + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "devOptional": true + }, + "node_modules/set-cookie-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", + "integrity": "sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==", + "dev": true + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "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/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "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/sigstore": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.9.0.tgz", + "integrity": "sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==", + "dev": true, + "dependencies": { + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/sign": "^1.0.0", + "@sigstore/tuf": "^1.0.3", + "make-fetch-happen": "^11.0.1" + }, + "bin": { + "sigstore": "bin/sigstore.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/simple-get/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "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/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "dev": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sonic-boom": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.0.1.tgz", + "integrity": "sha512-hTSD/6JMLyT4r9zeof6UtuBDpjJ9sO08/nmS5djaA9eozT9oOlNdpXSnzcgj4FTqpk3nkLrs61l4gip9r1HCrQ==", + "dev": true, + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "node_modules/spawn-please": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/spawn-please/-/spawn-please-2.0.2.tgz", + "integrity": "sha512-KM8coezO6ISQ89c1BzyWNtcn2V2kAVtwIXd3cN/V5a0xPYc1F/vydrRc01wsKFEQ/p+V1a4sw4z2yMITIXrgGw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.18", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz", + "integrity": "sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==", + "dev": true + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/ssri": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "dev": true, + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "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_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "devOptional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/string-width": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz", + "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==", + "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "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-cjs/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/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/string.prototype.trim": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "devOptional": 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-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", + "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", + "dev": true, + "dependencies": { + "js-tokens": "^9.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/stylelint": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.6.1.tgz", + "integrity": "sha512-yNgz2PqWLkhH2hw6X9AweV9YvoafbAD5ZsFdKN9BvSDVwGvPh+AUIrn7lYwy1S7IHmtFin75LLfX1m0D2tHu8Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "dependencies": { + "@csstools/css-parser-algorithms": "^2.6.3", + "@csstools/css-tokenizer": "^2.3.1", + "@csstools/media-query-list-parser": "^2.1.11", + "@csstools/selector-specificity": "^3.1.1", + "@dual-bundle/import-meta-resolve": "^4.1.0", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^9.0.0", + "css-functions-list": "^3.2.2", + "css-tree": "^2.3.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^9.0.0", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.3.1", + "ignore": "^5.3.1", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.31.0", + "mathml-tag-names": "^2.1.3", + "meow": "^13.2.0", + "micromatch": "^4.0.7", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.1", + "postcss": "^8.4.38", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^7.0.0", + "postcss-selector-parser": "^6.1.0", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^7.1.0", + "supports-hyperlinks": "^3.0.0", + "svg-tags": "^1.0.0", + "table": "^6.8.2", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "stylelint": "bin/stylelint.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/stylelint-config-recess-order": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.0.1.tgz", + "integrity": "sha512-rKbGkoa3h0rINrGln9TFVowvSCLgPJC5O0EuPiqlqWcJMb1lImEtXktcjFCVz+hwtSUiHD3ijJc3vP9muFOgJg==", + "dev": true, + "dependencies": { + "stylelint-order": "^6.0.4" + }, + "peerDependencies": { + "stylelint": ">=16" + } + }, + "node_modules/stylelint-config-recommended": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-14.0.0.tgz", + "integrity": "sha512-jSkx290CglS8StmrLp2TxAppIajzIBZKYm3IxT89Kg6fGlxbPiTiyH9PS5YUuVAFwaJLl1ikiXX0QWjI0jmgZQ==", + "dev": true, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.0.0" + } + }, + "node_modules/stylelint-config-standard": { + "version": "36.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-36.0.0.tgz", + "integrity": "sha512-3Kjyq4d62bYFp/Aq8PMKDwlgUyPU4nacXsjDLWJdNPRUgpuxALu1KnlAHIj36cdtxViVhXexZij65yM0uNIHug==", + "dev": true, + "dependencies": { + "stylelint-config-recommended": "^14.0.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.1.0" + } + }, + "node_modules/stylelint-order": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-6.0.4.tgz", + "integrity": "sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==", + "dev": true, + "dependencies": { + "postcss": "^8.4.32", + "postcss-sorting": "^8.0.2" + }, + "peerDependencies": { + "stylelint": "^14.0.0 || ^15.0.0 || ^16.0.1" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/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/stylelint/node_modules/file-entry-cache": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.0.0.tgz", + "integrity": "sha512-6MgEugi8p2tiUhqO7GnPsmbCCzj0YRCwwaTbpGRyKZesjRSzkqkAE9fPp7V2yMs5hwfgbQLgdvSSkGNg1s5Uvw==", + "dev": true, + "dependencies": { + "flat-cache": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", + "dev": true, + "dependencies": { + "flatted": "^3.3.1", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/stylelint/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/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stylelint/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/stylelint/node_modules/string-width/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/stylelint/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/stylelint/node_modules/strip-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/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/supports-hyperlinks": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz", + "integrity": "sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/table": { + "version": "6.8.2", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.2.tgz", + "integrity": "sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/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/table/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/table/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/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/table/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/table/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/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "devOptional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "devOptional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "devOptional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/thread-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.0.1.tgz", + "integrity": "sha512-O5O/RbdV3CjhtwFa4slXvrb/26lzsf4/NMts3QFIRniIsi+584QSneJXaHXasqcZO+B7pWZkr+4h9knhnAQevg==", + "dev": true, + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/tinybench": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz", + "integrity": "sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "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/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toad-cache": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", + "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "optional": true + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "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/ts-lit-plugin": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ts-lit-plugin/-/ts-lit-plugin-2.0.2.tgz", + "integrity": "sha512-DPXlVxhjWHxg8AyBLcfSYt2JXgpANV1ssxxwjY98o26gD8MzeiM68HFW9c2VeDd1CjoR3w7B/6/uKxwBQe+ioA==", + "dev": true, + "dependencies": { + "lit-analyzer": "^2.0.1", + "web-component-analyzer": "^2.0.0" + } + }, + "node_modules/ts-simple-type": { + "version": "2.0.0-next.0", + "resolved": "https://registry.npmjs.org/ts-simple-type/-/ts-simple-type-2.0.0-next.0.tgz", + "integrity": "sha512-A+hLX83gS+yH6DtzNAhzZbPfU+D9D8lHlTSd7GeoMRBjOt3GRylDqLTYbdmjA4biWvq2xSfpqfIDj2l0OA/BVg==", + "dev": true + }, + "node_modules/tsconfck": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.0.3.tgz", + "integrity": "sha512-4t0noZX9t6GcPTfBAbIbbIU4pfpCwh0ueq3S4O/5qXI1VwK1outmxhe9dOiEWqMz3MW2LKgDTpqWV+37IWuVbA==", + "dev": true, + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.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/tsx": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.11.0.tgz", + "integrity": "sha512-vzGGELOgAupsNVssAmZjbUDfdm/pWP4R+Kg8TVdsonxbXk0bEpE1qh0yV6/QxUVXaVlNemgcPajGdJJ82n3stg==", + "dev": true, + "dependencies": { + "esbuild": "~0.20.2", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tuf-js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", + "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", + "dev": true, + "dependencies": { + "@tufjs/models": "1.0.4", + "debug": "^4.3.4", + "make-fetch-happen": "^11.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "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/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.11.0.tgz", + "integrity": "sha512-ZKe3yHF/IS/kCUE4CGE3UgtK+Q7yRk1e9kwEI0rqm9XxMTd9P1eHe0LVVtrZ3oFuIQ2unJ9Xn0vTsLApzJ3aPw==", + "dev": true, + "dependencies": { + "@typescript-eslint/eslint-plugin": "7.11.0", + "@typescript-eslint/parser": "7.11.0", + "@typescript-eslint/utils": "7.11.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ufo": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", + "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "dev": true + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dev": true, + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "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/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-6.0.2.tgz", + "integrity": "sha512-EDxhTEVPZZRLWYcJ4ZXjGFN0oP7qYvbXWzEgRm/Yql4dHX5wDbvh89YHP6PK1lzZJYrMtXUuZZz8XGK+U6U1og==", + "dev": true, + "dependencies": { + "boxen": "^7.0.0", + "chalk": "^5.0.1", + "configstore": "^6.0.0", + "has-yarn": "^3.0.0", + "import-lazy": "^4.0.0", + "is-ci": "^3.0.1", + "is-installed-globally": "^0.4.0", + "is-npm": "^6.0.0", + "is-yarn-global": "^0.4.0", + "latest-version": "^7.0.0", + "pupa": "^3.1.0", + "semver": "^7.3.7", + "semver-diff": "^4.0.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/yeoman/update-notifier?sponsor=1" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "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" } }, - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==" }, - "@types/node": { - "version": "18.16.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.2.tgz", - "integrity": "sha512-GQW/JL/5Fz/0I8RpeBG9lKp0+aNcXEaVL71c0D2Q0QHDTFvlYKT7an0onCUXj85anv7b4/WesqdfchLc0jtsCg==" + "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==", + "devOptional": true }, - "@types/resolve": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", - "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", - "requires": { - "@types/node": "*" + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" } }, - "@types/trusted-types": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz", - "integrity": "sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==" + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } }, - "@webcomponents/webcomponentsjs": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.5.0.tgz", - "integrity": "sha512-C0l51MWQZ9kLzcxOZtniOMohpIFdCLZum7/TEHv3XWFc1Fvt5HCpbSX84x8ltka/JuNKcuiDnxXFkiB2gaePcg==" + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "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/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } }, - "bootstrap": { - "version": "5.0.0-beta2", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.0-beta2.tgz", - "integrity": "sha512-e+uPbPHqTQWKyCX435uVlOmgH9tUt0xtjvyOC7knhKgOS643BrQKuTo+KecGpPV7qlmOyZgCfaM4xxPWtDEN/g==" + "node_modules/vite": { + "version": "5.2.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", + "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", + "dev": true, + "dependencies": { + "esbuild": "^0.20.1", + "postcss": "^8.4.38", + "rollup": "^4.13.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 + } + } }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/vite-node": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dev": true, - "requires": { - "balanced-match": "^1.0.0" + "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" } }, - "builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==" + "node_modules/vite-plugin-static-copy": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-1.0.5.tgz", + "integrity": "sha512-02k0Rox+buYdEOfeilKZSgs1gXfPf9RjVztZEIYZgVIxjsVZi6AXssjzdi+qW6zYt00d3bq+tpP2voVXN2fKLw==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.3", + "fast-glob": "^3.2.11", + "fs-extra": "^11.1.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0" + } }, - "classnames": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", - "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" + "node_modules/vite-tsconfig-paths": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz", + "integrity": "sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "node_modules/vitest": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "dev": true, + "dependencies": { + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.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.3", + "vite": "^5.0.0", + "vite-node": "1.6.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.6.0", + "@vitest/ui": "1.6.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/vscode-css-languageservice": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-4.3.0.tgz", + "integrity": "sha512-BkQAMz4oVHjr0oOAz5PdeE72txlLQK7NIwzmclfr+b6fj6I8POwB+VoXvrZLTbWt9hWRgfvgiQRkh5JwrjPJ5A==", + "dev": true, + "dependencies": { + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "3.16.0-next.2", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.2" + } + }, + "node_modules/vscode-html-languageservice": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-3.1.0.tgz", + "integrity": "sha512-QAyRHI98bbEIBCqTzZVA0VblGU40na0txggongw5ZgTj9UVsVk5XbLT16O9OTcbqBGSqn0oWmFDNjK/XGIDcqg==", + "dev": true, + "dependencies": { + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "3.16.0-next.2", + "vscode-nls": "^4.1.2", + "vscode-uri": "^2.1.2" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==", "dev": true }, - "core-js": { - "version": "3.11.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.11.2.tgz", - "integrity": "sha512-3tfrrO1JpJSYGKnd9LKTBPqgUES/UYiCzMKeqwR1+jF16q4kD1BY2NvqkfuzXwQ6+CIWm55V9cjD7PQd+hijdw==" + "node_modules/vscode-languageserver-types": { + "version": "3.16.0-next.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz", + "integrity": "sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q==", + "dev": true }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" + "node_modules/vscode-nls": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", + "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==", + "dev": true }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==" + "node_modules/vscode-uri": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", + "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==", + "dev": true }, - "feather-icons": { - "version": "4.28.0", - "resolved": "https://registry.npmjs.org/feather-icons/-/feather-icons-4.28.0.tgz", - "integrity": "sha512-gRdqKESXRBUZn6Nl0VBq2wPHKRJgZz7yblrrc2lYsS6odkNFDnA4bqvrlEVRUPjE1tFax+0TdbJKZ31ziJuzjg==", - "requires": { - "classnames": "^2.2.5", - "core-js": "^3.1.3" + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" } }, - "flexboxgrid": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/flexboxgrid/-/flexboxgrid-6.3.1.tgz", - "integrity": "sha1-6ZiYr8B7cEdyK7galYpfuk1OIP0=" + "node_modules/web-component-analyzer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/web-component-analyzer/-/web-component-analyzer-2.0.0.tgz", + "integrity": "sha512-UEvwfpD+XQw99sLKiH5B1T4QwpwNyWJxp59cnlRwFfhUW6JsQpw5jMeMwi7580sNou8YL3kYoS7BWLm+yJ/jVQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.2", + "ts-simple-type": "2.0.0-next.0", + "typescript": "~5.2.0", + "yargs": "^17.7.2" + }, + "bin": { + "wca": "cli.js", + "web-component-analyzer": "cli.js" + } }, - "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/web-component-analyzer/node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "optional": true }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "optional": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } }, - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "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, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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/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" + } }, - "is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "requires": { - "builtin-modules": "^3.3.0" + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "devOptional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", - "requires": { - "has": "^1.0.3" + "node_modules/wide-align/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==", + "devOptional": true + }, + "node_modules/wide-align/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==", + "devOptional": true, + "engines": { + "node": ">=8" } }, - "is-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", - "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==" + "node_modules/wide-align/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==", + "devOptional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } }, - "is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", "dev": true, - "requires": { - "@types/estree": "*" + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lit": { - "version": "2.0.0-rc.2", - "resolved": "https://registry.npmjs.org/lit/-/lit-2.0.0-rc.2.tgz", - "integrity": "sha512-BOCuoJR04WaTV8UqTKk09cNcQA10Aq2LCcBOiHuF7TzWH5RNDsbCBP5QM9sLBSotGTXbDug/gFO08jq6TbyEtw==", - "requires": { - "@lit/reactive-element": "^1.0.0-rc.2", - "lit-element": "^3.0.0-rc.2", - "lit-html": "^2.0.0-rc.3" + "node_modules/widest-line/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" }, - "dependencies": { - "lit-element": { - "version": "3.0.0-rc.2", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.0.0-rc.2.tgz", - "integrity": "sha512-2Z7DabJ3b5K+p5073vFjMODoaWqy5PIaI4y6ADKm+fCGc8OnX9fU9dMoUEBZjFpd/bEFR9PBp050tUtBnT9XTQ==", - "requires": { - "@lit/reactive-element": "^1.0.0-rc.2", - "lit-html": "^2.0.0-rc.3" - } - }, - "lit-html": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.0.0-rc.3.tgz", - "integrity": "sha512-Y6P8LlAyQuqvzq6l/Nc4z5/P5M/rVLYKQIRxcNwSuGajK0g4kbcBFQqZmgvqKG+ak+dHZjfm2HUw9TF5N/pkCw==", - "requires": { - "@types/trusted-types": "^1.0.1" - } - } + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "lit-html": { - "version": "2.0.0-rc.3", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.0.0-rc.3.tgz", - "integrity": "sha512-Y6P8LlAyQuqvzq6l/Nc4z5/P5M/rVLYKQIRxcNwSuGajK0g4kbcBFQqZmgvqKG+ak+dHZjfm2HUw9TF5N/pkCw==", - "requires": { - "@types/trusted-types": "^1.0.1" + "node_modules/widest-line/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/widest-line/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" } }, - "magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", + "node_modules/widest-line/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, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "marked": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.3.tgz", - "integrity": "sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA==" + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, - "requires": { - "brace-expansion": "^2.0.1" + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "moment": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", - "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" + "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" + } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "node_modules/wrap-ansi-cjs/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, - "requires": { - "wrappy": "1" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "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 }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "node_modules/wrap-ansi-cjs/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" + } }, - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "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" } }, - "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "requires": { - "fsevents": "~2.3.2" + "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" } }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "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" + } }, - "wrappy": { + "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==", + "devOptional": true + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "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/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "devOptional": true + }, + "node_modules/yaml": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", + "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/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/yargs/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/yargs/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/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" + } } } } diff --git a/package.json b/package.json index 3f31223..5457730 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,107 @@ { - "name": "ros-dashboard", + "name": "@radically-open-security/dashboard", + "private": true, + "version": "1.0.0", + "type": "module", "scripts": { - "build": "./node_modules/.bin/rollup -c" + "##### BUILD #########": "###################################", + "build": "tsc && vite build", + "build:prod": "NODE_ENV=production tsc && vite build", + "build:test": "tsc --noEmit", + "build:ci": "vite build --mode test", + "##### DEV ###########": "###################################", + "dev": "vite", + "dev:preview": "vite preview", + "dev:mock": "tsx ./mock/run-mock.ts", + "dev:mock:oidc": "tsx ./mock/mock-oidc-server.ts", + "dev:mock:gitlab": "tsx ./mock/mock-gitlab-server.ts", + "##### TEST ##########": "###################################", + "test": "vitest run", + "test:watch": "vitest watch --ui", + "test:coverage": "vitest run --coverage", + "##### LINT ##########": "###################################", + "lint": "npm run lint:eslint && npm run lint:style && npm run lint:prettier", + "lint:fix": "npm run lint:eslint:fix && npm run lint:style:fix && npm run lint:prettier:fix", + "lint:staged": "lint-staged", + "lint:eslint": "eslint .", + "lint:eslint:fix": "eslint --fix --ext .ts,.tsx .", + "lint:style": "stylelint \"src/**/*.{css,ts}\"", + "lint:style:fix": "stylelint --fix \"src/**/*.{css,ts}\"", + "lint:prettier": "prettier --check \"{,!(node_modules|dist|build|coverage)/**/}*.{js,ts,json,svg,html}\"", + "lint:prettier:fix": "prettier --write \"{,!(node_modules|dist|build|coverage)/**/}*.{js,ts,json,svg,html}\"", + "lint:commit": "commitlint", + "##### GENERATE ######": "###################################", + "generate:oidc": "node ./node_modules/@axa-fr/oidc-client/bin/copy-service-worker-files.mjs public", + "##### UTILS #########": "###################################", + "commit": "cz", + "nodemon": "nodemon", + "check-updates": "ncu", + "check-updates:upgrade": "ncu -u", + "check-updates:interactive": "ncu -i", + "##### NPM ###########": "###################################", + "prepare": "husky" }, "dependencies": { - "@popperjs/core": "2.9.1", - "@rollup/plugin-node-resolve": "^13.0.6", - "@webcomponents/webcomponentsjs": "^2.5.0", - "bootstrap": "5.0.0-beta2", - "feather-icons": "4.28.0", - "flexboxgrid": "^6.3.1", - "lit": "^2.0.0-rc.2", - "lit-html": "^2.0.0-rc.3", - "marked": "^2.0.3", - "moment": "^2.29.1", - "rollup": "^2.68.0" - }, - "snowpack": { - "install": [ - "moment", - "marked", - "lit", - "lit-html/directives/repeat", - "lit-html/directives/class-map", - "lit-html/directives/style-map", - "bootstrap", - "@webcomponents/webcomponentsjs" - ] + "@axa-fr/oidc-client": "7.22.6", + "@lit-labs/preact-signals": "1.0.2", + "@lit-labs/router": "0.1.3", + "@lit/context": "1.1.1", + "@shoelace-style/shoelace": "2.15.1", + "@tanstack/lit-table": "8.18.0", + "date-fns": "3.6.0", + "lit": "3.1.3", + "marked": "12.0.2", + "pdfjs-dist": "4.3.136", + "urlpattern-polyfill": "10.0.0" }, "devDependencies": { - "@rollup/plugin-commonjs": "^24.1.0" + "@commitlint/cli": "19.3.0", + "@commitlint/config-conventional": "19.2.2", + "@eslint/eslintrc": "3.1.0", + "@eslint/js": "9.3.0", + "@fastify/cors": "9.0.1", + "@fastify/http-proxy": "9.5.0", + "@types/node": "20.12.13", + "@typescript-eslint/eslint-plugin": "7.11.0", + "@typescript-eslint/parser": "7.11.0", + "@vitest/coverage-v8": "1.6.0", + "commitizen": "4.3.0", + "concurrently": "8.2.2", + "cz-conventional-changelog": "3.3.0", + "eslint": "8.57.0", + "eslint-define-config": "2.1.0", + "eslint-import-resolver-typescript": "3.6.1", + "eslint-plugin-import": "2.29.1", + "eslint-plugin-lit": "1.14.0", + "eslint-plugin-wc": "2.1.0", + "fastify": "4.27.0", + "husky": "9.0.11", + "lint-staged": "15.2.5", + "nodemon": "3.1.2", + "npm-check-updates": "16.14.20", + "oauth2-mock-server": "7.1.2", + "postcss-lit": "1.1.1", + "prettier": "3.2.5", + "pretty-quick": "4.0.0", + "stylelint": "16.6.1", + "stylelint-config-recess-order": "5.0.1", + "stylelint-config-standard": "36.0.0", + "ts-lit-plugin": "2.0.2", + "tsx": "4.11.0", + "typescript": "5.4.5", + "typescript-eslint": "7.11.0", + "vite": "5.2.12", + "vite-plugin-static-copy": "1.0.5", + "vite-tsconfig-paths": "4.3.2", + "vitest": "1.6.0" + }, + "volta": { + "node": "20.12.2", + "npm": "10.5.2" + }, + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } } } diff --git a/public/OidcServiceWorker.js b/public/OidcServiceWorker.js new file mode 100644 index 0000000..18a1752 --- /dev/null +++ b/public/OidcServiceWorker.js @@ -0,0 +1,873 @@ +const scriptFilename = "OidcTrustedDomains.js"; +const acceptAnyDomainToken = "*"; +const TOKEN = { + REFRESH_TOKEN: "REFRESH_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER", + ACCESS_TOKEN: "ACCESS_TOKEN_SECURED_BY_OIDC_SERVICE_WORKER", + NONCE_TOKEN: "NONCE_SECURED_BY_OIDC_SERVICE_WORKER", + CODE_VERIFIER: "CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER" +}; +const TokenRenewMode = { + access_token_or_id_token_invalid: "access_token_or_id_token_invalid", + access_token_invalid: "access_token_invalid", + id_token_invalid: "id_token_invalid" +}; +const openidWellknownUrlEndWith = "/.well-known/openid-configuration"; +function normalizeUrl(url) { + try { + return new URL(url).toString(); + } catch (error) { + console.error(`Failed to normalize url: ${url}`); + return url; + } +} +function checkDomain(domains, endpoint) { + if (!endpoint) { + return; + } + const domain = domains.find((domain2) => { + var _a; + let testable; + if (typeof domain2 === "string") { + testable = new RegExp(`^${domain2}`); + } else { + testable = domain2; + } + return (_a = testable.test) == null ? void 0 : _a.call(testable, endpoint); + }); + if (!domain) { + throw new Error( + "Domain " + endpoint + " is not trusted, please add domain in " + scriptFilename + ); + } +} +const getDomains = (trustedDomain, type) => { + if (Array.isArray(trustedDomain)) { + return trustedDomain; + } + return trustedDomain[`${type}Domains`] ?? trustedDomain.domains ?? []; +}; +const getCurrentDatabaseDomain = (database2, url, trustedDomains2) => { + var _a; + if (url.endsWith(openidWellknownUrlEndWith)) { + return null; + } + for (const [key, currentDatabase] of Object.entries(database2)) { + const oidcServerConfiguration = currentDatabase.oidcServerConfiguration; + if (!oidcServerConfiguration) { + continue; + } + if (oidcServerConfiguration.tokenEndpoint && url === normalizeUrl(oidcServerConfiguration.tokenEndpoint)) { + continue; + } + if (oidcServerConfiguration.revocationEndpoint && url === normalizeUrl(oidcServerConfiguration.revocationEndpoint)) { + continue; + } + const trustedDomain = trustedDomains2 == null ? [] : trustedDomains2[key]; + const domains = getDomains(trustedDomain, "accessToken"); + const domainsToSendTokens = oidcServerConfiguration.userInfoEndpoint ? [normalizeUrl(oidcServerConfiguration.userInfoEndpoint), ...domains] : [...domains]; + let hasToSendToken = false; + if (domainsToSendTokens.find((f) => f === acceptAnyDomainToken)) { + hasToSendToken = true; + } else { + for (let i = 0; i < domainsToSendTokens.length; i++) { + let domain = domainsToSendTokens[i]; + if (typeof domain === "string") { + domain = new RegExp(`^${domain}`); + } + if ((_a = domain.test) == null ? void 0 : _a.call(domain, url)) { + hasToSendToken = true; + break; + } + } + } + if (hasToSendToken) { + if (!currentDatabase.tokens) { + return null; + } + return currentDatabase; + } + } + return null; +}; +function serializeHeaders(headers) { + const headersObj = {}; + for (const key of headers.keys()) { + if (headers.has(key)) { + headersObj[key] = headers.get(key); + } + } + return headersObj; +} +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +function countLetter(str, find) { + return str.split(find).length - 1; +} +const parseJwt = (payload) => { + return JSON.parse( + b64DecodeUnicode(payload.replaceAll(/-/g, "+").replaceAll(/_/g, "/")) + ); +}; +function b64DecodeUnicode(str) { + return decodeURIComponent( + Array.prototype.map.call( + atob(str), + (c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2) + ).join("") + ); +} +function computeTimeLeft(refreshTimeBeforeTokensExpirationInSecond, expiresAt) { + const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3; + return Math.round( + expiresAt - refreshTimeBeforeTokensExpirationInSecond - currentTimeUnixSecond + ); +} +function isTokensValid(tokens) { + if (!tokens) { + return false; + } + return computeTimeLeft(0, tokens.expiresAt) > 0; +} +const extractTokenPayload = (token) => { + try { + if (!token) { + return null; + } + if (countLetter(token, ".") === 2) { + return parseJwt(token.split(".")[1]); + } else { + return null; + } + } catch (e) { + console.warn(e); + } + return null; +}; +const isTokensOidcValid = (tokens, nonce, oidcServerConfiguration) => { + if (tokens.idTokenPayload) { + const idTokenPayload = tokens.idTokenPayload; + if (idTokenPayload && oidcServerConfiguration.issuer !== idTokenPayload.iss) { + return { isValid: false, reason: `Issuer does not match (oidcServerConfiguration issuer) ${oidcServerConfiguration.issuer} !== (idTokenPayload issuer) ${idTokenPayload.iss}` }; + } + const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3; + if (idTokenPayload && idTokenPayload.exp && idTokenPayload.exp < currentTimeUnixSecond) { + return { isValid: false, reason: `Token expired at (idTokenPayload exp) ${idTokenPayload.exp} < (currentTimeUnixSecond) ${currentTimeUnixSecond}` }; + } + const timeInSevenDays = 60 * 60 * 24 * 7; + if (idTokenPayload && idTokenPayload.iat && idTokenPayload.iat + timeInSevenDays < currentTimeUnixSecond) { + return { isValid: false, reason: `Token is used from too long time (idTokenPayload iat + timeInSevenDays) ${idTokenPayload.iat + timeInSevenDays} < (currentTimeUnixSecond) ${currentTimeUnixSecond}` }; + } + if (idTokenPayload && nonce && idTokenPayload.nonce && idTokenPayload.nonce !== nonce) { + return { isValid: false, reason: `Nonce does not match (nonce) ${nonce} !== (idTokenPayload nonce) ${idTokenPayload.nonce}` }; + } + } + return { isValid: true, reason: "" }; +}; +function extractedIssueAt(tokens, accessTokenPayload, _idTokenPayload) { + if (!tokens.issued_at) { + if (accessTokenPayload && accessTokenPayload.iat) { + return accessTokenPayload.iat; + } else if (_idTokenPayload && _idTokenPayload.iat) { + return _idTokenPayload.iat; + } else { + const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3; + return currentTimeUnixSecond; + } + } else if (typeof tokens.issued_at == "string") { + return parseInt(tokens.issued_at, 10); + } + return tokens.issued_at; +} +function _hideTokens(tokens, currentDatabaseElement, configurationName) { + if (!tokens.issued_at) { + const currentTimeUnixSecond = (/* @__PURE__ */ new Date()).getTime() / 1e3; + tokens.issued_at = currentTimeUnixSecond; + } else if (typeof tokens.issued_at == "string") { + tokens.issued_at = parseInt(tokens.issued_at, 10); + } + const accessTokenPayload = extractTokenPayload(tokens.access_token); + const secureTokens = { + ...tokens, + accessTokenPayload + }; + if (currentDatabaseElement.hideAccessToken) { + secureTokens.access_token = TOKEN.ACCESS_TOKEN + "_" + configurationName; + } + tokens.accessTokenPayload = accessTokenPayload; + const oldTokens = currentDatabaseElement.tokens; + let id_token; + if (oldTokens != null && "id_token" in oldTokens && !("id_token" in tokens)) { + id_token = oldTokens.id_token; + } else { + id_token = tokens.id_token; + } + tokens.id_token = id_token; + let _idTokenPayload = null; + if (id_token) { + _idTokenPayload = extractTokenPayload(id_token); + tokens.idTokenPayload = _idTokenPayload != null ? { ..._idTokenPayload } : null; + if (_idTokenPayload && _idTokenPayload.nonce && currentDatabaseElement.nonce != null) { + const keyNonce = TOKEN.NONCE_TOKEN + "_" + currentDatabaseElement.configurationName; + _idTokenPayload.nonce = keyNonce; + } + secureTokens.idTokenPayload = _idTokenPayload; + } + if (tokens.refresh_token) { + secureTokens.refresh_token = TOKEN.REFRESH_TOKEN + "_" + configurationName; + } + tokens.issued_at = extractedIssueAt(tokens, accessTokenPayload, _idTokenPayload); + const expireIn = typeof tokens.expires_in == "string" ? parseInt(tokens.expires_in, 10) : tokens.expires_in; + const idTokenExpiresAt = _idTokenPayload && _idTokenPayload.exp ? _idTokenPayload.exp : Number.MAX_VALUE; + const accessTokenExpiresAt = accessTokenPayload && accessTokenPayload.exp ? accessTokenPayload.exp : tokens.issued_at + expireIn; + let expiresAt; + const tokenRenewMode = currentDatabaseElement.oidcConfiguration.token_renew_mode; + if (tokenRenewMode === TokenRenewMode.access_token_invalid) { + expiresAt = accessTokenExpiresAt; + } else if (tokenRenewMode === TokenRenewMode.id_token_invalid) { + expiresAt = idTokenExpiresAt; + } else { + expiresAt = idTokenExpiresAt < accessTokenExpiresAt ? idTokenExpiresAt : accessTokenExpiresAt; + } + secureTokens.expiresAt = expiresAt; + tokens.expiresAt = expiresAt; + const nonce = currentDatabaseElement.nonce ? currentDatabaseElement.nonce.nonce : null; + const { isValid, reason } = isTokensOidcValid( + tokens, + nonce, + currentDatabaseElement.oidcServerConfiguration + ); + if (!isValid) { + throw Error(`Tokens are not OpenID valid, reason: ${reason}`); + } + if (oldTokens != null && "refresh_token" in oldTokens && !("refresh_token" in tokens)) { + const refreshToken = oldTokens.refresh_token; + currentDatabaseElement.tokens = { + ...tokens, + refresh_token: refreshToken + }; + } else { + currentDatabaseElement.tokens = tokens; + } + currentDatabaseElement.status = "LOGGED_IN"; + return secureTokens; +} +const demonstratingProofOfPossessionNonceResponseHeader = "DPoP-Nonce"; +function hideTokens(currentDatabaseElement) { + const configurationName = currentDatabaseElement.configurationName; + return (response) => { + if (response.status !== 200) { + return response; + } + const newHeaders = new Headers(response.headers); + if (response.headers.has(demonstratingProofOfPossessionNonceResponseHeader)) { + currentDatabaseElement.demonstratingProofOfPossessionNonce = response.headers.get(demonstratingProofOfPossessionNonceResponseHeader); + newHeaders.delete(demonstratingProofOfPossessionNonceResponseHeader); + } + return response.json().then((tokens) => { + const secureTokens = _hideTokens(tokens, currentDatabaseElement, configurationName); + const body = JSON.stringify(secureTokens); + return new Response(body, { + status: response.status, + statusText: response.statusText, + headers: newHeaders + }); + }); + }; +} +function replaceCodeVerifier(codeVerifier, newCodeVerifier) { + const regex = /code_verifier=[A-Za-z0-9_-]+/i; + return codeVerifier.replace(regex, `code_verifier=${newCodeVerifier}`); +} +const extractConfigurationNameFromCodeVerifier = (chaine) => { + const regex = /CODE_VERIFIER_SECURED_BY_OIDC_SERVICE_WORKER_([^&\s]+)/; + const result = chaine.match(regex); + if (result && result.length > 1) { + return result[1]; + } else { + return null; + } +}; +const version = "7.22.6"; +function strToUint8(str) { + return new TextEncoder().encode(str); +} +function binToUrlBase64(bin) { + return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+/g, ""); +} +function utf8ToBinaryString(str) { + const escstr = encodeURIComponent(str); + return escstr.replace(/%([0-9A-F]{2})/g, function(match, p1) { + return String.fromCharCode(parseInt(p1, 16)); + }); +} +const uint8ToUrlBase64 = (uint8) => { + let bin = ""; + uint8.forEach(function(code) { + bin += String.fromCharCode(code); + }); + return binToUrlBase64(bin); +}; +function strToUrlBase64(str) { + return binToUrlBase64(utf8ToBinaryString(str)); +} +const defaultDemonstratingProofOfPossessionConfiguration = { + importKeyAlgorithm: { + name: "ECDSA", + namedCurve: "P-256", + hash: { name: "ES256" } + }, + signAlgorithm: { name: "ECDSA", hash: { name: "SHA-256" } }, + generateKeyAlgorithm: { + name: "ECDSA", + namedCurve: "P-256" + }, + digestAlgorithm: { name: "SHA-256" }, + jwtHeaderAlgorithm: "ES256" +}; +const sign = (w) => async (jwk, headers, claims, demonstratingProofOfPossessionConfiguration, jwtHeaderType = "dpop+jwt") => { + jwk = Object.assign({}, jwk); + headers.typ = jwtHeaderType; + headers.alg = demonstratingProofOfPossessionConfiguration.jwtHeaderAlgorithm; + switch (headers.alg) { + case "ES256": + headers.jwk = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y }; + break; + case "RS256": + headers.jwk = { kty: jwk.kty, n: jwk.n, e: jwk.e, kid: headers.kid }; + break; + default: + throw new Error("Unknown or not implemented JWS algorithm"); + } + const jws = { + // @ts-ignore + // JWT "headers" really means JWS "protected headers" + protected: strToUrlBase64(JSON.stringify(headers)), + // @ts-ignore + // JWT "claims" are really a JSON-defined JWS "payload" + payload: strToUrlBase64(JSON.stringify(claims)) + }; + const keyType = demonstratingProofOfPossessionConfiguration.importKeyAlgorithm; + const exportable = true; + const privileges = ["sign"]; + const privateKey = await w.crypto.subtle.importKey("jwk", jwk, keyType, exportable, privileges); + const data = strToUint8(`${jws.protected}.${jws.payload}`); + const signatureType = demonstratingProofOfPossessionConfiguration.signAlgorithm; + const signature = await w.crypto.subtle.sign(signatureType, privateKey, data); + jws.signature = uint8ToUrlBase64(new Uint8Array(signature)); + return `${jws.protected}.${jws.payload}.${jws.signature}`; +}; +var JWT = { sign }; +const generate = (w) => async (generateKeyAlgorithm) => { + const keyType = generateKeyAlgorithm; + const exportable = true; + const privileges = ["sign", "verify"]; + const key = await w.crypto.subtle.generateKey(keyType, exportable, privileges); + return await w.crypto.subtle.exportKey("jwk", key.privateKey); +}; +const neuter = (jwk) => { + const copy = Object.assign({}, jwk); + delete copy.d; + copy.key_ops = ["verify"]; + return copy; +}; +const EC = { + generate, + neuter +}; +const thumbprint = (w) => async (jwk, digestAlgorithm) => { + let sortedPub; + switch (jwk.kty) { + case "EC": + sortedPub = '{"crv":"CRV","kty":"EC","x":"X","y":"Y"}'.replace("CRV", jwk.crv).replace("X", jwk.x).replace("Y", jwk.y); + break; + case "RSA": + sortedPub = '{"e":"E","kty":"RSA","n":"N"}'.replace("E", jwk.e).replace("N", jwk.n); + break; + default: + throw new Error("Unknown or not implemented JWK type"); + } + const hash = await w.crypto.subtle.digest(digestAlgorithm, strToUint8(sortedPub)); + return uint8ToUrlBase64(new Uint8Array(hash)); +}; +var JWK = { thumbprint }; +const generateJwkAsync = (w) => async (generateKeyAlgorithm) => { + const jwk = await EC.generate(w)(generateKeyAlgorithm); + return jwk; +}; +const generateJwtDemonstratingProofOfPossessionAsync = (w) => (demonstratingProofOfPossessionConfiguration) => async (jwk, method = "POST", url, extrasClaims = {}) => { + const claims = { + // https://www.rfc-editor.org/rfc/rfc9449.html#name-concept + jti: btoa(guid()), + htm: method, + htu: url, + iat: Math.round(Date.now() / 1e3), + ...extrasClaims + }; + const kid = await JWK.thumbprint(w)(jwk, demonstratingProofOfPossessionConfiguration.digestAlgorithm); + const jwt = await JWT.sign(w)(jwk, { kid }, claims, demonstratingProofOfPossessionConfiguration); + return jwt; +}; +const guid = () => { + const guidHolder = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"; + const hex = "0123456789abcdef"; + let r = 0; + let guidResponse = ""; + for (let i = 0; i < 36; i++) { + if (guidHolder[i] !== "-" && guidHolder[i] !== "4") { + r = Math.random() * 16 | 0; + } + if (guidHolder[i] === "x") { + guidResponse += hex[r]; + } else if (guidHolder[i] === "y") { + r &= 3; + r |= 8; + guidResponse += hex[r]; + } else { + guidResponse += guidHolder[i]; + } + } + return guidResponse; +}; +const isDpop = (trustedDomain) => { + if (Array.isArray(trustedDomain)) { + return false; + } + return trustedDomain.demonstratingProofOfPossession ?? false; +}; +const getDpopConfiguration = (trustedDomain) => { + if (!isDpop(trustedDomain)) { + return null; + } + if (Array.isArray(trustedDomain)) { + return null; + } + return trustedDomain.demonstratingProofOfPossessionConfiguration ?? defaultDemonstratingProofOfPossessionConfiguration; +}; +const getDpopOnlyWhenDpopHeaderPresent = (trustedDomain) => { + if (!isDpop(trustedDomain)) { + return null; + } + if (Array.isArray(trustedDomain)) { + return null; + } + return trustedDomain.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent ?? true; +}; +function textEncodeLite(str) { + const buf = new ArrayBuffer(str.length); + const bufView = new Uint8Array(buf); + for (let i = 0; i < str.length; i++) { + bufView[i] = str.charCodeAt(i); + } + return bufView; +} +function base64urlOfHashOfASCIIEncodingAsync(code) { + return new Promise((resolve, reject) => { + crypto.subtle.digest("SHA-256", textEncodeLite(code)).then((buffer) => { + return resolve(uint8ToUrlBase64(new Uint8Array(buffer))); + }, (error) => reject(error)); + }); +} +if (typeof trustedTypes !== "undefined" && typeof trustedTypes.createPolicy == "function") { + trustedTypes.createPolicy("default", { + createScriptURL: function(url) { + if (url == scriptFilename) { + return url; + } else { + throw new Error("Untrusted script URL blocked: " + url); + } + } + }); +} +const _self = self; +_self.importScripts(scriptFilename); +const id = Math.round((/* @__PURE__ */ new Date()).getTime() / 1e3).toString(); +const keepAliveJsonFilename = "OidcKeepAliveServiceWorker.json"; +const handleInstall = (event) => { + console.log("[OidcServiceWorker] service worker installed " + id); + event.waitUntil(_self.skipWaiting()); +}; +const handleActivate = (event) => { + console.log("[OidcServiceWorker] service worker activated " + id); + event.waitUntil(_self.clients.claim()); +}; +const database = {}; +const getCurrentDatabasesTokenEndpoint = (database2, url) => { + const databases = []; + for (const [, value] of Object.entries(database2)) { + if (value.oidcServerConfiguration != null && url.startsWith(normalizeUrl(value.oidcServerConfiguration.tokenEndpoint))) { + databases.push(value); + } else if (value.oidcServerConfiguration != null && value.oidcServerConfiguration.revocationEndpoint && url.startsWith( + normalizeUrl(value.oidcServerConfiguration.revocationEndpoint) + )) { + databases.push(value); + } + } + return databases; +}; +const keepAliveAsync = async (event) => { + const originalRequest = event.request; + const isFromVanilla = originalRequest.headers.has("oidc-vanilla"); + const init = { status: 200, statusText: "oidc-service-worker" }; + const response = new Response("{}", init); + if (!isFromVanilla) { + const originalRequestUrl = new URL(originalRequest.url); + const minSleepSeconds = Number(originalRequestUrl.searchParams.get("minSleepSeconds")) || 240; + for (let i = 0; i < minSleepSeconds; i++) { + await sleep(1e3 + Math.floor(Math.random() * 1e3)); + const cache = await caches.open("oidc_dummy_cache"); + await cache.put(event.request, response.clone()); + } + } + return response; +}; +async function generateDpopAsync(originalRequest, currentDatabase, url, extrasClaims = {}) { + const headersExtras = serializeHeaders(originalRequest.headers); + if (currentDatabase && currentDatabase.demonstratingProofOfPossessionConfiguration && currentDatabase.demonstratingProofOfPossessionJwkJson && (!currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent || currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent && headersExtras["dpop"])) { + const dpopConfiguration = currentDatabase.demonstratingProofOfPossessionConfiguration; + const jwk = currentDatabase.demonstratingProofOfPossessionJwkJson; + headersExtras["dpop"] = await generateJwtDemonstratingProofOfPossessionAsync(self)(dpopConfiguration)(jwk, "POST", url, extrasClaims); + if (currentDatabase.demonstratingProofOfPossessionNonce != null) { + headersExtras["nonce"] = currentDatabase.demonstratingProofOfPossessionNonce; + } + } + return headersExtras; +} +const handleFetch = async (event) => { + const originalRequest = event.request; + const url = normalizeUrl(originalRequest.url); + if (url.includes(keepAliveJsonFilename)) { + event.respondWith(keepAliveAsync(event)); + return; + } + const currentDatabaseForRequestAccessToken = getCurrentDatabaseDomain( + database, + url, + trustedDomains + ); + if (currentDatabaseForRequestAccessToken && currentDatabaseForRequestAccessToken.tokens && currentDatabaseForRequestAccessToken.tokens.access_token) { + while (currentDatabaseForRequestAccessToken.tokens && !isTokensValid(currentDatabaseForRequestAccessToken.tokens)) { + await sleep(200); + } + let requestMode = originalRequest.mode; + if (originalRequest.mode !== "navigate" && currentDatabaseForRequestAccessToken.convertAllRequestsToCorsExceptNavigate) { + requestMode = "cors"; + } + let headers; + if (originalRequest.mode == "navigate" && !currentDatabaseForRequestAccessToken.setAccessTokenToNavigateRequests) { + headers = { + ...serializeHeaders(originalRequest.headers) + }; + } else { + const authorization = originalRequest.headers.get("authorization"); + let authenticationMode = "Bearer"; + if (authorization) { + authenticationMode = authorization.split(" ")[0]; + } + headers = { + ...serializeHeaders(originalRequest.headers), + authorization: authenticationMode + " " + currentDatabaseForRequestAccessToken.tokens.access_token + }; + } + let init; + if (originalRequest.mode === "navigate") { + init = { + headers + }; + } else { + init = { + headers, + mode: requestMode + }; + } + const newRequest = new Request(originalRequest, init); + event.respondWith(fetch(newRequest)); + return; + } + if (event.request.method !== "POST") { + return; + } + let currentDatabase = null; + const currentDatabases = getCurrentDatabasesTokenEndpoint(database, url); + const numberDatabase = currentDatabases.length; + if (numberDatabase > 0) { + const maPromesse = new Promise((resolve, reject) => { + const clonedRequest = originalRequest.clone(); + const response = clonedRequest.text().then(async (actualBody) => { + if (actualBody.includes(TOKEN.REFRESH_TOKEN) || actualBody.includes(TOKEN.ACCESS_TOKEN)) { + let headers = serializeHeaders(originalRequest.headers); + let newBody = actualBody; + for (let i = 0; i < numberDatabase; i++) { + const currentDb = currentDatabases[i]; + if (currentDb && currentDb.tokens != null) { + const claimsExtras = { ath: await base64urlOfHashOfASCIIEncodingAsync(currentDb.tokens.access_token) }; + headers = await generateDpopAsync(originalRequest, currentDb, url, claimsExtras); + const keyRefreshToken = TOKEN.REFRESH_TOKEN + "_" + currentDb.configurationName; + if (actualBody.includes(keyRefreshToken)) { + newBody = newBody.replace( + keyRefreshToken, + encodeURIComponent(currentDb.tokens.refresh_token) + ); + currentDatabase = currentDb; + break; + } + const keyAccessToken = TOKEN.ACCESS_TOKEN + "_" + currentDb.configurationName; + if (actualBody.includes(keyAccessToken)) { + newBody = newBody.replace( + keyAccessToken, + encodeURIComponent(currentDb.tokens.access_token) + ); + currentDatabase = currentDb; + break; + } + } + } + const fetchPromise = fetch(originalRequest, { + body: newBody, + method: clonedRequest.method, + headers: { + ...headers + }, + mode: clonedRequest.mode, + cache: clonedRequest.cache, + redirect: clonedRequest.redirect, + referrer: clonedRequest.referrer, + credentials: clonedRequest.credentials, + integrity: clonedRequest.integrity + }); + if (currentDatabase && currentDatabase.oidcServerConfiguration != null && currentDatabase.oidcServerConfiguration.revocationEndpoint && url.startsWith( + normalizeUrl( + currentDatabase.oidcServerConfiguration.revocationEndpoint + ) + )) { + return fetchPromise.then(async (response2) => { + const text = await response2.text(); + return new Response(text, response2); + }); + } + return fetchPromise.then(hideTokens(currentDatabase)); + } else if (actualBody.includes("code_verifier=") && extractConfigurationNameFromCodeVerifier(actualBody) != null) { + const currentLoginCallbackConfigurationName = extractConfigurationNameFromCodeVerifier( + actualBody + ); + currentDatabase = database[currentLoginCallbackConfigurationName]; + let newBody = actualBody; + if (currentDatabase && currentDatabase.codeVerifier != null) { + newBody = replaceCodeVerifier( + newBody, + currentDatabase.codeVerifier + ); + } + const headersExtras = await generateDpopAsync(originalRequest, currentDatabase, url); + return fetch(originalRequest, { + body: newBody, + method: clonedRequest.method, + headers: { + ...headersExtras + }, + mode: clonedRequest.mode, + cache: clonedRequest.cache, + redirect: clonedRequest.redirect, + referrer: clonedRequest.referrer, + credentials: clonedRequest.credentials, + integrity: clonedRequest.integrity + // @ts-ignore + }).then(hideTokens(currentDatabase)); + } + return fetch(originalRequest, { + body: actualBody, + method: clonedRequest.method, + headers: { + ...serializeHeaders(originalRequest.headers) + }, + mode: clonedRequest.mode, + cache: clonedRequest.cache, + redirect: clonedRequest.redirect, + referrer: clonedRequest.referrer, + credentials: clonedRequest.credentials, + integrity: clonedRequest.integrity + }); + }); + response.then((r) => { + resolve(r); + }).catch((err) => { + reject(err); + }); + }); + event.respondWith(maPromesse); + } +}; +const handleMessage = async (event) => { + const port = event.ports[0]; + const data = event.data; + if (event.data.type === "claim") { + _self.clients.claim().then(() => port.postMessage({})); + return; + } + const configurationName = data.configurationName; + let currentDatabase = database[configurationName]; + if (trustedDomains == null) { + trustedDomains = {}; + } + if (!currentDatabase) { + const trustedDomain = trustedDomains[configurationName]; + const showAccessToken = Array.isArray(trustedDomain) ? false : trustedDomain.showAccessToken; + const doNotSetAccessTokenToNavigateRequests = Array.isArray(trustedDomain) ? true : trustedDomain.setAccessTokenToNavigateRequests; + const convertAllRequestsToCorsExceptNavigate = Array.isArray(trustedDomain) ? false : trustedDomain.convertAllRequestsToCorsExceptNavigate; + database[configurationName] = { + tokens: null, + state: null, + codeVerifier: null, + oidcServerConfiguration: null, + oidcConfiguration: void 0, + nonce: null, + status: null, + configurationName, + hideAccessToken: !showAccessToken, + setAccessTokenToNavigateRequests: doNotSetAccessTokenToNavigateRequests ?? true, + convertAllRequestsToCorsExceptNavigate: convertAllRequestsToCorsExceptNavigate ?? false, + demonstratingProofOfPossessionNonce: null, + demonstratingProofOfPossessionJwkJson: null, + demonstratingProofOfPossessionConfiguration: null, + demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent: false + }; + currentDatabase = database[configurationName]; + if (!trustedDomains[configurationName]) { + trustedDomains[configurationName] = []; + } + } + switch (data.type) { + case "clear": + currentDatabase.tokens = null; + currentDatabase.state = null; + currentDatabase.codeVerifier = null; + currentDatabase.demonstratingProofOfPossessionNonce = null; + currentDatabase.demonstratingProofOfPossessionJwkJson = null; + currentDatabase.demonstratingProofOfPossessionConfiguration = null; + currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = false; + currentDatabase.status = data.data.status; + port.postMessage({ configurationName }); + return; + case "init": { + const oidcServerConfiguration = data.data.oidcServerConfiguration; + const trustedDomain = trustedDomains[configurationName]; + const domains = getDomains(trustedDomain, "oidc"); + if (!domains.some((domain) => domain === acceptAnyDomainToken)) { + [ + oidcServerConfiguration.tokenEndpoint, + oidcServerConfiguration.revocationEndpoint, + oidcServerConfiguration.userInfoEndpoint, + oidcServerConfiguration.issuer + ].forEach((url) => { + checkDomain(domains, url); + }); + } + currentDatabase.oidcServerConfiguration = oidcServerConfiguration; + currentDatabase.oidcConfiguration = data.data.oidcConfiguration; + if (currentDatabase.demonstratingProofOfPossessionConfiguration == null) { + const demonstratingProofOfPossessionConfiguration = getDpopConfiguration(trustedDomains[configurationName]); + if (demonstratingProofOfPossessionConfiguration != null) { + if (currentDatabase.oidcConfiguration.demonstrating_proof_of_possession) { + console.warn("In service worker, demonstrating_proof_of_possession must be configured from trustedDomains file"); + } + currentDatabase.demonstratingProofOfPossessionConfiguration = demonstratingProofOfPossessionConfiguration; + currentDatabase.demonstratingProofOfPossessionJwkJson = await generateJwkAsync(self)(demonstratingProofOfPossessionConfiguration.generateKeyAlgorithm); + currentDatabase.demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent = getDpopOnlyWhenDpopHeaderPresent(trustedDomains[configurationName]) ?? false; + } + } + if (!currentDatabase.tokens) { + port.postMessage({ + tokens: null, + status: currentDatabase.status, + configurationName, + version + }); + } else { + const tokens = { + ...currentDatabase.tokens + }; + if (currentDatabase.hideAccessToken) { + tokens.access_token = TOKEN.ACCESS_TOKEN + "_" + configurationName; + } + if (tokens.refresh_token) { + tokens.refresh_token = TOKEN.REFRESH_TOKEN + "_" + configurationName; + } + if (tokens.idTokenPayload && tokens.idTokenPayload.nonce && currentDatabase.nonce != null) { + tokens.idTokenPayload.nonce = TOKEN.NONCE_TOKEN + "_" + configurationName; + } + port.postMessage({ + tokens, + status: currentDatabase.status, + configurationName, + version + }); + } + return; + } + case "setDemonstratingProofOfPossessionNonce": { + currentDatabase.demonstratingProofOfPossessionNonce = data.data.demonstratingProofOfPossessionNonce; + port.postMessage({ configurationName }); + return; + } + case "getDemonstratingProofOfPossessionNonce": { + const demonstratingProofOfPossessionNonce = currentDatabase.demonstratingProofOfPossessionNonce; + port.postMessage({ + configurationName, + demonstratingProofOfPossessionNonce + }); + return; + } + case "setState": { + currentDatabase.state = data.data.state; + port.postMessage({ configurationName }); + return; + } + case "getState": { + const state = currentDatabase.state; + port.postMessage({ configurationName, state }); + return; + } + case "setCodeVerifier": { + currentDatabase.codeVerifier = data.data.codeVerifier; + port.postMessage({ configurationName }); + return; + } + case "getCodeVerifier": { + port.postMessage({ + configurationName, + codeVerifier: currentDatabase.codeVerifier != null ? TOKEN.CODE_VERIFIER + "_" + configurationName : null + }); + return; + } + case "setSessionState": { + currentDatabase.sessionState = data.data.sessionState; + port.postMessage({ configurationName }); + return; + } + case "getSessionState": { + const sessionState = currentDatabase.sessionState; + port.postMessage({ configurationName, sessionState }); + return; + } + case "setNonce": { + const nonce = data.data.nonce; + if (nonce) { + currentDatabase.nonce = nonce; + } + port.postMessage({ configurationName }); + return; + } + case "getNonce": { + const keyNonce = TOKEN.NONCE_TOKEN + "_" + configurationName; + const nonce = currentDatabase.nonce ? keyNonce : null; + port.postMessage({ configurationName, nonce }); + return; + } + default: { + return; + } + } +}; +_self.addEventListener("install", handleInstall); +_self.addEventListener("activate", handleActivate); +_self.addEventListener("fetch", handleFetch); +_self.addEventListener("message", handleMessage); +//# sourceMappingURL=OidcServiceWorker.js.map diff --git a/public/OidcTrustedDomains.js b/public/OidcTrustedDomains.js new file mode 100644 index 0000000..72a7776 --- /dev/null +++ b/public/OidcTrustedDomains.js @@ -0,0 +1,35 @@ +// Add bellow trusted domains, access tokens will automatically injected to be send to +// trusted domain can also be a path like https://www.myapi.com/users, +// then all subroute like https://www.myapi.com/useers/1 will be authorized to send access_token to. + +// Domains used by OIDC server must be also declared here +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const trustedDomains = { + default: [ + 'https://login.staging.radical.sexy', + 'https://git.staging.radical.sexy', + 'http://localhost:3001' + ], + // config_classic: ['https://demo.duendesoftware.com'], + // config_without_silent_login: ['https://demo.duendesoftware.com'], + // config_without_refresh_token: ['https://demo.duendesoftware.com'], + // config_without_refresh_token_silent_login: ['https://demo.duendesoftware.com'], + // config_google: ['https://oauth2.googleapis.com', 'https://openidconnect.googleapis.com'], + // config_with_hash: ['https://demo.duendesoftware.com'], +} + +// Service worker will continue to give access token to the JavaScript client +// Ideal to hide refresh token from client JavaScript, but to retrieve access_token for some +// scenarios which require it. For example, to send it via websocket connection. +// trustedDomains.config_show_access_token = { +// domains: ['https://demo.duendesoftware.com'], +// showAccessToken: true, +// // convertAllRequestsToCorsExceptNavigate: false, +// // setAccessTokenToNavigateRequests: true, +// } + +// // This example defines domains used by OIDC server separately from domains to which access tokens will be injected. +// trustedDomains.config_separate_oidc_access_token_domains = { +// oidcDomains: ['https://demo.duendesoftware.com'], +// accessTokenDomains: ['https://myapi'], +// } diff --git a/images/favicon-16x16.png b/public/favicon-16x16.png similarity index 100% rename from images/favicon-16x16.png rename to public/favicon-16x16.png diff --git a/images/favicon-180x180.png b/public/favicon-180x180.png similarity index 100% rename from images/favicon-180x180.png rename to public/favicon-180x180.png diff --git a/images/favicon-192x192.png b/public/favicon-192x192.png similarity index 100% rename from images/favicon-192x192.png rename to public/favicon-192x192.png diff --git a/images/favicon-32x32.png b/public/favicon-32x32.png similarity index 100% rename from images/favicon-32x32.png rename to public/favicon-32x32.png diff --git a/images/favicon.ico b/public/favicon.ico similarity index 100% rename from images/favicon.ico rename to public/favicon.ico diff --git a/public/iframe.css b/public/iframe.css new file mode 100644 index 0000000..2b6ac80 --- /dev/null +++ b/public/iframe.css @@ -0,0 +1,143 @@ +:root { + color-scheme: light dark; + + --sl-color-gray-50: hsl(0deg 0% 97.5%); + --sl-color-gray-100: hsl(240deg 4.8% 95.9%); + --sl-color-gray-200: hsl(240deg 5.9% 90%); + --sl-color-gray-300: hsl(240deg 4.9% 83.9%); + --sl-color-gray-400: hsl(240deg 5% 64.9%); + --sl-color-gray-500: hsl(240deg 3.8% 46.1%); + --sl-color-gray-600: hsl(240deg 5.2% 33.9%); + --sl-color-gray-700: hsl(240deg 5.3% 26.1%); + --sl-color-gray-800: hsl(240deg 3.7% 15.9%); + --sl-color-gray-900: hsl(240deg 5.9% 10%); + --sl-color-gray-950: hsl(240deg 7.3% 8%); + --sl-color-orange-50: rgb(254 248 245); + --sl-color-orange-100: rgb(253 232 221); + --sl-color-orange-200: rgb(251 215 195); + --sl-color-orange-300: rgb(250 195 165); + --sl-color-orange-400: rgb(247 167 123); + --sl-color-orange-500: rgb(244 128 64); + --sl-color-orange-600: rgb(216 96 29); + --sl-color-orange-700: rgb(178 79 24); + --sl-color-orange-800: rgb(147 65 20); + --sl-color-orange-900: rgb(105 46 14); + --sl-color-orange-950: rgb(65 29 9); + --sl-color-primary-50: var(--sl-color-orange-50); + --sl-color-primary-100: var(--sl-color-orange-100); + --sl-color-primary-200: var(--sl-color-orange-200); + --sl-color-primary-300: var(--sl-color-orange-300); + --sl-color-primary-400: var(--sl-color-orange-400); + --sl-color-primary-500: var(--sl-color-orange-500); + --sl-color-primary-600: var(--sl-color-orange-600); + --sl-color-primary-700: var(--sl-color-orange-700); + --sl-color-primary-800: var(--sl-color-orange-800); + --sl-color-primary-900: var(--sl-color-orange-900); + --sl-color-primary-950: var(--sl-color-orange-950); + --sl-color-rose-50: hsl(355.7deg 100% 97.3%); + --sl-color-rose-100: hsl(355.6deg 100% 94.7%); + --sl-color-rose-200: hsl(352.7deg 96.1% 90%); + --sl-color-rose-300: hsl(352.6deg 95.7% 81.8%); + --sl-color-rose-400: hsl(351.3deg 94.5% 71.4%); + --sl-color-rose-500: hsl(349.7deg 89.2% 60.2%); + --sl-color-rose-600: hsl(346.8deg 77.2% 49.8%); + --sl-color-rose-700: hsl(345.3deg 82.7% 40.8%); + --sl-color-rose-800: hsl(343.4deg 79.7% 34.7%); + --sl-color-rose-900: hsl(341.5deg 75.5% 30.4%); + --sl-color-rose-950: hsl(341.3deg 70.1% 17.1%); +} + +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol'; + background: light-dark(#fff, #000); +} + +h1, h2, h3, h4, h5, h6 { + margin-bottom: 0; + font-size: 24px; + color: var(--sl-color-primary-500) +} + +.dark { + color: var(--sl-color-gray-100); + background: var(--sl-color-gray-900) +} + +.light { + color: var(--sl-color-gray-900); + background: white; +} + +code { + color: var(--sl-color-rose-500); +} + +img { + display: block; + max-width: 100%; + margin: 15px auto; +} + +pre { + padding: 15px; + overflow-x: auto; +} + +body.light pre { + background:var(--sl-color-gray-100) +} + +body.dark pre { + background:var(--sl-color-gray-800) +} + +body.light pre code { + color: var(--sl-color-gray-900) +} + +body.dark pre code { + color: var(--sl-color-gray-100) +} + +blockquote { + padding: 0.5em 10px; + margin: 1.5em 10px; + quotes: "\201C""\201D""\2018""\2019"; + border-left-style: solid; + border-left-width: 10px; + +} + +blockquote::before { + margin-right: 0.25em; + font-size: 4em; + line-height: 0.1em; + vertical-align: -0.4em; + content: open-quote; +} + +blockquote p { + display: inline; +} + +body.light blockquote { + background: var(--sl-color-gray-100); + border-left-color: var(--sl-color-gray-300); +} + +body.dark blockquote { + background: var(--sl-color-gray-800); + border-left-color: var(--sl-color-gray-600); +} + +body.light blockquote::before { + color: var(--sl-color-gray-300); +} + +body.dark blockquote::before { + color: var(--sl-color-gray-600); +} \ No newline at end of file diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..f376fe1 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,3 @@ +User-agent: AdsBot-Google +User-agent: * +Disallow: / diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index e6bd838..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,58 +0,0 @@ -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; - -export default [ - { - input: "node_modules/moment/src/moment.js", - output: { - file: "web_modules/moment.js" - } - },{ - input: "node_modules/marked/src/marked.js", - output: { - file: "web_modules/marked.js" - }, - plugins: [nodeResolve(), commonjs({transformMixedEsModules:true})] - },{ - input: "node_modules/lit/index.js", - output: { - file: "web_modules/lit.js" - }, - plugins: [nodeResolve()] - },{ - input: "node_modules/lit/directive.js", - output: { - file: "web_modules/lit/directive.js" - }, - plugins: [nodeResolve()] - },{ - input: "node_modules/lit/async-directive.js", - output: { - file: "web_modules/lit/async-directive.js" - }, - plugins: [nodeResolve()] - },{ - input: "node_modules/lit-html/directives/style-map.js", - output: { - file: "web_modules/lit-html/directives/style-map.js" - }, - plugins: [nodeResolve()] - },{ - input: "node_modules/lit-html/directives/class-map.js", - output: { - file: "web_modules/lit-html/directives/class-map.js" - }, - plugins: [nodeResolve()] - },{ - input: "node_modules/lit-html/directives/repeat.js", - output: { - file: "web_modules/lit-html/directives/repeat.js" - }, - plugins: [nodeResolve()] - },{ - input: "node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js", - output: { - file: "web_modules/@webcomponents/webcomponentsjs.js" - } - } -] \ No newline at end of file diff --git a/site.webmanifest b/site.webmanifest index 6358101..7f5dc50 100644 --- a/site.webmanifest +++ b/site.webmanifest @@ -1,16 +1,16 @@ { - "name": "Radically Open Security", - "short_name": "ROS", - "icons": [ - { - "src": "images/favicon-192x192.png", - "sizes": "192x192", - "type": "image/png" - } - ], - "display": "standalone", - "background_color": "#FFFFFF", - "theme_color": "#F06000", - "scope": "https://git.radicallyopensecurity.com/board/", - "lang": "en-US" -} \ No newline at end of file + "name": "Radically Open Security", + "short_name": "ROS", + "icons": [ + { + "src": "images/favicon-192x192.png", + "sizes": "192x192", + "type": "image/png" + } + ], + "display": "standalone", + "background_color": "#FFFFFF", + "theme_color": "#F06000", + "scope": "https://git.radicallyopensecurity.com/board/", + "lang": "en-US" +} diff --git a/src/api/gitlab/client/create-access-token.ts b/src/api/gitlab/client/create-access-token.ts new file mode 100644 index 0000000..281e4a7 --- /dev/null +++ b/src/api/gitlab/client/create-access-token.ts @@ -0,0 +1,30 @@ +import { config } from '@/config' + +import { CONTENT_TYPE, CONTENT_TYPE_JSON } from '@/constants/http' + +import { handleResponse } from '@/utils/fetch/handle-response' + +export type CreateGitLabAccessToken = { + scopes: string[] + name: string + expires_at: Date +} + +export const createAccessToken = async ( + id: number, + params: CreateGitLabAccessToken +): Promise<{ token: string }> => { + const url = new URL( + `${config.app.gitlabBaseUrl}/projects/${id}/access_tokens` + ) + + const response = await fetch(url, { + method: 'POST', + headers: { + [CONTENT_TYPE]: CONTENT_TYPE_JSON, + }, + body: JSON.stringify(params), + }) + + return handleResponse(response) +} diff --git a/src/api/gitlab/client/create-project.ts b/src/api/gitlab/client/create-project.ts new file mode 100644 index 0000000..3e177d4 --- /dev/null +++ b/src/api/gitlab/client/create-project.ts @@ -0,0 +1,50 @@ +import { config } from '@/config' + +import { CONTENT_TYPE, CONTENT_TYPE_JSON } from '@/constants/http' + +import { handleResponse } from '@/utils/fetch/handle-response' + +import { GitLabProject } from '../types/gitlab-project' + +const DEFAULT_OPTIONS: Partial = { + default_branch: 'main', + wiki_access_level: 'disabled', + pages_access_level: 'disabled', + issues_access_level: 'private', + packages_enabled: false, + enforce_auth_checks_on_uploads: false, +} + +export type CreateGitLabProject = { + import_url: string + default_branch: string + wiki_access_level: string + pages_access_level: string + issues_access_level: string + path: string + packages_enabled: false + namespace_id: number + topics: string[] + enforce_auth_checks_on_uploads: false +} + +export const createProject = async ( + params: Partial +): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects`) + + const body = { + ...DEFAULT_OPTIONS, + ...params, + } + + const response = await fetch(url, { + method: 'POST', + headers: { + [CONTENT_TYPE]: CONTENT_TYPE_JSON, + }, + body: JSON.stringify(body), + }) + + return handleResponse(response) +} diff --git a/src/api/gitlab/client/create-variable.ts b/src/api/gitlab/client/create-variable.ts new file mode 100644 index 0000000..310bade --- /dev/null +++ b/src/api/gitlab/client/create-variable.ts @@ -0,0 +1,31 @@ +import { config } from '@/config' + +import { CONTENT_TYPE, CONTENT_TYPE_JSON } from '@/constants/http' + +import { handleResponse } from '@/utils/fetch/handle-response' + +import { GitLabProject } from '../types/gitlab-project' + +export type CreateGitLabVariable = { + key: string + value: string + protected: boolean + masked: boolean +} + +export const createVariable = async ( + id: number, + params: CreateGitLabVariable +): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${id}/variables`) + + const response = await fetch(url, { + method: 'POST', + headers: { + [CONTENT_TYPE]: CONTENT_TYPE_JSON, + }, + body: JSON.stringify(params), + }) + + return handleResponse(response) +} diff --git a/src/api/gitlab/client/discussions.ts b/src/api/gitlab/client/discussions.ts new file mode 100644 index 0000000..c31aabc --- /dev/null +++ b/src/api/gitlab/client/discussions.ts @@ -0,0 +1,25 @@ +import { config } from '@/config' + +import { type GitLabDiscussion } from '@/api/gitlab/types/gitlab-discussion' +import { type FetchPaginatedParameters } from '@/api/gitlab/utils/fetch-paginated' + +import { handleResponse } from '@/utils/fetch/handle-response' + +export const discussions = async ({ + perPage, + page, + projectId, + issueId, +}: FetchPaginatedParameters & { projectId: number; issueId: number }): Promise< + GitLabDiscussion[] +> => { + const url = new URL( + `${config.app.gitlabBaseUrl}/projects/${projectId}/issues/${issueId}/discussions` + ) + + url.searchParams.set('per_page', perPage?.toString() ?? '') + url.searchParams.set('page', page?.toString() ?? '') + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/events.ts b/src/api/gitlab/client/events.ts new file mode 100644 index 0000000..671a962 --- /dev/null +++ b/src/api/gitlab/client/events.ts @@ -0,0 +1,21 @@ +import { config } from '@/config' + +import { GitLabEvent } from '@/api/gitlab/types/gitlab-event' +import { type FetchPaginatedParameters } from '@/api/gitlab/utils/fetch-paginated' + +import { handleResponse } from '@/utils/fetch/handle-response' + +export const events = async ({ + perPage, + page, + id, +}: FetchPaginatedParameters & { id: number }): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${id}/events`) + + url.searchParams.set('target', 'issue') + url.searchParams.set('per_page', perPage?.toString() ?? '') + url.searchParams.set('page', page?.toString() ?? '') + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/group-projects.ts b/src/api/gitlab/client/group-projects.ts new file mode 100644 index 0000000..7c2ef14 --- /dev/null +++ b/src/api/gitlab/client/group-projects.ts @@ -0,0 +1,25 @@ +import { config } from '@/config' + +import { handleResponse } from '@/utils/fetch/handle-response' + +import { GitLabProject } from '../types/gitlab-project' + +type GetGitLabGroupProjects = { + path: string + scope?: 'projects' + perPage?: number +} + +export const groupProjects = async ({ + path, + perPage = 100, + scope, +}: GetGitLabGroupProjects): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/groups/${path}/projects`) + + url.searchParams.set('per_page', perPage?.toString() ?? '') + url.searchParams.set('scope', scope ?? '') + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/group.ts b/src/api/gitlab/client/group.ts new file mode 100644 index 0000000..d798bbd --- /dev/null +++ b/src/api/gitlab/client/group.ts @@ -0,0 +1,16 @@ +import { config } from '@/config' + +import { GitLabLabel } from '@/api/gitlab/types/gitlab-label' + +import { handleResponse } from '@/utils/fetch/handle-response' + +type GetGitLabGroup = { + id: number +} + +export const group = async ({ id }: GetGitLabGroup): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/groups/${id}`) + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/groups.ts b/src/api/gitlab/client/groups.ts new file mode 100644 index 0000000..42bfe12 --- /dev/null +++ b/src/api/gitlab/client/groups.ts @@ -0,0 +1,27 @@ +import { config } from '@/config' + +import { handleResponse } from '@/utils/fetch/handle-response' +import { isDefined } from '@/utils/object/is-defined' + +import { GitLabGroup } from '../types/gitlab-group' + +type GetGitLabGroups = { + allAvailable?: boolean + minAccessLevel?: number +} + +export const groups = async ({ + allAvailable = true, + minAccessLevel, +}: GetGitLabGroups): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/groups`) + + url.searchParams.set('all_available', allAvailable ? 'true' : 'false') + + if (isDefined(minAccessLevel)) { + url.searchParams.set('min_access_level', minAccessLevel.toString()) + } + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/issues.ts b/src/api/gitlab/client/issues.ts new file mode 100644 index 0000000..6f55558 --- /dev/null +++ b/src/api/gitlab/client/issues.ts @@ -0,0 +1,20 @@ +import { config } from '@/config' + +import { GitLabIssue } from '@/api/gitlab/types/gitlab-issue' +import { type FetchPaginatedParameters } from '@/api/gitlab/utils/fetch-paginated' + +import { handleResponse } from '@/utils/fetch/handle-response' + +export const issues = async ({ + perPage, + page, + id, +}: FetchPaginatedParameters & { id: number }): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${id}/issues`) + + url.searchParams.set('per_page', perPage?.toString() ?? '') + url.searchParams.set('page', page?.toString() ?? '') + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/jobs.ts b/src/api/gitlab/client/jobs.ts new file mode 100644 index 0000000..39f4831 --- /dev/null +++ b/src/api/gitlab/client/jobs.ts @@ -0,0 +1,42 @@ +import { config } from '@/config' + +import { type FetchPaginatedParameters } from '@/api/gitlab/utils/fetch-paginated' + +import { handleResponse } from '@/utils/fetch/handle-response' + +import { GitLabJob } from '../types/gitlab-job' + +type GitLabJobScope = + | 'created' + | 'pending' + | 'running' + | 'failed' + | 'success' + | 'canceled' + | 'skipped' + | 'waiting_for_resource' + | 'manual' + +type Parameters = FetchPaginatedParameters & { + projectId: number + scopes?: GitLabJobScope[] +} + +export const jobs = async ({ + perPage, + page, + projectId, + scopes = [], +}: Parameters): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${projectId}/jobs`) + + url.searchParams.set('per_page', perPage?.toString() ?? '') + url.searchParams.set('page', page?.toString() ?? '') + + for (const scope of scopes) { + url.searchParams.append('scope[]', scope) + } + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/labels.ts b/src/api/gitlab/client/labels.ts new file mode 100644 index 0000000..b83641d --- /dev/null +++ b/src/api/gitlab/client/labels.ts @@ -0,0 +1,20 @@ +import { config } from '@/config' + +import { GitLabLabel } from '@/api/gitlab/types/gitlab-label' +import { type FetchPaginatedParameters } from '@/api/gitlab/utils/fetch-paginated' + +import { handleResponse } from '@/utils/fetch/handle-response' + +export const labels = async ({ + perPage, + page, + id, +}: FetchPaginatedParameters & { id: number }): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${id}/labels`) + + url.searchParams.set('per_page', perPage?.toString() ?? '') + url.searchParams.set('page', page?.toString() ?? '') + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/members.ts b/src/api/gitlab/client/members.ts new file mode 100644 index 0000000..f1e4e71 --- /dev/null +++ b/src/api/gitlab/client/members.ts @@ -0,0 +1,20 @@ +import { config } from '@/config' + +import { GitLabMember } from '@/api/gitlab/types/gitlab-member' +import { type FetchPaginatedParameters } from '@/api/gitlab/utils/fetch-paginated' + +import { handleResponse } from '@/utils/fetch/handle-response' + +export const members = async ({ + perPage, + page, + id, +}: FetchPaginatedParameters & { id: number }): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${id}/members`) + + url.searchParams.set('per_page', perPage?.toString() ?? '') + url.searchParams.set('page', page?.toString() ?? '') + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/project-file.ts b/src/api/gitlab/client/project-file.ts new file mode 100644 index 0000000..5f128c8 --- /dev/null +++ b/src/api/gitlab/client/project-file.ts @@ -0,0 +1,26 @@ +import { config } from '@/config' + +import { handleResponse } from '@/utils/fetch/handle-response' + +import { GitLabProjectFile } from '../types/gitlab-project-file' + +type ProjectFile = { + id: number + path: string + branch?: string +} + +export const projectFile = async ({ + id, + path, + branch = 'main', +}: ProjectFile): Promise => { + const url = new URL( + `${config.app.gitlabBaseUrl}/projects/${id}/repository/files/${encodeURIComponent(path)}` + ) + + url.searchParams.set('ref', branch) + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/project.ts b/src/api/gitlab/client/project.ts new file mode 100644 index 0000000..18ef9b8 --- /dev/null +++ b/src/api/gitlab/client/project.ts @@ -0,0 +1,18 @@ +import { config } from '@/config' + +import { handleResponse } from '@/utils/fetch/handle-response' + +import { GitLabProject } from '../types/gitlab-project' + +type GetGitLabProject = { + id: number +} + +export const project = async ({ + id, +}: GetGitLabProject): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${id}`) + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/projects.ts b/src/api/gitlab/client/projects.ts new file mode 100644 index 0000000..37859f7 --- /dev/null +++ b/src/api/gitlab/client/projects.ts @@ -0,0 +1,22 @@ +import { config } from '@/config' + +import { GitLabProject } from '@/api/gitlab/types/gitlab-project' + +import { handleResponse } from '@/utils/fetch/handle-response' + +type GetGitLabProjectsParams = { + perPage?: number + page?: number +} + +export const projects = async ( + params: GetGitLabProjectsParams +): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects`) + + url.searchParams.set('per_page', params.perPage?.toString() ?? '') + url.searchParams.set('page', params.page?.toString() ?? '') + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/update-project-file.ts b/src/api/gitlab/client/update-project-file.ts new file mode 100644 index 0000000..4b081d9 --- /dev/null +++ b/src/api/gitlab/client/update-project-file.ts @@ -0,0 +1,41 @@ +import { config } from '@/config' + +import { handleResponse } from '@/utils/fetch/handle-response' + +import { GitLabProjectFile } from '../types/gitlab-project-file' + +export type UpdateProjectFile = { + id: number + path: string + branch: string + content: string + commitMessage: string +} + +export const updateProjectFile = async ({ + id, + path, + branch = 'main', + content, + commitMessage, +}: UpdateProjectFile): Promise => { + const url = new URL( + `${config.app.gitlabBaseUrl}/projects/${id}/repository/files/${encodeURIComponent(path)}` + ) + + const body = { + branch, + content, + commit_message: commitMessage, + } + + const response = await fetch(url, { + method: 'PUT', + body: JSON.stringify(body), + headers: { + 'Content-Type': 'application/json', + }, + }) + + return handleResponse(response) +} diff --git a/src/api/gitlab/client/update-project.ts b/src/api/gitlab/client/update-project.ts new file mode 100644 index 0000000..13cb7b3 --- /dev/null +++ b/src/api/gitlab/client/update-project.ts @@ -0,0 +1,26 @@ +import { config } from '@/config' + +import { CONTENT_TYPE, CONTENT_TYPE_JSON } from '@/constants/http' + +import { handleResponse } from '@/utils/fetch/handle-response' + +import { GitLabProject } from '../types/gitlab-project' + +export type UpdateGitLabProject = Omit, 'id'> + +export const updateProject = async ( + id: number, + params: UpdateGitLabProject +): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${id}`) + + const response = await fetch(url, { + method: 'PUT', + headers: { + [CONTENT_TYPE]: CONTENT_TYPE_JSON, + }, + body: JSON.stringify(params), + }) + + return handleResponse(response) +} diff --git a/src/api/gitlab/client/user.ts b/src/api/gitlab/client/user.ts new file mode 100644 index 0000000..4307ea0 --- /dev/null +++ b/src/api/gitlab/client/user.ts @@ -0,0 +1,11 @@ +import { config } from '@/config' + +import { GitLabUser } from '@/api/gitlab/types/gitlab-user' + +import { handleResponse } from '@/utils/fetch/handle-response' + +export const user = async (): Promise => { + const url = `${config.app.gitlabBaseUrl}/user` + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/client/variables.ts b/src/api/gitlab/client/variables.ts new file mode 100644 index 0000000..9c8198f --- /dev/null +++ b/src/api/gitlab/client/variables.ts @@ -0,0 +1,20 @@ +import { config } from '@/config' + +import { GitLabVariable } from '@/api/gitlab/types/gitlab-variable' +import { type FetchPaginatedParameters } from '@/api/gitlab/utils/fetch-paginated' + +import { handleResponse } from '@/utils/fetch/handle-response' + +export const variables = async ({ + perPage, + page, + id, +}: FetchPaginatedParameters & { id: number }): Promise => { + const url = new URL(`${config.app.gitlabBaseUrl}/projects/${id}/variables`) + + url.searchParams.set('per_page', perPage?.toString() ?? '') + url.searchParams.set('page', page?.toString() ?? '') + + const response = await fetch(url) + return handleResponse(response) +} diff --git a/src/api/gitlab/constants.ts b/src/api/gitlab/constants.ts new file mode 100644 index 0000000..69e8096 --- /dev/null +++ b/src/api/gitlab/constants.ts @@ -0,0 +1 @@ +export const BUILD_STAGE = 'build' diff --git a/src/api/gitlab/gitlab-client.ts b/src/api/gitlab/gitlab-client.ts new file mode 100644 index 0000000..f749c09 --- /dev/null +++ b/src/api/gitlab/gitlab-client.ts @@ -0,0 +1,43 @@ +import { createAccessToken } from '@/api/gitlab/client/create-access-token' +import { createProject } from '@/api/gitlab/client/create-project' +import { createVariable } from '@/api/gitlab/client/create-variable' +import { discussions } from '@/api/gitlab/client/discussions' +import { events } from '@/api/gitlab/client/events' +import { group } from '@/api/gitlab/client/group' +import { groupProjects } from '@/api/gitlab/client/group-projects' +import { groups } from '@/api/gitlab/client/groups' +import { issues } from '@/api/gitlab/client/issues' +import { jobs } from '@/api/gitlab/client/jobs' +import { labels } from '@/api/gitlab/client/labels' +import { members } from '@/api/gitlab/client/members' +import { project } from '@/api/gitlab/client/project' +import { projectFile } from '@/api/gitlab/client/project-file' +import { projects } from '@/api/gitlab/client/projects' +import { updateProject } from '@/api/gitlab/client/update-project' +import { updateProjectFile } from '@/api/gitlab/client/update-project-file' +import { user } from '@/api/gitlab/client/user' +import { variables } from '@/api/gitlab/client/variables' + +export const gitlabClient = { + createAccessToken, + createProject, + createVariable, + discussions, + events, + group, + groups, + groupProjects, + issues, + jobs, + labels, + members, + projectFile, + project, + projects, + updateProject, + updateProjectFile, + user, + variables, +} + +export type GitLabClient = typeof gitlabClient diff --git a/src/api/gitlab/types/gitlab-discussion.ts b/src/api/gitlab/types/gitlab-discussion.ts new file mode 100644 index 0000000..331dec5 --- /dev/null +++ b/src/api/gitlab/types/gitlab-discussion.ts @@ -0,0 +1,30 @@ +export type GitLabDiscussion = { + id: string + individual_note: boolean + notes: { + id: number + type: null + body: string + attachment: null + author: { + id: number + username: string + name: string + state: string + locked: boolean + avatar_url: string | null + web_url: string + } + created_at: string + updated_at: string + system: boolean + noteable_id: number + noteable_type: string + project_id: number + resolvable: boolean + confidential: boolean + internal: boolean + noteable_iid: number + commands_changes: unknown + }[] +} diff --git a/src/api/gitlab/types/gitlab-event.ts b/src/api/gitlab/types/gitlab-event.ts new file mode 100644 index 0000000..d31dbdb --- /dev/null +++ b/src/api/gitlab/types/gitlab-event.ts @@ -0,0 +1,56 @@ +type GitLabPushEvent = { + action_name: 'pushed to' + push_data: { + commit_count: number + action: string + ref_type: string + commit_from: string + commit_to: string + ref: string + commit_title: string + ref_count: unknown + } +} + +type GitLabStateEvent = { + action_name: 'opened' | 'closed' | 'updated' + target_title: string +} + +type GitLabCommentEvent = { + action_name: 'commented on' + note: { + noteable_iid: string + } +} + +type GitLabCreateEvent = { + action_name: 'created' +} + +export type GitLabEvent = { + id: number + project_id: number + action_name: string + target_id: string + target_iid: string + target_type: unknown + target_title: string + author_id: number + created_at: string + author: { + id: number + username: string + name: string + state: string + locked: boolean + avatar_url: string + web_url: string + } + author_username: string +} & ( + | GitLabPushEvent + | GitLabStateEvent + | GitLabCommentEvent + | GitLabCreateEvent +) diff --git a/src/api/gitlab/types/gitlab-group.ts b/src/api/gitlab/types/gitlab-group.ts new file mode 100644 index 0000000..9570f04 --- /dev/null +++ b/src/api/gitlab/types/gitlab-group.ts @@ -0,0 +1,45 @@ +export interface GitLabGroup { + id: number + web_url: string + name: string + path: string + description: string + visibility: string + share_with_group_lock: boolean + require_two_factor_authentication: boolean + two_factor_grace_period: number + project_creation_level: string + auto_devops_enabled: boolean | null + subgroup_creation_level: string + emails_disabled: boolean + emails_enabled: boolean + mentions_disabled: boolean | null + lfs_enabled: boolean + math_rendering_limits_enabled: boolean + lock_math_rendering_limits_enabled: boolean + default_branch: string | null + default_branch_protection: number + default_branch_protection_defaults: GitLabGroupDefaultBranchProtectionDefaults + avatar_url: string | null + request_access_enabled: boolean + full_name: string + full_path: string + created_at: string + parent_id: number | null + organization_id: number + shared_runners_setting: string +} + +export interface GitLabGroupDefaultBranchProtectionDefaults { + allowed_to_push: GitLabGroupDefaultBranchProtectionAllowedToPush[] + allow_force_push: boolean + allowed_to_merge: GitLabGroupDefaultBranchProtectionAllowedToMerge[] +} + +export interface GitLabGroupDefaultBranchProtectionAllowedToPush { + access_level: number +} + +export interface GitLabGroupDefaultBranchProtectionAllowedToMerge { + access_level: number +} diff --git a/src/api/gitlab/types/gitlab-issue.ts b/src/api/gitlab/types/gitlab-issue.ts new file mode 100644 index 0000000..59a7f87 --- /dev/null +++ b/src/api/gitlab/types/gitlab-issue.ts @@ -0,0 +1,62 @@ +export type GitLabIssue = { + id: number + iid: number + project_id: number + title: string + description: string + state: string + created_at: string + updated_at: string + closed_at: unknown + closed_by: unknown + labels: string[] + milestone: unknown + assignees: unknown[] + author: { + id: number + username: string + name: string + state: string + locked: boolean + avatar_url: string + web_url: string + } + type: string + assignee: unknown + user_notes_count: number + merge_requests_count: number + upvotes: number + downvotes: number + due_date: unknown + confidential: boolean + discussion_locked: unknown + issue_type: string + web_url: string + time_stats: { + time_estimate: number + total_time_spent: number + human_time_estimate: unknown + human_total_time_spent: unknown + } + task_completion_status: { + count: number + completed_count: number + } + has_tasks: boolean + task_status: string + _links: { + self: string + notes: string + award_emoji: string + project: string + closed_as_duplicate_of: unknown + } + references: { + short: string + relative: string + full: string + } + severity: string + moved_to_id: unknown + service_desk_reply_to: unknown +} diff --git a/src/api/gitlab/types/gitlab-job.ts b/src/api/gitlab/types/gitlab-job.ts new file mode 100644 index 0000000..4bc3885 --- /dev/null +++ b/src/api/gitlab/types/gitlab-job.ts @@ -0,0 +1,91 @@ +export type GitLabJob = { + commit: { + author_email: string + author_name: string + created_at: string + id: string + message: string + short_id: string + title: string + } + coverage: string | null + archived: boolean + allow_failure: boolean + created_at: string + started_at: string + finished_at: string + erased_at: string | null + duration: number + queued_duration: number + artifacts_file?: { + filename: string + size: number + } + artifacts: { + file_type: string + size: number + filename: string + file_format: string + }[] + artifacts_expire_at: string + tag_list: string[] + id: number + name: string + pipeline: { + id: number + project_id: number + ref: string + sha: string + status: string + } + ref: string + runner?: { + id: number + description: string + ip_address: string | null + active: boolean + paused: boolean + is_shared: boolean + runner_type: string + name: string | null + online: boolean + status: string + } + runner_manager?: { + id: number + system_id: string + version: string + revision: string + platform: string + architecture: string + created_at: string + contacted_at: string + ip_address: string + status: string + } + stage: string + status: string + failure_reason: string + tag: boolean + web_url: string + project: { + ci_job_token_scope_enabled: boolean + } + user: { + id: number + name: string + username: string + state: string + avatar_url: string + web_url: string + created_at: string + bio: string | null + location: string | null + public_email: string + skype: string + linkedin: string + twitter: string + website_url: string + organization: string + } +} diff --git a/src/api/gitlab/types/gitlab-label.ts b/src/api/gitlab/types/gitlab-label.ts new file mode 100644 index 0000000..f0ea57b --- /dev/null +++ b/src/api/gitlab/types/gitlab-label.ts @@ -0,0 +1,11 @@ +export type GitLabLabel = { + id: number + name: string + description?: string + description_html: string + text_color: string + color: string + subscribed: boolean + priority: unknown + is_project_label: boolean +} diff --git a/src/api/gitlab/types/gitlab-member.ts b/src/api/gitlab/types/gitlab-member.ts new file mode 100644 index 0000000..70b0b91 --- /dev/null +++ b/src/api/gitlab/types/gitlab-member.ts @@ -0,0 +1,12 @@ +export type GitLabMember = { + id: number + username: string + name: string + state: string + locked: boolean + avatar_url: string | null + web_url: string + access_level: number + created_at: string + expires_at: string +} diff --git a/src/api/gitlab/types/gitlab-project-file.ts b/src/api/gitlab/types/gitlab-project-file.ts new file mode 100644 index 0000000..18c1094 --- /dev/null +++ b/src/api/gitlab/types/gitlab-project-file.ts @@ -0,0 +1,13 @@ +export type GitLabProjectFile = { + file_name: string + file_path: string + size: number + encoding: string + content_sha256: string + ref: string + blob_id: string + commit_id: string + last_commit_id: string + execute_filemode: boolean + content: string +} diff --git a/src/api/gitlab/types/gitlab-project.ts b/src/api/gitlab/types/gitlab-project.ts new file mode 100644 index 0000000..fd810ec --- /dev/null +++ b/src/api/gitlab/types/gitlab-project.ts @@ -0,0 +1,26 @@ +export type GitLabProject = { + id: number + name: string + name_with_namespace: string + path: string + path_with_namespace: string + created_at: string + default_branch: string + ssh_url_to_repo: string + web_url: string + readme_url: string + avatar_url: string + last_activity_at: string + archived: boolean + updated_at: string + namespace: { + id: number + name: string + path: string + web_url: string + avatar_url: string | null + } + tag_list: string[] + topics: string[] + http_url_to_repo: string +} diff --git a/src/api/gitlab/types/gitlab-user.ts b/src/api/gitlab/types/gitlab-user.ts new file mode 100644 index 0000000..fd63ba8 --- /dev/null +++ b/src/api/gitlab/types/gitlab-user.ts @@ -0,0 +1,3 @@ +export type GitLabUser = { + avatar_url: string | null +} diff --git a/src/api/gitlab/types/gitlab-variable.ts b/src/api/gitlab/types/gitlab-variable.ts new file mode 100644 index 0000000..349a508 --- /dev/null +++ b/src/api/gitlab/types/gitlab-variable.ts @@ -0,0 +1,11 @@ +export type GitLabVariable = { + variable_type: string + key: string + value: string + hidden: boolean + protected: boolean + masked: boolean + raw: boolean + environment_scope: string + description: string | null +} diff --git a/src/api/gitlab/utils/fetch-paginated.ts b/src/api/gitlab/utils/fetch-paginated.ts new file mode 100644 index 0000000..2c67228 --- /dev/null +++ b/src/api/gitlab/utils/fetch-paginated.ts @@ -0,0 +1,34 @@ +export type FetchPaginatedParameters = { + perPage?: number + page?: number +} + +type FetchPaginatedFunction = ( + params: FetchPaginatedParameters +) => Promise + +const PER_PAGE = 50 + +export const fetchPaginated = async ( + fn: FetchPaginatedFunction, + perPage: number = PER_PAGE +) => { + let page = 1 + let complete = false + + let data: T[] = [] + + while (!complete) { + const result = await fn({ page, perPage }) + + if (!result.length) { + complete = true + break + } + + data = data.concat(result) + page += 1 + } + + return data +} diff --git a/src/app-shell.ts b/src/app-shell.ts new file mode 100644 index 0000000..90c9bd8 --- /dev/null +++ b/src/app-shell.ts @@ -0,0 +1,141 @@ +import './polyfills' +import { provide } from '@lit/context' +import { Router } from '@lit-labs/router' +import { setBasePath } from '@shoelace-style/shoelace/dist/utilities/base-path.js' +import { html, css, LitElement } from 'lit' +import { customElement } from 'lit/decorators.js' +import { createRef, ref } from 'lit/directives/ref.js' + +import { config } from '@/config' + +import { theme } from '@/theme/theme' +import { registerTheme } from '@/theme/utils/register-theme' + +import { routerContext, routes } from '@/routes' + +import { isDefined } from '@/utils/object/is-defined' +import { versionValues } from '@/utils/version/version-values' + +import { ChatServiceResult, chatService } from '@/modules/chat/chat-service' + +import '@shoelace-style/shoelace/dist/components/alert/alert.js' +import '@shoelace-style/shoelace/dist/components/avatar/avatar.js' +import '@shoelace-style/shoelace/dist/components/badge/badge.js' +import '@shoelace-style/shoelace/dist/components/button/button.js' +import '@shoelace-style/shoelace/dist/components/button-group/button-group.js' +import '@shoelace-style/shoelace/dist/components/card/card.js' +import '@shoelace-style/shoelace/dist/components/details/details.js' +import '@shoelace-style/shoelace/dist/components/dialog/dialog.js' +import '@shoelace-style/shoelace/dist/components/divider/divider.js' +import '@shoelace-style/shoelace/dist/components/dropdown/dropdown.js' +import '@shoelace-style/shoelace/dist/components/icon/icon.js' +import '@shoelace-style/shoelace/dist/components/icon-button/icon-button.js' +import '@shoelace-style/shoelace/dist/components/input/input.js' +import '@shoelace-style/shoelace/dist/components/menu/menu.js' +import '@shoelace-style/shoelace/dist/components/menu-item/menu-item.js' +import '@shoelace-style/shoelace/dist/components/menu-label/menu-label.js' +import '@shoelace-style/shoelace/dist/components/option/option.js' +import '@shoelace-style/shoelace/dist/components/select/select.js' +import '@shoelace-style/shoelace/dist/components/skeleton/skeleton.js' +import '@shoelace-style/shoelace/dist/components/tab/tab.js' +import '@shoelace-style/shoelace/dist/components/tab-group/tab-group.js' +import '@shoelace-style/shoelace/dist/components/tab-panel/tab-panel.js' +import '@shoelace-style/shoelace/dist/components/tooltip/tooltip.js' +import '@shoelace-style/shoelace/dist/components/visually-hidden/visually-hidden.js' + +import '@/elements/pdf-button/pdf-button' +import '@/elements/secure-iframe/secure-iframe' +import '@/elements/version-footer/version-footer' + +import '@/features/side-bar/side-bar' +import '@/features/top-bar/top-bar' + +import '@/pages/not-found' + +import '@/theme/light.css' +import '@/theme/dark.css' +import '@/theme/base.css' + +const FOOTER_VALUES = versionValues() + +setBasePath('/') + +const ELEMENT_NAME = 'app-shell' + +@customElement(ELEMENT_NAME) +export class AppShell extends LitElement { + @provide({ context: routerContext }) + private router = new Router(this, routes) + + iframeRef = createRef() + chat: ChatServiceResult | null = null + + public disconnectedCallback(): void { + this.chat?.disconnect() + super.disconnectedCallback() + } + + protected firstUpdated() { + registerTheme() + if (isDefined(this.iframeRef.value)) { + this.chat = chatService(this.iframeRef.value) + } + } + + static styles = [ + ...theme, + css` + :host { + height: 100%; + } + + main { + display: flex; + height: calc(100% - 80px); + background: var(--sl-color-gray-50); + } + + #routes { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 100%; + min-height: calc(100% - 80px); + overflow-y: auto; + } + + #content { + flex: 1; + padding: var(--content-padding); + } + + #chat { + display: none; + } + `, + ] + + render() { + return html` +
+ +
+
${this.router.outlet()}
+ +
+
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: AppShell + } +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..54b8a85 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,69 @@ +import { parseLogLevel } from '@/utils/logging/parse-log-level' +import { LogLevel } from '@/utils/logging/types' +import { ensureString } from '@/utils/string/ensure-string' + +type Config = { + app: { + logLevel: LogLevel + gitlabBaseUrl: string + url: string + version: string + commit: string + publicReportIssueUrl?: string + internalReportIssueUrl?: string + repositoryUrl?: string + } + services: { + rocketChatUrl: string + gitlabUrl: string + codiMdUrl: string + } + oidc: { + clientId: string + authority: string + redirectPath: string + scope: string + } +} + +const gitlabAuthority = ensureString( + 'VITE_GITLAB_AUTHORITY', + import.meta.env.VITE_GITLAB_AUTHORITY +) + +const appUrl = ensureString('VITE_APP_URL', import.meta.env.VITE_APP_URL) + +export const config: Config = { + app: { + logLevel: parseLogLevel(import.meta.env.VITE_LOG_LEVEL), + gitlabBaseUrl: `${gitlabAuthority}/api/v4`, + url: appUrl, + version: __APP_VERSION__, + commit: __APP_COMMIT__, + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + repositoryUrl: import.meta.env.VITE_REPOSITORY_URL || undefined, + publicReportIssueUrl: + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + import.meta.env.VITE_PUBLIC_ISSUE_TRACKER || undefined, + internalReportIssueUrl: + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + import.meta.env.VITE_INTERNAL_ISSUE_TRACKER || undefined, + }, + services: { + rocketChatUrl: ensureString( + 'VITE_ROCKETCHAT_URL', + import.meta.env.VITE_ROCKETCHAT_URL + ), + codiMdUrl: ensureString('VITE_CODIMD_URL', import.meta.env.VITE_CODIMD_URL), + gitlabUrl: gitlabAuthority, + }, + oidc: { + clientId: ensureString( + 'VITE_GITLAB_CLIENT_ID', + import.meta.env.VITE_GITLAB_CLIENT_ID + ), + authority: gitlabAuthority, + redirectPath: '/auth/callback', + scope: 'openid profile api read_repository write_repository', + }, +} diff --git a/src/constants/http.ts b/src/constants/http.ts new file mode 100644 index 0000000..87c7ba6 --- /dev/null +++ b/src/constants/http.ts @@ -0,0 +1,2 @@ +export const CONTENT_TYPE = 'Content-Type' +export const CONTENT_TYPE_JSON = 'application/json' diff --git a/src/constants/projects.ts b/src/constants/projects.ts new file mode 100644 index 0000000..dde2405 --- /dev/null +++ b/src/constants/projects.ts @@ -0,0 +1,9 @@ +export const QUOTE_PROJECT_TAG = 'offerte' +export const QUOTE_PROJECT_PREFIX = 'off-' +export const PENTEST_PROJECT_TAG = 'pentest' +export const PENTEST_PROJECT_PREFIX = 'pen-' + +export const ROS_NAMESPACE = 'ros' + +export const GITLAB_PROJECT_PATH_PATTERN = + /^(?[a-zA-Z]+)\/(?:(?pen|off)-)?(?[a-zA-Z0-9](?:-?[a-zA-Z0-9]+)*)$/ diff --git a/src/elements/data-table/data-table.ts b/src/elements/data-table/data-table.ts new file mode 100644 index 0000000..0d06802 --- /dev/null +++ b/src/elements/data-table/data-table.ts @@ -0,0 +1,128 @@ +import { + ColumnDef, + getCoreRowModel, + TableController, + RowData, + flexRender, + getSortedRowModel, +} from '@tanstack/lit-table' +import { LitElement, css, html } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { repeat } from 'lit/directives/repeat.js' +import { styleMap } from 'lit/directives/style-map.js' + +import { theme } from '@/theme/theme' + +import './sort-caret' + +const ELEMENT_NAME = 'data-table' + +@customElement(ELEMENT_NAME) +export class DataTable extends LitElement { + private tableController = new TableController(this) + + @property() + private columns: ColumnDef[] = [] + + @property() + private data: TData[] = [] + + static styles = [ + ...theme, + css` + table { + width: 100%; + } + + .red { + color: red; + } + + a, + a:link, + a:visited, + a:active { + color: var(--sl-color-primary-500); + } + + a:hover { + color: var(--sl-color-primary-800); + } + `, + ] + + protected render() { + const table = this.tableController.table({ + columns: this.columns, + data: this.data, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + }) + + console.log(table.getState()) + + return html` + + + ${table.getHeaderGroups().map( + (headerGroup) => html` + + ${headerGroup.headers.map( + (header) => html` + + ` + )} + + ` + )} + + + ${repeat( + table.getRowModel().rows, + (row) => row.id, + (row) => + html` + ${row + .getVisibleCells() + .map( + (cell) => html` + + ` + )} + ` + )} + +
+
+ ${header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + +
+
+ ${flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} +
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: DataTable + } +} diff --git a/src/elements/data-table/sort-caret.ts b/src/elements/data-table/sort-caret.ts new file mode 100644 index 0000000..6d589c8 --- /dev/null +++ b/src/elements/data-table/sort-caret.ts @@ -0,0 +1,26 @@ +import { html, LitElement, nothing } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +const ELEMENT_NAME = 'sort-caret' + +@customElement(ELEMENT_NAME) +export class SortCaret extends LitElement { + @property() + type: null | 'desc' | 'asc' = null + + protected render() { + if (!this.type) { + return nothing + } + + const icon = this.type === 'asc' ? 'caret-up' : 'caret-down' + + return html` ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: SortCaret + } +} diff --git a/src/elements/pdf-button/pdf-button.ts b/src/elements/pdf-button/pdf-button.ts new file mode 100644 index 0000000..b3ecba0 --- /dev/null +++ b/src/elements/pdf-button/pdf-button.ts @@ -0,0 +1,66 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +const ELEMENT_NAME = 'pdf-button' + +@customElement(ELEMENT_NAME) +export class PdfButton extends LitElement { + @property() + downloadUrl = '' + @property() + previewUrl = '' + @property() + titleText = '' + + static styles = [ + ...theme, + css` + sl-menu { + padding: 0; + } + + sl-menu-item::part(base) { + padding: 0; + } + + sl-menu-item::part(checked-icon), + sl-menu-item::part(suffix), + sl-menu-item::part(submenu-icon) { + display: none; + } + `, + ] + + render() { + const { downloadUrl, previewUrl, titleText } = this + + return html` + + + + ${titleText} + + + + More options + + + + + + Preview + + + + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: PdfButton + } +} diff --git a/src/elements/pdf-preview/pdf-preview.ts b/src/elements/pdf-preview/pdf-preview.ts new file mode 100644 index 0000000..9efa53f --- /dev/null +++ b/src/elements/pdf-preview/pdf-preview.ts @@ -0,0 +1,168 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { createRef, ref } from 'lit/directives/ref.js' +import { TextLayer, getDocument } from 'pdfjs-dist' + +import { theme } from '@/theme/theme' + +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +// @ts-expect-error pdfjs weirdness +const { pdfjsLib } = globalThis +// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access +pdfjsLib.GlobalWorkerOptions.workerSrc = '/assets/pdf.worker.min.mjs' + +const ELEMENT_NAME = 'pdf-preview' + +@customElement(ELEMENT_NAME) +export class PdfPreview extends LitElement { + private viewer = createRef() + private canvases: HTMLCanvasElement[] = [] + private textLayers: HTMLDivElement[] = [] + + @property() + url = '' + + @property() + password = '' + + static styles = [ + ...theme, + css` + :host { + height: 100%; + min-height: 100%; + } + + #wrapper { + position: relative; + height: 100%; + margin: 0 auto; + overflow-y: auto; + } + + #viewer { + position: absolute; + left: 50%; + transform: translateX(-50%); + } + + canvas { + display: block; + margin-bottom: var(--sl-spacing-large); + } + + .page { + position: relative; + } + + .text-layer { + position: absolute; + inset: 0; + z-index: 0; + width: 100% !important; + height: 100% !important; + overflow: clip; + line-height: 1; + text-align: initial; + caret-color: CanvasText; + opacity: 1; + transform-origin: 0 0; + text-size-adjust: none; + forced-color-adjust: none; + } + + .text-layer :is(span, br) { + position: absolute; + color: transparent; + white-space: pre; + cursor: text; + transform-origin: 0% 0%; + } + `, + ] + + protected async firstUpdated() { + if (!this.viewer.value) { + throw new Error('root not defined') + } + + const pdf = await getDocument({ + url: this.url, + password: this.password, + }).promise + + for (let pageNumber = 0; pageNumber < pdf.numPages; pageNumber++) { + const canvas = document.createElement('canvas') + + const textLayer = document.createElement('div') + textLayer.className = 'text-layer' + + const page = document.createElement('page') + page.className = 'page' + + page.appendChild(canvas) + page.appendChild(textLayer) + + this.canvases.push(canvas) + this.textLayers.push(textLayer) + + this.viewer.value.appendChild(page) + } + + for (let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber++) { + const page = await pdf.getPage(pageNumber) + + const scale = 1.5 + const viewport = page.getViewport({ scale: scale }) + + const canvas = this.canvases[pageNumber - 1] + const context = canvas.getContext('2d') + + if (!context) { + throw new Error('context not defined') + } + + canvas.height = viewport.height + canvas.width = viewport.width + + const textLayer = this.textLayers[pageNumber - 1] + textLayer.style.setProperty('--scale-factor', viewport.scale.toString()) + textLayer.style.height = `${viewport.height}px` + textLayer.style.width = `${viewport.width}px` + textLayer.style.left = `${canvas.offsetLeft}px` + textLayer.style.top = `${canvas.offsetTop}px` + + this.viewer.value.style.width = `${viewport.width}px` + this.viewer.value.style.height = `${viewport.height}px` + + // Render PDF page into canvas context + const renderContext = { + canvasContext: context, + viewport: viewport, + } + await page.render(renderContext).promise + + const textContent = await page.getTextContent() + + const layer = new TextLayer({ + container: textLayer, + textContentSource: textContent, + viewport, + }) + + await layer.render() + } + } + + render() { + return html`
+
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: PdfPreview + } +} diff --git a/src/elements/secure-iframe/secure-iframe.ts b/src/elements/secure-iframe/secure-iframe.ts new file mode 100644 index 0000000..9b7ddee --- /dev/null +++ b/src/elements/secure-iframe/secure-iframe.ts @@ -0,0 +1,92 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { html, css, LitElement } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { createRef, ref, Ref } from 'lit/directives/ref.js' + +import { theme } from '@/theme/theme' + +import { themeSignal } from '@/modules/app/signals/theme-signal' + +const ELEMENT_NAME = 'secure-iframe' + +@customElement(ELEMENT_NAME) +export class SecureIframe extends SignalWatcher(LitElement) { + private ref: Ref = createRef() + + @property() + private sandbox = '' + + @property() + private UNSAFE_html = '' + + private mutationObserver: MutationObserver = new MutationObserver(() => { + this.resizeIframe() + }) + + private resizeObserver: ResizeObserver = new ResizeObserver(() => { + this.resizeIframe() + }) + + disconnectedCallback() { + super.disconnectedCallback() + this.mutationObserver.disconnect() + this.resizeObserver.disconnect() + } + + protected updated(): void { + if (!this.ref.value) { + return + } + + this.mutationObserver.observe(this.ref.value, { + attributes: true, + childList: true, + subtree: true, + }) + + this.resizeObserver.observe(this.ref.value) + + this.ref.value.srcdoc = ` + + + + + + ${this.UNSAFE_html} + + + ` + } + + protected resizeIframe() { + const offsetHeight = + this.ref.value!.contentDocument?.documentElement.offsetHeight ?? 0 + const offset = `${offsetHeight + 18}px` + + setTimeout(() => { + this.ref.value!.style.height = offset + }, 100) // timeout value discovered empirically + // 5 bucks if you can get rid of the timeout + } + + static styles = [ + ...theme, + css` + iframe { + width: 100%; + height: 100%; + border: 0; + } + `, + ] + + render() { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: SecureIframe + } +} diff --git a/src/elements/version-footer/version-footer.ts b/src/elements/version-footer/version-footer.ts new file mode 100644 index 0000000..98fc84a --- /dev/null +++ b/src/elements/version-footer/version-footer.ts @@ -0,0 +1,60 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { type VersionValue } from '@/utils/version/version-values' + +const ELEMENT_NAME = 'version-footer' + +@customElement(ELEMENT_NAME) +export class VersionFooter extends LitElement { + @property() + values: VersionValue[] = [] + + static styles = [ + ...theme, + css` + :host { + padding: var(--sl-spacing-x-small); + color: var(--sl-color-neutral-600); + text-align: center; + background: var(--sl-color-neutral-200); + box-shadow: var(--sl-shadow-large); + } + + a { + margin: 0 var(--sl-spacing-x-small); + color: var(--sl-color-neutral-600); + text-decoration: none; + } + + a, + a:link, + a:visited, + a:active { + color: var(--sl-color-neutral-600); + } + + a:hover { + color: var(--sl-color-primary-500); + } + `, + ] + + render() { + return this.values.length + ? html`${this.values.map( + ({ href, text }, index) => + html` ${text} + ${index < this.values.length - 1 ? ' | ' : ''}` + )}` + : html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: VersionFooter + } +} diff --git a/src/features/project-detail/constants.ts b/src/features/project-detail/constants.ts new file mode 100644 index 0000000..4fba2d7 --- /dev/null +++ b/src/features/project-detail/constants.ts @@ -0,0 +1 @@ +export const ARCHIVED_TOPIC = 'archive' diff --git a/src/features/project-detail/elements/build-item.ts b/src/features/project-detail/elements/build-item.ts new file mode 100644 index 0000000..c471d79 --- /dev/null +++ b/src/features/project-detail/elements/build-item.ts @@ -0,0 +1,115 @@ +import { format } from 'date-fns' +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { ProjectBuild } from '@/modules/projects/types/project-build' + +import { + getQuotePdfUrl, + getReportPdfUrl, + getCsvUrl, + getPdfPageFromName, +} from '@/modules/projects/utils/build-artifacts' + +const ELEMENT_NAME = 'build-item' + +@customElement(ELEMENT_NAME) +export class BuildItem extends LitElement { + @property() + private projectNamespace = '' + @property() + private projectName = '' + @property() + private projectId = 0 + @property() + private build!: ProjectBuild + @property() + private background: 'light' | 'dark' = 'light' + + static styles = [ + ...theme, + css` + #wrapper { + display: flex; + justify-content: space-between; + padding: var(--sl-spacing-small); + } + + #details { + display: flex; + gap: var(--sl-spacing-small); + align-items: center; + } + + .dark { + background: var(--sl-color-gray-100); + } + `, + ] + + render() { + const { id, status, url, createdAt } = this.build + + let badgeColor: 'neutral' | 'success' | 'danger' = 'neutral' + if (status === 'success') { + badgeColor = 'success' + } else if (status === 'failed') { + badgeColor = 'danger' + } + + return html` +
+
+ + #${id} + + + ${status} + + at ${format(createdAt, 'eeee, dd-MM-yyyy HH:mm')} +
+
+ + + + + + + + CSV + +
+
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: BuildItem + } +} diff --git a/src/features/project-detail/elements/cards/crew-card.skeleton.ts b/src/features/project-detail/elements/cards/crew-card.skeleton.ts new file mode 100644 index 0000000..2b69e49 --- /dev/null +++ b/src/features/project-detail/elements/cards/crew-card.skeleton.ts @@ -0,0 +1,21 @@ +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +const ELEMENT_NAME = 'crew-card-skeleton' + +@customElement(ELEMENT_NAME) +export class CrewCardSkeleton extends LitElement { + static styles = [...theme] + + render() { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: CrewCardSkeleton + } +} diff --git a/src/features/project-detail/elements/cards/crew-card.ts b/src/features/project-detail/elements/cards/crew-card.ts new file mode 100644 index 0000000..fb01868 --- /dev/null +++ b/src/features/project-detail/elements/cards/crew-card.ts @@ -0,0 +1,118 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { ProjectDetailsMember } from '@/modules/projects/types/project-details' + +const ELEMENT_NAME = 'crew-card' + +@customElement(ELEMENT_NAME) +export class CrewCard extends LitElement { + @property() + private staff: ProjectDetailsMember[] = [] + @property() + private customers: ProjectDetailsMember[] = [] + + static styles = [ + ...theme, + css` + :host { + --avatar-size: 24px; + } + + sl-card::part(body) { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-x-large); + } + + p { + margin: 0; + } + + sl-card h2 { + margin-bottom: var(--sl-spacing-medium); + } + + .list { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-medium); + margin-top: var(--sl-spacing-medium); + } + + .item { + display: flex; + gap: var(--sl-spacing-x-small); + align-items: center; + } + + .avatar, + .avatar::part(base) { + width: var(--avatar-size); + height: var(--avatar-size); + color: var(--sl-color-neutral-500); + } + `, + ] + + render() { + const { staff, customers } = this + + const lists = [ + { + title: 'Staff', + list: staff, + }, + { + title: 'Stakeholders', + list: customers, + }, + ] + + const defaultAvatar = html`` + + const content = lists.some((x) => x.list.length > 0) + ? html` ${lists + .filter((x) => x.list.length) + .map( + ({ title, list }) => + html`
+

${title}

+
+ ${list.map( + ({ avatar, name, url }) => html` + + ${avatar + ? html` + ` + : defaultAvatar} + ${name} + + ` + )} +
+
` + )}` + : html`
+

Staff

+

This project doesn't have members yet.

+
` + + return html` ${content} ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: CrewCard + } +} diff --git a/src/features/project-detail/elements/cards/findings-card.skeleton.ts b/src/features/project-detail/elements/cards/findings-card.skeleton.ts new file mode 100644 index 0000000..1d4466f --- /dev/null +++ b/src/features/project-detail/elements/cards/findings-card.skeleton.ts @@ -0,0 +1,21 @@ +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +const ELEMENT_NAME = 'findings-card-skeleton' + +@customElement(ELEMENT_NAME) +export class FindingsCardSkeleton extends LitElement { + static styles = [...theme] + + render() { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: FindingsCardSkeleton + } +} diff --git a/src/features/project-detail/elements/cards/findings-card.ts b/src/features/project-detail/elements/cards/findings-card.ts new file mode 100644 index 0000000..2cedb22 --- /dev/null +++ b/src/features/project-detail/elements/cards/findings-card.ts @@ -0,0 +1,162 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { + type ProjectDetailsFinding, + type ProjectDetailsGroupedFindings, +} from '@/modules/projects/types/project-details' + +import '@/features/project-detail/elements/finding-details' + +const emptyText = (type: string): string => `This project doesn't have ${type}.` + +const ELEMENT_NAME = 'findings-card' + +@customElement(ELEMENT_NAME) +export class FindingsCard extends LitElement { + @property() + private findings: ProjectDetailsGroupedFindings[] = [] + @property() + private nonFindings: ProjectDetailsFinding[] = [] + @property() + private projectId = 0 + @property() + private baseUrl = '' + + static styles = [ + ...theme, + css` + sl-card::part(body) { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-x-large); + } + + .findings-content { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-large); + } + + section p { + margin: 0; + } + + .title-badge { + font-size: var(--sl-font-size-x-large); + } + + .badge-findings::part(base) { + background: var(--sl-color-blue-400); + } + + .badge-findings-todo::part(base) { + background: var(--sl-color-teal-500); + } + + .badge-findings-extreme::part(base) { + background: var(--sl-color-danger-500); + } + + .badge-findings-high::part(base) { + background: var(--sl-color-orange-500); + } + + .badge-findings-elevated::part(base) { + background: var(--sl-color-yellow-500); + } + + .badge-findings-moderate::part(base) { + background: var(--sl-color-violet-500); + } + + .badge-findings-low::part(base) { + background: var(--sl-color-fuchsia-500); + } + + .badge-findings-unknown::part(base) { + background: var(--sl-color-neutral-500); + } + + .badge-non-findings::part(base) { + background: var(--sl-color-neutral-400); + } + + sl-card h2, + sl-card h3 { + display: flex; + gap: var(--sl-spacing-small); + align-items: center; + margin-bottom: var(--sl-spacing-medium); + } + `, + ] + + render() { + const { findings, nonFindings, projectId } = this + + const amountFindings = findings + .map((x) => x.findings) + .flatMap((x) => x.length) + .reduce((acc, curr) => acc + curr, 0) + + return html` +
+

+ Findings + ${amountFindings} +

+
+ ${findings.length + ? html` + ${findings.map( + ({ group, findings }) => html` +
+

+ ${group} + ${findings.length} +

+ +
+ ` + )} + ` + : html`

${emptyText('findings')}

`} +
+
+
+

+ Non-findings + ${nonFindings.length} +

+ + ${nonFindings.length + ? html`` + : html`

${emptyText('non-findings')}

`} +
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: FindingsCard + } +} diff --git a/src/features/project-detail/elements/cards/history-card.skeleton.ts b/src/features/project-detail/elements/cards/history-card.skeleton.ts new file mode 100644 index 0000000..fb72652 --- /dev/null +++ b/src/features/project-detail/elements/cards/history-card.skeleton.ts @@ -0,0 +1,21 @@ +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +const ELEMENT_NAME = 'history-card-skeleton' + +@customElement(ELEMENT_NAME) +export class HistoryCardSkeleton extends LitElement { + static styles = [...theme] + + render() { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: HistoryCardSkeleton + } +} diff --git a/src/features/project-detail/elements/cards/history-card.ts b/src/features/project-detail/elements/cards/history-card.ts new file mode 100644 index 0000000..adc3f6e --- /dev/null +++ b/src/features/project-detail/elements/cards/history-card.ts @@ -0,0 +1,145 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { map } from 'lit/directives/map.js' + +import { theme } from '@/theme/theme' + +import { ProjectDetailsHistory } from '@/modules/projects/types/project-details' + +import { getEventIcon } from '../../utils/get-event-icon' +import { getEventText } from '../../utils/get-event-text' + +const ELEMENT_NAME = 'history-card' + +@customElement(ELEMENT_NAME) +export class HistoryCard extends LitElement { + @property() + private history!: ProjectDetailsHistory[] + + @property() + private baseUrl!: string + + static styles = [ + ...theme, + css` + :host { + --icon-size: 32px; + --avatar-size: 24px; + } + + sl-card h2 { + margin-bottom: var(--sl-spacing-medium); + } + + .event { + display: flex; + gap: var(--sl-spacing-large); + align-items: center; + padding-bottom: var(--sl-spacing-medium); + border-bottom: 1px solid var(--sl-color-neutral-200); + } + + sl-details .event:nth-child(1) { + padding-top: var(--sl-spacing-medium); + border-top: 1px solid var(--sl-color-neutral-200); + } + + sl-details .event:last-child { + padding: 0; + border: 0; + } + + sl-details::part(content) { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-large); + } + + .content { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-x-small); + } + + sl-icon.event-icon { + width: var(--icon-size); + height: var(--icon-size); + color: var(--sl-color-primary-600); + } + + .avatar, + .avatar::part(base) { + width: var(--avatar-size); + height: var(--avatar-size); + color: var(--sl-color-neutral-500); + } + + .user-link { + display: flex; + gap: var(--sl-spacing-x-small); + align-items: center; + } + + .time { + margin-left: auto; + } + `, + ] + + render() { + const { history, baseUrl } = this + + const defaultAvatar = html`` + + return html` + +

History

+ ${map( + history, + ({ dateDisplay, events }) => + html` + ${map(events, (event) => { + const { path, userUrl, user, avatar, time } = event + return html` +
+ +
+ + ${avatar + ? html` + ` + : defaultAvatar} + ${user} + + +
+ ${time} +
+ ` + })} +
` + )} +
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: HistoryCard + } +} diff --git a/src/features/project-detail/elements/cards/project-builds.ts b/src/features/project-detail/elements/cards/project-builds.ts new file mode 100644 index 0000000..3c76b92 --- /dev/null +++ b/src/features/project-detail/elements/cards/project-builds.ts @@ -0,0 +1,74 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { map } from 'lit/directives/map.js' + +import { theme } from '@/theme/theme' + +import { ProjectBuild } from '@/modules/projects/types/project-build' + +import '@/features/project-detail/elements/build-item' + +const ELEMENT_NAME = 'project-builds' + +@customElement(ELEMENT_NAME) +export class ProjectBuilds extends LitElement { + @property() + private projectNamespace = '' + @property() + private projectName = '' + @property() + private projectId = 0 + @property() + private builds: ProjectBuild[] = [] + + static styles = [ + ...theme, + css` + sl-card::part(base) { + display: flex; + flex-direction: column; + min-height: 300px; + max-height: 500px; + overflow: auto; + resize: vertical; + } + + sl-card::part(body) { + display: flex; + flex: 1; + align-self: stretch; + width: 100%; + } + + section { + display: flex; + flex: 1; + flex-direction: column; + } + `, + ] + + render() { + return html` +
+ ${map( + this.builds, + (build, index) => + html`` + )} +
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectBuilds + } +} diff --git a/src/features/project-detail/elements/cards/project-chat.skeleton.ts b/src/features/project-detail/elements/cards/project-chat.skeleton.ts new file mode 100644 index 0000000..e332c8e --- /dev/null +++ b/src/features/project-detail/elements/cards/project-chat.skeleton.ts @@ -0,0 +1,67 @@ +import { LitElement, html, css } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +const ELEMENT_NAME = 'project-chat-skeleton' + +const SKELETON_SIZES = [50, 45, 65, 89, 40, 50, 33, 40, 50, 70] +const SKELETON_CSS = SKELETON_SIZES.map( + (size, index) => css` + #chat sl-skeleton:nth-child(${index + 1}) { + // postcss-lit-disable-next-line + width: ${size}%; + } + ` +) + +@customElement(ELEMENT_NAME) +export class ProjectChatSkeleton extends LitElement { + static styles = [ + ...theme, + css` + sl-card::part(base) { + display: flex; + flex-direction: row; + height: 300px; + } + + sl-card::part(body) { + display: flex; + flex: 1; + align-self: stretch; + width: 100%; + } + + #chat { + display: flex; + flex-direction: column; + justify-content: space-between; + width: 100%; + } + `, + ...SKELETON_CSS, + ] + + render() { + return html` + +
+ + + + + + + +
+
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectChatSkeleton + } +} diff --git a/src/features/project-detail/elements/cards/project-chat.ts b/src/features/project-detail/elements/cards/project-chat.ts new file mode 100644 index 0000000..c871da5 --- /dev/null +++ b/src/features/project-detail/elements/cards/project-chat.ts @@ -0,0 +1,78 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +const ELEMENT_NAME = 'project-chat' + +@customElement(ELEMENT_NAME) +export class ProjectChat extends LitElement { + @property() + private chatUrl: string | null = null + + static styles = [ + ...theme, + css` + sl-card::part(base) { + display: flex; + flex-direction: column; + min-height: 300px; + overflow: auto; + resize: vertical; + } + + sl-card::part(body) { + display: flex; + flex: 1; + align-self: stretch; + width: 100%; + } + + section { + display: flex; + flex: 1; + flex-direction: column; + } + + secure-iframe { + width: 100%; + height: 100%; + } + + p { + display: flex; + flex: 1; + gap: var(--sl-spacing-x-small); + align-items: center; + justify-content: center; + margin: 0; + } + + p sl-icon { + position: relative; + bottom: 2px; + } + `, + ] + + render() { + const { chatUrl } = this + + return html` +
+ +
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectChat + } +} diff --git a/src/features/project-detail/elements/cards/recent-changes-card.skeleton.ts b/src/features/project-detail/elements/cards/recent-changes-card.skeleton.ts new file mode 100644 index 0000000..0d1b5de --- /dev/null +++ b/src/features/project-detail/elements/cards/recent-changes-card.skeleton.ts @@ -0,0 +1,21 @@ +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +const ELEMENT_NAME = 'recent-changes-card-skeleton' + +@customElement(ELEMENT_NAME) +export class RecentChangesCardSkeleton extends LitElement { + static styles = [...theme] + + render() { + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: RecentChangesCardSkeleton + } +} diff --git a/src/features/project-detail/elements/cards/recent-changes-card.ts b/src/features/project-detail/elements/cards/recent-changes-card.ts new file mode 100644 index 0000000..f392de2 --- /dev/null +++ b/src/features/project-detail/elements/cards/recent-changes-card.ts @@ -0,0 +1,81 @@ +import { formatDistance } from 'date-fns' +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { ProjectDetailsFinding } from '@/modules/projects/types/project-details' + +const ELEMENT_NAME = 'recent-changes-card' + +@customElement(ELEMENT_NAME) +export class RecentChangesCard extends LitElement { + @property() + private findings: ProjectDetailsFinding[] = [] + + @property() + private now!: Date + + static styles = [ + ...theme, + css` + sl-card h2 { + margin-bottom: var(--sl-spacing-medium); + } + + p { + margin: 0; + } + + #findings { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-medium); + } + + .finding { + display: flex; + gap: var(--sl-spacing-small); + } + + .time { + margin-left: auto; + } + `, + ] + + render() { + const { findings, now } = this + + const content = + findings.length > 0 + ? html`
+ ${findings.map( + ({ iid, title, updatedAt, url }) => + html`
+ #${iid} + ${title} + ${formatDistance(now, updatedAt)} ago +
` + )} +
` + : html`

This project hasn't had any activity yet.

` + + return html` + +

Recent Changes

+ ${content} +
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: RecentChangesCard + } +} diff --git a/src/features/project-detail/elements/cards/title-card.skeleton.ts b/src/features/project-detail/elements/cards/title-card.skeleton.ts new file mode 100644 index 0000000..3bfd6e0 --- /dev/null +++ b/src/features/project-detail/elements/cards/title-card.skeleton.ts @@ -0,0 +1,68 @@ +import { LitElement, html, css } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +const ELEMENT_NAME = 'title-card-skeleton' + +@customElement(ELEMENT_NAME) +export class TitleCardSkeleton extends LitElement { + static styles = [ + ...theme, + css` + header { + display: flex; + justify-content: space-between; + } + + #title { + width: 50%; + height: 28px; + margin: 16px 0; + } + + #title-section { + width: 100%; + } + + #avatar { + width: 128px; + min-width: 128px; + height: 128px; + border-radius: var(--sl-border-radius-medium); + } + + #toolbar { + width: 400px; + height: 41px; + margin-top: var(--sl-spacing-3x-large); + } + + #toolbar sl-skeleton { + position: relative; + top: 4px; + height: 24px; + } + `, + ] + + render() { + return html` +
+
+ +
+ +
+
+ +
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: TitleCardSkeleton + } +} diff --git a/src/features/project-detail/elements/cards/title-card.ts b/src/features/project-detail/elements/cards/title-card.ts new file mode 100644 index 0000000..f92a49a --- /dev/null +++ b/src/features/project-detail/elements/cards/title-card.ts @@ -0,0 +1,354 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { SlDialog, SlInput, SlSelect } from '@shoelace-style/shoelace' +import { format } from 'date-fns' +import { LitElement, html, css } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' +import { map } from 'lit/directives/map.js' +import { createRef, ref } from 'lit/directives/ref.js' + +import { theme } from '@/theme/theme' + +import { Project, ProjectStatus } from '@/modules/projects/types/project' +import { ProjectDetails } from '@/modules/projects/types/project-details' + +import { + PENTEST_STATUSES, + QUOTE_STATUSES, +} from '@/modules/projects/normalizers/normalize-project-status' +import { updateDatesQuery } from '@/modules/projects/queries/update-dates-query' +import { updateProjectQuery } from '@/modules/projects/queries/update-project-query' +import { getPdfPageFromName } from '@/modules/projects/utils/build-artifacts' + +import { ARCHIVED_TOPIC } from '../../constants' +import { archiveProject } from '../../utils/archive-project' + +import '../pdf-password-dialog' + +const ELEMENT_NAME = 'title-card' + +@customElement(ELEMENT_NAME) +export class TitleCard extends SignalWatcher(LitElement) { + private dialogRef = createRef() + private editDatesRef = createRef() + private startDateRef = createRef() + private endDateRef = createRef() + private statusDialogRef = createRef() + private statusRef = createRef() + + @property() + public project!: Project + @property() + public projectDetail!: ProjectDetails + @property() + public onClickReload!: () => void + @property() + public isDetailsLoading = false + + @state() + private startDate: Date | null = null + @state() + private endDate: Date | null = null + + @state() + private status: ProjectStatus | null = null + + public disconnectedCallback() { + this.startDateRef.value?.removeEventListener('sl-change', () => { + this.handleDateChange(this.startDateRef.value!.value, 'startDate') + }) + + this.endDateRef.value?.removeEventListener('sl-change', () => { + this.handleDateChange(this.endDateRef.value!.value, 'endDate') + }) + + this.statusRef.value?.removeEventListener('sl-change', () => { + this.handleStatusChange(this.statusRef.value?.value as ProjectStatus) + }) + } + + protected firstUpdated(): void { + this.startDateRef.value?.addEventListener('sl-change', () => { + this.handleDateChange(this.startDateRef.value!.value, 'startDate') + }) + + this.endDateRef.value?.addEventListener('sl-change', () => { + this.handleDateChange(this.endDateRef.value!.value, 'endDate') + }) + + this.statusRef.value?.addEventListener('sl-change', () => { + this.handleStatusChange(this.statusRef.value?.value as ProjectStatus) + }) + } + + handleDateChange(value: string, type: 'startDate' | 'endDate') { + this[type] = new Date(value) + } + + handleStatusChange(value: ProjectStatus) { + this.status = value + } + + async handleSaveDates() { + await updateDatesQuery.fetch({ + projectId: this.project.id, + endDate: this.endDate, + startDate: this.startDate, + }) + + await this.editDatesRef.value?.hide() + } + + async handleSaveStatus() { + const filtered = this.project.topics.filter( + (x) => !x.startsWith('projectStatus:') + ) + const topics = [...filtered, `projectStatus:${this.status}`] + await updateProjectQuery.fetch([this.project.id, { topics }]) + + await this.statusDialogRef.value?.hide() + } + + static styles = [ + ...theme, + css` + :host { + --avatar-size: 128px; + } + + section { + display: flex; + justify-content: space-between; + } + + #toolbar { + display: flex; + flex-wrap: wrap; + gap: var(--sl-spacing-x-small); + margin-top: var(--sl-spacing-x-large); + } + + #avatar-container { + display: flex; + align-self: center; + width: 128px; + height: 100%; + } + + #avatar { + width: var(--avatar-size); + height: var(--avatar-size); + color: var(--sl-color-primary-500); + } + + .pending-archive { + font-size: var(--sl-font-size-x-large); + color: var(--sl-color-gray-500); + } + + #dates { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-x-small); + margin-top: var(--sl-spacing-medium); + } + `, + ] + + render() { + const { onClickReload } = this + + const { topics, nameWithNamespace, avatar, url, quotePdf, reportPdf } = + this.project + + const avatarElement = avatar + ? html`` + : html`` + + const isArchived = topics.includes(ARCHIVED_TOPIC) + + const isArchivingLoading = updateProjectQuery.status === 'loading' + + const startDate = this.project.startDate + ? format(this.project.startDate, 'yyyy-MM-dd') + : 'TBD' + const endDate = this.project.endDate + ? format(this.project.endDate, 'yyyy-MM-dd') + : 'TBD' + + const statuses = + this.project.type === 'pentest' ? PENTEST_STATUSES : QUOTE_STATUSES + + return html` +
+
+

+ ${nameWithNamespace}${isArchived + ? html` (pending archival)` + : ''} +

+
+ + + Reload + + + + + + GitLab + + + + + + + this.dialogRef.value?.show()} + > + + PDF Password + + + + this.dialogRef.value?.hide()} + >Close + + + { + await archiveProject(this.project.id, this.project.topics) + }} + > + ${isArchived + ? html` + Unarchive` + : html` + Archive`} + + + this.editDatesRef.value?.show()} + >Edit Dates + + Start Date + + End Date + + this.editDatesRef.value?.hide()} + >Close + Save + + + this.statusDialogRef.value?.show()} + >Edit Status + + Status + + ${map( + statuses, + ({ value, label }) => + html`${label}` + )} + + this.statusDialogRef.value?.hide()} + >Close + Save + +
+
+ Status: ${this.project.status} + Start Date: ${startDate} + End Date: ${endDate} +
+
+
${avatarElement}
+
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: TitleCard + } +} diff --git a/src/features/project-detail/elements/finding-details-item.ts b/src/features/project-detail/elements/finding-details-item.ts new file mode 100644 index 0000000..5ef7410 --- /dev/null +++ b/src/features/project-detail/elements/finding-details-item.ts @@ -0,0 +1,122 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { type SlDetails } from '@shoelace-style/shoelace' +import { LitElement, css, html } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' +import { createRef, ref, type Ref } from 'lit/directives/ref.js' + +import { theme } from '@/theme/theme' + +import { ProjectDetailsFinding } from '@/modules/projects/types/project-details' + +import { projectFindingsQuery } from '@/modules/projects/queries/project-finding-query' +import { projectFindingKey } from '@/modules/projects/utils/project-finding-key' + +import { findingMarkdownHtml } from '@/features/project-detail/utils/finding-markdown-html' + +import { createLogger } from '@/utils/logging/create-logger' + +const ELEMENT_NAME = 'finding-details-item' + +const logger = createLogger(ELEMENT_NAME) + +@customElement(ELEMENT_NAME) +export class FindingDetailsItem extends SignalWatcher(LitElement) { + private detailsRef: Ref = createRef() + + @property() + private finding!: ProjectDetailsFinding + @property() + private projectId = 0 + @property() + private baseUrl = '' + @state() + private fetched = false + + public disconnectedCallback() { + super.disconnectedCallback() + this.detailsRef.value?.removeEventListener('sl-show', () => this.onExpand()) + } + + protected async firstUpdated() { + const value = this.detailsRef.value + + await value?.updateComplete + + if (!value) { + logger.debug( + `Could not register finding details element for findings`, + this.finding, + this.projectId + ) + return + } + + value.addEventListener('sl-show', () => this.onExpand()) + } + + protected async updated(changed: Map): Promise { + if (this.fetched) { + if (changed.has('projectId')) { + this.fetched = false + if (!this.detailsRef.value?.hidden) { + await this.detailsRef.value?.hide() + } + } + } + } + + private onExpand() { + if (!this.fetched) { + void projectFindingsQuery.fetch([this.projectId, this.finding.iid]) + this.fetched = true + } + } + + static styles = [ + ...theme, + css` + sl-details::part(content) { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-large); + } + + h3 { + margin: 0; + } + `, + ] + + render() { + const { projectId, finding } = this + const { iid, title } = finding + + const details = + projectFindingsQuery.data?.[projectFindingKey(projectId, iid)] + + let iframe = html`` + + if (details?.isLoading) { + iframe = html`loading...` + } else if (!details?.isLoading && details?.data) { + iframe = html`` + } + + return html` + ${iframe} + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: FindingDetailsItem + } +} diff --git a/src/features/project-detail/elements/finding-details.ts b/src/features/project-detail/elements/finding-details.ts new file mode 100644 index 0000000..57cc4ce --- /dev/null +++ b/src/features/project-detail/elements/finding-details.ts @@ -0,0 +1,51 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' +import { map } from 'lit/directives/map.js' + +import { theme } from '@/theme/theme' + +import { ProjectDetailsFinding } from '@/modules/projects/types/project-details' + +import '@/features/project-detail/elements/finding-details-item' + +const ELEMENT_NAME = 'finding-details' + +@customElement(ELEMENT_NAME) +export class FindingDetails extends LitElement { + @property() + private findings: ProjectDetailsFinding[] = [] + @property() + private projectId = 0 + @property() + private baseUrl = '' + + static styles = [ + ...theme, + css` + :host { + display: flex; + flex-direction: column; + } + `, + ] + + render() { + const { findings, projectId, baseUrl } = this + + return map( + findings, + (finding) => + html`` + ) + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: FindingDetails + } +} diff --git a/src/features/project-detail/elements/pdf-password-dialog.ts b/src/features/project-detail/elements/pdf-password-dialog.ts new file mode 100644 index 0000000..ffa7308 --- /dev/null +++ b/src/features/project-detail/elements/pdf-password-dialog.ts @@ -0,0 +1,75 @@ +import { LitElement, css, html } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { PDF_PASSWORD_KEY } from '@/modules/projects/constants/variables' +import { createVariableQuery } from '@/modules/projects/queries/create-variable-query' + +import { generatePassword } from '@/utils/string/generate-password' + +const ELEMENT_NAME = 'pdf-password-dialog' + +@customElement(ELEMENT_NAME) +export class PdfPasswordDialog extends LitElement { + @property() + password!: string + @property() + isLoading!: boolean + @property() + projectId!: number + + static styles = [ + ...theme, + css` + :host { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-medium); + } + `, + ] + + render() { + if (!this.password) { + return html` + This project doesn't have a PDF password. Click here to generate one and + add it to the project. + { + const password = generatePassword() + await createVariableQuery.fetch([ + this.projectId, + { + key: PDF_PASSWORD_KEY, + masked: true, + protected: true, + value: password, + }, + ]) + }} + > + Generate + + ` + } + + return html` + Be careful storing and sharing the PDF password! + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: PdfPasswordDialog + } +} diff --git a/src/features/project-detail/project-detail.skeleton.ts b/src/features/project-detail/project-detail.skeleton.ts new file mode 100644 index 0000000..bac38e9 --- /dev/null +++ b/src/features/project-detail/project-detail.skeleton.ts @@ -0,0 +1,65 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { html, css, LitElement } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import '@/features/project-detail/elements/cards/crew-card.skeleton' +import '@/features/project-detail/elements/cards/findings-card.skeleton' +import '@/features/project-detail/elements/cards/history-card.skeleton' +import '@/features/project-detail/elements/cards/project-chat.skeleton' +import '@/features/project-detail/elements/cards/recent-changes-card.skeleton' +import '@/features/project-detail/elements/cards/title-card.skeleton' + +const ELEMENT_NAME = 'project-detail-skeleton' + +@customElement(ELEMENT_NAME) +export class ProjectDetailSkeleton extends SignalWatcher(LitElement) { + static styles = [ + ...theme, + css` + :host { + display: grid; + grid-template-areas: + 'title title title' + 'chat chat chat' + 'changes changes crew' + 'findings findings history'; + gap: var(--sl-spacing-large); + } + + #title { + grid-area: title; + } + + #chat { + grid-area: chat; + } + + #changes { + grid-area: changes; + } + + #findings { + grid-area: findings; + } + + #history { + grid-area: history; + } + `, + ] + + render() { + return html` + + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectDetailSkeleton + } +} diff --git a/src/features/project-detail/project-detail.ts b/src/features/project-detail/project-detail.ts new file mode 100644 index 0000000..091077f --- /dev/null +++ b/src/features/project-detail/project-detail.ts @@ -0,0 +1,168 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { html, css, LitElement } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { config } from '@/config' + +import { theme } from '@/theme/theme' + +import { type Project } from '@/modules/projects/types/project' +import { ProjectDetails } from '@/modules/projects/types/project-details' + +import '@/features/project-detail/elements/cards/crew-card' +import '@/features/project-detail/elements/cards/findings-card' +import '@/features/project-detail/elements/cards/history-card' +import '@/features/project-detail/elements/cards/project-builds' +import '@/features/project-detail/elements/cards/project-chat' +import '@/features/project-detail/elements/cards/recent-changes-card' +import '@/features/project-detail/elements/cards/title-card' + +const ELEMENT_NAME = 'project-detail' + +@customElement(ELEMENT_NAME) +export class ProjectDetail extends SignalWatcher(LitElement) { + @property() + private project!: Project + @property() + private projectDetail!: ProjectDetails + @property() + private onClickReload!: () => void + @property() + private isDetailsLoading = false + + static styles = [ + ...theme, + css` + :host { + display: grid; + grid-template-areas: + 'title title title' + 'chat chat chat' + 'changes changes crew' + 'findings findings history'; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); + gap: var(--sl-spacing-large); + } + + #title { + grid-area: title; + } + + #chat { + z-index: 10; + grid-area: chat; + } + + #changes { + z-index: 10; + grid-area: changes; + } + + #findings { + z-index: 10; + grid-area: findings; + } + + #history { + z-index: 10; + grid-area: history; + } + + #crew { + z-index: 10; + grid-area: crew; + } + + sl-tab-panel::part(base) { + padding-top: 0; + } + `, + ] + + render() { + const { project, projectDetail, onClickReload } = this + + const { quoteChannel, pentestChannel, url } = project + const { + staff, + customers, + history, + findings, + nonFindings, + allFindings, + builds, + } = projectDetail + + // #TODO: Fix grid heights when cells dont have equal height + // probably use flex instead of grid for the columns + const recentChanges = allFindings + .slice() + .sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()) + .slice(0, 7) + + const now = new Date() + + const baseUrl = `${config.services.gitlabUrl}/${project.pathWithNamespace}` + + return html` + + + + + Quote Channel + ${project.type === 'pentest' + ? html`Pentest Channel` + : ''} + Builds + + + + ${project.type === 'pentest' + ? html` + + ` + : ''} + + + + + + + + + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectDetail + } +} diff --git a/src/features/project-detail/utils/archive-project.ts b/src/features/project-detail/utils/archive-project.ts new file mode 100644 index 0000000..7724afc --- /dev/null +++ b/src/features/project-detail/utils/archive-project.ts @@ -0,0 +1,14 @@ +import { updateProjectQuery } from '@/modules/projects/queries/update-project-query' + +import { ARCHIVED_TOPIC } from '../constants' + +export const archiveProject = async (id: number, originalTopics: string[]) => { + let topics = originalTopics + if (topics.includes(ARCHIVED_TOPIC)) { + topics = topics.filter((x) => x !== ARCHIVED_TOPIC) + } else { + topics = topics.concat([ARCHIVED_TOPIC]) + } + + await updateProjectQuery.fetch([id, { topics }]) +} diff --git a/src/features/project-detail/utils/finding-markdown-html.ts b/src/features/project-detail/utils/finding-markdown-html.ts new file mode 100644 index 0000000..28fbc6d --- /dev/null +++ b/src/features/project-detail/utils/finding-markdown-html.ts @@ -0,0 +1,85 @@ +import { marked } from 'marked' + +import { ProjectDetailsFinding } from '@/modules/projects/types/project-details' +import { ProjectFindingDetails } from '@/modules/projects/types/project-findings' + +import { rewriteUploads } from '@/modules/projects/utils/rewrite-uploads' + +export const findingMarkdownHtml = ( + // these types are very confusing + // #TODO: rename to something more sane + finding: ProjectDetailsFinding, + details: ProjectFindingDetails, + baseUrl: string +): string => { + const descriptionMarkdown = marked( + rewriteUploads(finding.description, baseUrl), + { + gfm: true, + } + ) as string + + const typeMarkdown = marked(rewriteUploads(details.type, baseUrl), { + gfm: true, + }) as string + + const technicalDescriptionMarkdown = marked( + rewriteUploads(details.technicalDescription, baseUrl), + { + gfm: true, + } + ) as string + + const impactMarkdown = marked(rewriteUploads(details.impact, baseUrl), { + gfm: true, + }) as string + + const recommendationMarkdown = marked( + rewriteUploads(details.recommendation, baseUrl), + { + gfm: true, + } + ) as string + + const sections: { title: string; text: string }[] = [] + + if (typeMarkdown) { + sections.push({ title: 'Type', text: typeMarkdown }) + } + + if (technicalDescriptionMarkdown) { + sections.push({ + title: 'Technical Description', + text: technicalDescriptionMarkdown, + }) + } + + if (impactMarkdown) { + sections.push({ title: 'Impact', text: impactMarkdown }) + } + + if (recommendationMarkdown) { + sections.push({ title: 'Recommendation', text: recommendationMarkdown }) + } + + const content = sections + .map( + ({ title, text }) => ` +
+

${title}

+
+ ${text} +
+ ` + ) + .join('\n') + + const htmlContent = ` +
+ ${descriptionMarkdown} +
+ + ${content}` + + return htmlContent +} diff --git a/src/features/project-detail/utils/get-event-icon.ts b/src/features/project-detail/utils/get-event-icon.ts new file mode 100644 index 0000000..c7117ca --- /dev/null +++ b/src/features/project-detail/utils/get-event-icon.ts @@ -0,0 +1,26 @@ +import { ProjectDetailsEvent } from '@/modules/projects/types/project-details' + +import { isCommentEvent } from '@/modules/projects/utils/is-comment-event' +import { isCreatedEvent } from '@/modules/projects/utils/is-created-event' +import { isPushEvent } from '@/modules/projects/utils/is-push-event' +import { isStateEvent } from '@/modules/projects/utils/is-state-event' + +export const getEventIcon = (event: ProjectDetailsEvent): string => { + if (isPushEvent(event)) { + return 'git' + } else if (isStateEvent(event)) { + return 'pen' + } else if (isCommentEvent(event)) { + return 'chat-text' + } else if (isCreatedEvent(event)) { + return 'file-earmark-plus' + } else if (event.action === 'joined') { + return 'person-add' + } else if (event.action === 'left') { + return 'person-dash' + } else if (event.action === 'imported') { + return 'box-arrow-in-down' + } + + return 'asterisk' +} diff --git a/src/features/project-detail/utils/get-event-text.ts b/src/features/project-detail/utils/get-event-text.ts new file mode 100644 index 0000000..3002185 --- /dev/null +++ b/src/features/project-detail/utils/get-event-text.ts @@ -0,0 +1,25 @@ +import { TemplateResult, html } from 'lit' + +import { ProjectDetailsEvent } from '@/modules/projects/types/project-details' + +import { isCommentEvent } from '@/modules/projects/utils/is-comment-event' +import { isCreatedEvent } from '@/modules/projects/utils/is-created-event' +import { isPushEvent } from '@/modules/projects/utils/is-push-event' +import { isStateEvent } from '@/modules/projects/utils/is-state-event' + +export const getEventText = (event: ProjectDetailsEvent): TemplateResult<1> => { + let content = html`${event.action}` + + if (isPushEvent(event)) { + content = html`pushed ${event.commitCount} commits: ${event.commitTitle}` + } else if (isStateEvent(event)) { + content = html`${event.action} + #${event.targetIid} ${event.targetTitle} ` + } else if (isCommentEvent(event)) { + content = html`${event.action} ${event.targetTitle} (#${event.targetIid}) ` + } else if (isCreatedEvent(event)) { + content = html`created the project` + } + + return content +} diff --git a/src/features/project-list-item-large/project-list-item-large.ts b/src/features/project-list-item-large/project-list-item-large.ts new file mode 100644 index 0000000..67c162c --- /dev/null +++ b/src/features/project-list-item-large/project-list-item-large.ts @@ -0,0 +1,108 @@ +import { formatDistance } from 'date-fns' +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { type Project } from '@/modules/projects/types/project' + +const ELEMENT_NAME = 'project-list-item-large' + +@customElement(ELEMENT_NAME) +export class ProjectListItemLarge extends LitElement { + @property() + private project!: Project + + @property() + private now!: Date + + static styles = [ + ...theme, + css` + :host { + display: flex; + border-collapse: collapse; + border: 1px solid var(--sl-color-gray-200); + + --image-size: 48px; + } + + a { + display: flex; + gap: var(--sl-spacing-small); + align-items: center; + width: 100%; + padding: var(--sl-spacing-x-small); + text-decoration: none; + cursor: pointer; + } + + a:link, + a:visited, + a:active { + color: unset; + } + + a:hover { + background: var(--sl-color-gray-100); + } + + #avatar { + width: var(--image-size); + height: var(--image-size); + color: var(--sl-color-primary-500); + } + + #details { + display: flex; + flex-direction: column; + } + + #name { + font-size: var(--sl-font-size-large); + } + + #updated { + font-size: var(--sl-font-size-small); + color: var(--sl-color-gray-500); + } + `, + ] + + render() { + const { + now, + project: { + avatar, + namespace, + nameWithNamespace, + updatedAt, + pathWithNamespace, + }, + } = this + + const image = avatar || namespace.avatar + + const avatarElement = image + ? html`` + : html`` + + return html` + + ${avatarElement} +
+ ${nameWithNamespace} + Updated ${formatDistance(now, updatedAt)} ago +
+
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectListItemLarge + } +} diff --git a/src/features/projects-overview/columns.ts b/src/features/projects-overview/columns.ts new file mode 100644 index 0000000..43dc505 --- /dev/null +++ b/src/features/projects-overview/columns.ts @@ -0,0 +1,118 @@ +import { ColumnDef } from '@tanstack/lit-table' +import { format, isBefore } from 'date-fns' +import { html } from 'lit' + +import { ProjectWithChannelNames } from '@/modules/projects/utils/add-channel-names' + +const formatDate = (date?: Date | null, defaultTo = 'TBD'): string => + date ? format(date, 'yyyy-MM-dd') : defaultTo + +const compareDates = ( + a: Date | null | undefined, + b: Date | null | undefined +): number => { + if (!a) { + return 1 + } + if (!b) { + return -1 + } + + return a.getTime() - b.getTime() +} + +export const createColumns = ( + now = new Date() +): ColumnDef[] => { + return [ + { + accessorFn: (row) => row.project.pathWithNamespace, + header: 'Project', + cell: ({ row }) => + html`${row.original.project.pathWithNamespace}`, + }, + { + accessorFn: (row) => row.project.type, + header: 'Type', + cell: ({ row }) => + html`${row.original.project.type}`, + }, + { + accessorFn: (row) => row.project.status, + header: 'Status', + cell: (data) => data.getValue(), + }, + { + accessorFn: (row) => row.project.startDate, + header: 'Start Date', + cell: ({ row }) => html`${formatDate(row.original.project.startDate)}`, + sortingFn: (a, b) => + compareDates( + a.original.project.startDate, + b.original.project.startDate + ), + }, + { + accessorFn: (row) => row.project.endDate, + header: 'End Date', + cell: ({ row }) => { + const isBeforeNow = row.original.project.endDate + ? isBefore(row.original.project.endDate, now) + : false + + return html`${formatDate(row.original.project.endDate)}` + }, + sortingFn: (a, b) => + compareDates(a.original.project.endDate, b.original.project.endDate), + }, + { + accessorFn: (row) => row.project.lastActivityAt, + header: 'Last GitLab Activity', + cell: ({ row }) => + html`${formatDate(row.original.project.lastActivityAt, 'Unknown')}`, + sortingFn: (a, b) => + compareDates( + a.original.project.lastActivityAt, + b.original.project.lastActivityAt + ), + }, + { + accessorFn: (row) => row.quoteChannel?.lastUpdatedAt, + header: 'Last Quote Channel Activity', + cell: ({ row }) => + html`${formatDate( + row.original.quoteChannel?.lastUpdatedAt, + 'Unknown' + )}`, + sortingFn: (a, b) => + compareDates( + a.original.quoteChannel?.lastUpdatedAt, + b.original.quoteChannel?.lastUpdatedAt + ), + }, + { + accessorFn: (row) => row.pentestChannel?.lastUpdatedAt, + header: 'Last Pentest Channel Activity', + cell: ({ row }) => + html`${formatDate( + row.original.pentestChannel?.lastUpdatedAt, + 'Unknown' + )}`, + sortingFn: (a, b) => + compareDates( + a.original.pentestChannel?.lastUpdatedAt, + b.original.pentestChannel?.lastUpdatedAt + ), + }, + ] +} diff --git a/src/features/projects-overview/projects-overview.ts b/src/features/projects-overview/projects-overview.ts new file mode 100644 index 0000000..df11b00 --- /dev/null +++ b/src/features/projects-overview/projects-overview.ts @@ -0,0 +1,60 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { ColumnDef } from '@tanstack/lit-table' +import { LitElement, html, css } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { Project } from '@/modules/projects/types/project' + +import { + ProjectWithChannelNames, + addChannelNames, +} from '@/modules/projects/utils/add-channel-names' + +import { createColumns } from './columns' + +import { chatSubscriptionsSignal } from '@/modules/chat/signals/chat-subscriptions-signal' + +import '@/elements/data-table/data-table' + +const ELEMENT_NAME = 'projects-overview' + +@customElement(ELEMENT_NAME) +export class ProjectsOverview extends SignalWatcher(LitElement) { + @property() + projects: Project[] = [] + + @state() + columns: ColumnDef[] = createColumns(new Date()) + + static styles = [ + ...theme, + css` + :host { + text-align: left; + } + `, + ] + + render() { + const projectsWithChannels = addChannelNames( + this.projects, + chatSubscriptionsSignal.subscriptions + ) + + return html` +

Overview

+ +
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectsOverview + } +} diff --git a/src/features/side-bar/constants.ts b/src/features/side-bar/constants.ts new file mode 100644 index 0000000..9818a2e --- /dev/null +++ b/src/features/side-bar/constants.ts @@ -0,0 +1,25 @@ +import { config } from '@/config' + +export type Service = { + title: string + icon: string + href: string +} + +export const SERVICES: Service[] = [ + { + title: 'Rocket.Chat', + icon: 'rocket-takeoff', + href: config.services.rocketChatUrl, + }, + { + title: 'GitLab', + icon: 'gitlab', + href: config.services.gitlabUrl, + }, + { + title: 'CodiMD', + icon: 'journals', + href: config.services.codiMdUrl, + }, +] diff --git a/src/features/side-bar/elements/project-list-item.ts b/src/features/side-bar/elements/project-list-item.ts new file mode 100644 index 0000000..2a5b7e9 --- /dev/null +++ b/src/features/side-bar/elements/project-list-item.ts @@ -0,0 +1,145 @@ +import { formatDistance } from 'date-fns' +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { type Project } from '@/modules/projects/types/project' + +import { ChatSubscription } from '@/modules/chat/types/chat-subscription' + +const ELEMENT_NAME = 'project-list-item' + +@customElement(ELEMENT_NAME) +export class ProjectListItem extends LitElement { + @property() + private project!: Project + @property() + private quoteChannel?: ChatSubscription + @property() + private pentestChannel?: ChatSubscription + + static styles = [ + ...theme, + css` + :host { + display: flex; + + --image-size: 32px; + } + + a { + display: flex; + gap: var(--sl-spacing-small); + align-items: center; + width: 100%; + padding: var(--sl-spacing-x-small); + text-decoration: none; + cursor: pointer; + } + + a:link, + a:visited, + a:active { + color: unset; + } + + a:hover { + background: var(--sl-color-gray-100); + } + + #avatar { + width: var(--image-size); + height: var(--image-size); + color: var(--sl-color-primary-500); + } + + #details { + display: flex; + flex-direction: column; + line-height: var(--sl-line-height-dense); + } + + #namespace { + font-size: var(--sl-font-size-small); + color: var(--sl-color-gray-600); + } + + #chat { + margin-left: auto; + } + `, + ] + + render() { + const { + pathWithNamespace, + avatar, + namespace, + name, + isPentest, + lastActivityAt, + } = this.project + + const image = avatar || namespace.avatar + + const avatarElement = image + ? html`` + : html`` + + const now = new Date() + + const lastGitLabActivity = formatDistance(lastActivityAt, now) + const lastQuoteActivity = this.quoteChannel + ? html`Quote updated + ${formatDistance(this.quoteChannel.lastUpdatedAt, now)} ago` + : '' + const lastPentestActivity = this.pentestChannel + ? html`Pentest updated + ${formatDistance(this.pentestChannel.lastUpdatedAt, now)} ago` + : '' + + return html` + + ${avatarElement} +
+ ${namespace.name} + ${isPentest ? 'pentest' : 'quote'}Status: ${this.project.status} + ${name} + + GitLab updated ${lastGitLabActivity} ago + ${lastQuoteActivity} + ${lastPentestActivity} +
+
+ ${this.quoteChannel?.unread + ? html` + ${this.quoteChannel?.unread} + ` + : ''} + ${this.pentestChannel?.unread + ? html` + ${this.pentestChannel?.unread}` + : ''} +
+
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectListItem + } +} diff --git a/src/features/side-bar/elements/ros-projects.ts b/src/features/side-bar/elements/ros-projects.ts new file mode 100644 index 0000000..b6a8ad1 --- /dev/null +++ b/src/features/side-bar/elements/ros-projects.ts @@ -0,0 +1,236 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { SlInput, SlSelect } from '@shoelace-style/shoelace' +import { LitElement, html, css } from 'lit' +import { customElement, property, state } from 'lit/decorators.js' +import { Ref, createRef, ref } from 'lit/directives/ref.js' + +import { theme } from '@/theme/theme' + +import { type Project } from '@/modules/projects/types/project' + +import { addChannelNames } from '@/modules/projects/utils/add-channel-names' + +import { + SortDirection, + SortOption, + TypeFilter, + filterProjects, + sortProjects, +} from '../utils' + +import { ChatSubscriptionMap } from '@/modules/chat/signals/chat-subscriptions-signal' + +import '@/features/side-bar/elements/project-list-item' + +const ELEMENT_NAME = 'ros-projects' + +@customElement(ELEMENT_NAME) +export class RosProjects extends SignalWatcher(LitElement) { + private searchRef: Ref = createRef() + private typeFilterRef: Ref = createRef() + private sortOptionRef: Ref = createRef() + private sortDirectionRef: Ref = createRef() + + @property() + private projects!: Project[] + @property() + private subscriptions!: ChatSubscriptionMap + @property() + private newProjectHref!: string + @property() + private onReload!: () => undefined + @property() + private isLoading = true + + @state() + private search = '' + @state() + private typeFilter: TypeFilter = 'all' + @state() + private sortOption: SortOption = 'name' + @state() + private sortDirection: SortDirection = 'asc' + + disconnectedCallback(): void { + super.disconnectedCallback() + + this.searchRef.value?.removeEventListener('sl-input', () => + this.handleSearch() + ) + + this.typeFilterRef.value?.removeEventListener('sl-change', () => { + this.handleType() + }) + + this.sortOptionRef.value?.removeEventListener('sl-change', () => { + this.handleSortOption() + }) + + this.sortDirectionRef.value?.removeEventListener('sl-change', () => { + this.handleSortDirection() + }) + } + + protected firstUpdated(): void { + this.searchRef.value?.addEventListener('sl-input', () => { + this.handleSearch() + }) + + this.typeFilterRef.value?.addEventListener('sl-change', () => { + this.handleType() + }) + + this.sortOptionRef.value?.addEventListener('sl-change', () => { + this.handleSortOption() + }) + + this.sortDirectionRef.value?.addEventListener('sl-change', () => { + this.handleSortDirection() + }) + } + + private handleSearch() { + this.search = this.searchRef.value?.value ?? '' + } + + private handleType() { + this.typeFilter = this.typeFilterRef.value?.value as TypeFilter + } + + private handleSortOption() { + this.sortOption = this.sortOptionRef.value?.value as SortOption + } + private handleSortDirection() { + this.sortDirection = this.sortDirectionRef.value?.value as SortDirection + } + + static styles = [ + ...theme, + css` + #projects-header { + display: flex; + align-items: center; + justify-content: space-between; + } + + #projects-header sl-button { + justify-self: flex-end; + } + + #search { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-medium); + padding-top: var(--sl-spacing-medium); + } + + #search-input, + #search-input::part(form-control) { + display: inline-block; + width: 100%; + color: var(--sl-color-gray-600); + } + + #search-input sl-icon { + color: var(--sl-color-gray-600); + } + + #search-input::part(input)::placeholder { + color: var(--sl-color-gray-500); + } + + #search-results { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-small); + } + + #filters { + display: flex; + gap: var(--sl-spacing-small); + } + `, + ] + + render() { + const { projects, newProjectHref, onReload } = this + + const filtered = filterProjects(projects, this.search, this.typeFilter) + + const projectsWithChannels = addChannelNames(filtered, this.subscriptions) + + const sorted = sortProjects( + projectsWithChannels, + this.sortOption, + this.sortDirection + ) + + return html` +
+

Projects

+
+ + Reload + + + + New Project + +
+
+ + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: RosProjects + } +} diff --git a/src/features/side-bar/elements/ros-services.ts b/src/features/side-bar/elements/ros-services.ts new file mode 100644 index 0000000..7e8afc7 --- /dev/null +++ b/src/features/side-bar/elements/ros-services.ts @@ -0,0 +1,69 @@ +import { LitElement, html, css } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { type Service } from '@/features/side-bar/constants' + +const ELEMENT_NAME = 'ros-services' + +@customElement(ELEMENT_NAME) +export class RosServices extends LitElement { + @property() + private services: Service[] = [] + + static styles = [ + ...theme, + css` + #services { + display: grid; + grid-template-columns: auto auto; + grid-gap: var(--sl-spacing-small); + padding: var(--sl-spacing-medium); + } + + #services a:link, + #services a:active, + #services a:visited { + color: var(--sl-color-gray-600); + text-decoration: none; + } + + #services a:hover { + color: var(--sl-color-primary-500); + } + + .services-item span { + display: flex; + gap: var(--sl-spacing-x-small); + align-items: baseline; + } + + .service-item sl-icon { + margin-right: var(--sl-spacing-3x-small); + } + `, + ] + + render() { + const { services } = this + + return html` +

ROS Services

+
+ ${services.map( + ({ title, icon, href }) => + html` + ${title} + ` + )} +
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: RosServices + } +} diff --git a/src/features/side-bar/side-bar.ts b/src/features/side-bar/side-bar.ts new file mode 100644 index 0000000..f1a28ca --- /dev/null +++ b/src/features/side-bar/side-bar.ts @@ -0,0 +1,69 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { html, css, LitElement } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { AppRoute } from '@/routes' + +import { projectsQuery } from '@/modules/projects/queries/projects-query' + +import { SERVICES } from '@/features/side-bar/constants' + +import { chatSubscriptionsSignal } from '@/modules/chat/signals/chat-subscriptions-signal' + +import '@/features/side-bar/elements/ros-services' +import '@/features/side-bar/elements/ros-projects' + +const ELEMENT_NAME = 'side-bar' + +@customElement(ELEMENT_NAME) +export class SideBar extends SignalWatcher(LitElement) { + static styles = [ + ...theme, + css` + :host { + position: relative; + z-index: 20; + display: flex; + flex-direction: column; + gap: var(--sl-spacing-medium); + min-width: 324px; + max-width: 324px; + padding: var(--content-padding); + overflow-y: auto; + background: var(--sl-color-neutral-0); + } + + ros-services::part(heading), + ros-projects::part(heading) { + margin-bottom: 0; + font-size: var(--sl-font-size-medium); + font-weight: var(--sl-font-weight-normal); + color: var(--sl-color-primary-600); + text-transform: uppercase; + } + `, + ] + render() { + const all = projectsQuery.data?.all ?? [] + const sorted = all.slice().sort((a, b) => a.name.localeCompare(b.name)) + + return html` + + + ` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: SideBar + } +} diff --git a/src/features/side-bar/utils.ts b/src/features/side-bar/utils.ts new file mode 100644 index 0000000..1ea9c42 --- /dev/null +++ b/src/features/side-bar/utils.ts @@ -0,0 +1,102 @@ +import { Project } from '@/modules/projects/types/project' + +import { ProjectWithChannelNames } from '@/modules/projects/utils/add-channel-names' + +export type TypeFilter = 'all' | 'quote' | 'pentest' +export type SortOption = 'name' | 'gitlabActivity' | 'chatActivity' +export type SortDirection = 'asc' | 'dsc' + +const getTime = ( + project: ProjectWithChannelNames, + fn: typeof Math.min | typeof Math.max +): number => { + const latestDate = fn( + project.pentestChannel?.lastUpdatedAt.getTime() ?? 0, + project.quoteChannel?.lastUpdatedAt.getTime() ?? 0 + ) + + return latestDate +} + +export const sortProjects = ( + projects: ProjectWithChannelNames[], + option: SortOption, + direction: SortDirection +): ProjectWithChannelNames[] => { + let result = projects + + switch (option) { + case 'name': + result = result.sort((a, b) => + a.project.name.localeCompare(b.project.name) + ) + break + + case 'gitlabActivity': + result = result.sort( + (a, b) => + a.project.lastActivityAt.getTime() - + b.project.lastActivityAt.getTime() + ) + break + + case 'chatActivity': + { + const mathFn = direction === 'dsc' ? Math.max : Math.min + result = result.sort((a, b) => getTime(a, mathFn) - getTime(b, mathFn)) + } + + break + + default: + break + } + + return direction === 'dsc' ? result.reverse() : result +} + +export const filterProjects = ( + projects: Project[], + search: string, + type: TypeFilter +) => { + let result = projects + + result = result.filter((project) => { + if (type === 'pentest') { + return project.isPentest + } else if (type === 'quote') { + return project.isQuote + } + + return project + }) + + if (search) { + result = projects.filter((p) => { + if ( + p.name + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .toLocaleLowerCase() + .includes(search) + ) { + return true + } + + if ( + p.namespace.name + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .toLocaleLowerCase() + .includes(search) + ) { + return true + } + + return false + }) + } + + return result +} diff --git a/src/features/top-bar/top-bar.style.ts b/src/features/top-bar/top-bar.style.ts new file mode 100644 index 0000000..dc0c1f6 --- /dev/null +++ b/src/features/top-bar/top-bar.style.ts @@ -0,0 +1,129 @@ +import { CSSResult, css } from 'lit' + +import { theme } from '@/theme/theme' + +export const topBarStyles: CSSResult[] = [ + ...theme, + css` + :host { + --avatar-size: 42px; + --danger-color: var(--sl-color-red-600); + --danger-hover-color: var(--sl-color-red-500); + } + + .wrapper { + position: relative; + z-index: 100; + display: flex; + align-items: center; + height: 80px; + padding: 0 0 0 var(--sl-spacing-large); + background: var(--sl-color-primary-500); + box-shadow: var(--sl-shadow-large); + } + + .wrapper-danger { + background: var(--danger-color); + } + + #content { + display: flex; + flex-grow: 1; + align-items: center; + height: 100%; + font-size: var(--sl-font-size-large); + color: var(--sl-color-neutral-0); + } + + .dropdown { + display: flex; + align-items: stretch; + height: 100%; + } + + #avatar::part(base) { + width: var(--avatar-size); + height: var(--avatar-size); + } + + h1 { + margin: 0; + font-size: var(--sl-font-size-large); + font-weight: var(--sl-font-weight-normal); + color: var(--sl-color-neutral-0); + } + + #brand { + margin-right: auto; + } + + #content a:link { + text-decoration: none; + } + + .menu { + display: flex; + gap: var(--sl-spacing-small); + align-items: center; + height: 100%; + padding: 0 var(--sl-spacing-medium); + color: var(--sl-color-neutral-0); + cursor: pointer; + } + + #menu-button { + color: var(--sl-color-neutral-0); + } + + .menu:hover, + #menu-button:hover, + #menu-button::part(base):hover { + color: var(--sl-color-neutral-0); + background: var(--sl-color-primary-600); + } + + .wrapper-danger .menu:hover, + .wrapper-danger #menu-button:hover, + .wrapper-danger #menu-button::part(base):hover { + color: var(--sl-color-neutral-0); + background: var(--danger-hover-color); + } + + sl-dialog::part(title) { + color: var(--sl-color-neutral-1000); + } + + sl-dialog::part(body) { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-medium); + } + + .gitlab-footer { + display: flex; + gap: var(--sl-spacing-small); + justify-content: end; + } + + #gitlab-top-badge { + margin-right: var(--sl-spacing-small); + } + + #gitlab-top-badge, + #gitlab-top-badge sl-icon-button::part(base) { + color: var(--sl-color-neutral-0); + cursor: pointer; + } + + #gitlab-top-badge:hover sl-icon-button::part(base), + #gitlab-top-badge::part(base):hover { + background: var(--danger-hover-color); + } + + .menu-label::part(base) { + display: flex; + gap: var(--sl-spacing-x-small); + align-items: center; + } + `, +] diff --git a/src/features/top-bar/top-bar.ts b/src/features/top-bar/top-bar.ts new file mode 100644 index 0000000..103fe46 --- /dev/null +++ b/src/features/top-bar/top-bar.ts @@ -0,0 +1,245 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { + SlDialog, + SlInput, + SlMenu, + SlMenuItem, + SlRequestCloseEvent, +} from '@shoelace-style/shoelace' +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' +import { Ref, createRef, ref } from 'lit/directives/ref.js' + +import { + Theme, + fromLocalStorage, + getTheme, + registerTheme, + setLocalStorage, +} from '@/theme/utils/register-theme' + +import { appSignal } from '@/modules/app/signals/app-signal' + +import { userQuery } from '@/modules/user/queries/user-query' + +import { topBarStyles } from './top-bar.style' +import { toggleCheckBoxes } from './utils/toggle-checkboxes' + +const ELEMENT_NAME = 'top-bar' + +@customElement(ELEMENT_NAME) +export class TopBar extends SignalWatcher(LitElement) { + static styles = topBarStyles + + private buttonRefs = { + [Theme.Dark]: createRef(), + [Theme.Light]: createRef(), + [Theme.System]: createRef(), + } + + private menuRef: Ref = createRef() + private gitlabTokenRef: Ref = createRef() + private gitlabDialogRef: Ref = createRef() + + disconnectedCallback(): void { + super.disconnectedCallback() + this.gitlabDialogRef.value?.removeEventListener('sl-request-close', (e) => + this.onOverlayClose(e) + ) + } + + private processChecked( + theme: Theme, + mode: 'first-run' | 'change' = 'first-run' + ) { + toggleCheckBoxes(theme, this.buttonRefs) + + if (mode === 'change') { + setLocalStorage(theme) + registerTheme('once') + this.requestUpdate() + } + } + + onOverlayClose(e: SlRequestCloseEvent) { + if (e.detail.source === 'overlay') { + appSignal.setShowGitLabTokenDialog(false) + } + } + + protected firstUpdated() { + const theme = fromLocalStorage() + this.processChecked(theme) + + this.menuRef.value?.addEventListener('sl-select', (e) => { + this.processChecked(e.detail.item.value as Theme, 'change') + }) + + this.gitlabDialogRef.value?.addEventListener('sl-request-close', (e) => + this.onOverlayClose(e) + ) + } + + render() { + const { avatar, name } = userQuery.data ?? {} + + const currentTheme = getTheme() + const iconName = currentTheme === Theme.Light ? 'sun' : 'moon-stars' + + const gitlabToken = appSignal.gitLabToken + + const gitlabMode = gitlabToken ? 'danger' : 'warning' + const gitlabModeText = gitlabToken ? 'ELEVATED ACCESS' : 'Read Access' + + const wrapperClasses = ['wrapper'] + + if (gitlabToken) { + wrapperClasses.push('wrapper-danger') + } + + return html`
+
+

Radically Open Security

+ + ${gitlabMode === 'danger' + ? html` { + appSignal.setGitLabToken('') + }} + id="gitlab-top-badge" + variant="danger" + pill + pulse + >GitLab Access Elevated + ` + : ''} + + + + + + + GitLab + ${gitlabModeText} + + + appSignal.setShowGitLabTokenDialog(true)} + > + + ${gitlabToken ? 'Change token' : 'Set token'} + + ${gitlabToken + ? html` { + appSignal.set({ + gitLabToken: '', + showGitLabTokenDialog: false, + }) + }} + > + + Clear token + ` + : ''} + + + + + + Warning
+ Do not share your GitLab token with anyone +
+ + +
+ + + + + Theme + + + Light + + + + Dark + + + + + System + + + +
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: TopBar + } +} diff --git a/src/features/top-bar/utils/toggle-checkboxes.ts b/src/features/top-bar/utils/toggle-checkboxes.ts new file mode 100644 index 0000000..e84572d --- /dev/null +++ b/src/features/top-bar/utils/toggle-checkboxes.ts @@ -0,0 +1,23 @@ +import { type Ref } from 'lit/directives/ref.js' + +import { type Theme } from '@/theme/utils/register-theme' + +export const toggleCheckBoxes = ( + theme: Theme, + refs: { + [Theme.Dark]: Ref + [Theme.Light]: Ref + [Theme.System]: Ref + } +) => { + const entries = Object.entries(refs) as [Theme, Ref][] + + const toSet = entries.find(([key]) => key === theme) + const toRemove = entries.filter(([key]) => key !== theme) + + if (toSet) { + toSet[1].value?.setAttribute('checked', 'true') + } + + toRemove.forEach((entry) => entry[1].value?.removeAttribute('checked')) +} diff --git a/src/modules/app/signals/app-signal.ts b/src/modules/app/signals/app-signal.ts new file mode 100644 index 0000000..f1b8d86 --- /dev/null +++ b/src/modules/app/signals/app-signal.ts @@ -0,0 +1,37 @@ +import { signal as createSignal } from '@lit-labs/preact-signals' + +type AppSignalValue = { gitLabToken: string; showGitLabTokenDialog: boolean } + +const signal = createSignal({ + gitLabToken: import.meta.env.VITE_SECRET_GITLAB_TOKEN ?? '', + showGitLabTokenDialog: false, +}) + +const setSignal = (partial: Partial) => { + signal.value = { + ...signal.value, + ...partial, + } +} + +export const appSignal = { + get gitLabToken() { + return signal.value.gitLabToken + }, + get showGitLabTokenDialog() { + return signal.value.showGitLabTokenDialog + }, + set: setSignal, + setShowGitLabTokenDialog: (value: boolean) => { + setSignal({ + showGitLabTokenDialog: value, + }) + }, + setGitLabToken: (value: string) => { + setSignal({ + gitLabToken: value, + }) + }, +} + +export type AppSignal = typeof appSignal diff --git a/src/modules/app/signals/theme-signal.ts b/src/modules/app/signals/theme-signal.ts new file mode 100644 index 0000000..8ad4924 --- /dev/null +++ b/src/modules/app/signals/theme-signal.ts @@ -0,0 +1,14 @@ +import { signal as createSignal } from '@lit-labs/preact-signals' + +import { Theme } from '@/theme/utils/register-theme' + +const signal = createSignal('light' as Theme) + +export const themeSignal = { + get theme() { + return signal.value + }, + set: (theme: Theme) => { + signal.value = theme + }, +} diff --git a/src/modules/app/utils/update-title.ts b/src/modules/app/utils/update-title.ts new file mode 100644 index 0000000..8fa20e6 --- /dev/null +++ b/src/modules/app/utils/update-title.ts @@ -0,0 +1,10 @@ +const DEFAULT_TITLE = 'R♡S Dashboard' + +export const updateTitle = (value?: string) => { + if (!value) { + document.title = DEFAULT_TITLE + return + } + + document.title = `${value} | ${DEFAULT_TITLE}` +} diff --git a/src/modules/auth/client/auth-client.ts b/src/modules/auth/client/auth-client.ts new file mode 100644 index 0000000..a0f10ee --- /dev/null +++ b/src/modules/auth/client/auth-client.ts @@ -0,0 +1,20 @@ +import { + OidcClient, + TokenAutomaticRenewMode, + TokenRenewMode, +} from '@axa-fr/oidc-client' + +import { config } from '@/config' + +export const authClient = OidcClient.getOrCreate(() => fetch)({ + client_id: config.oidc.clientId, + authority: config.oidc.authority, + scope: config.oidc.scope, + redirect_uri: `${window.location.origin}${config.oidc.redirectPath}`, + service_worker_relative_url: '/OidcServiceWorker.js', + refresh_time_before_tokens_expiration_in_second: 40, + service_worker_only: true, + token_renew_mode: TokenRenewMode.access_token_invalid, + token_automatic_renew_mode: + TokenAutomaticRenewMode.AutomaticOnlyWhenFetchExecuted, +}) diff --git a/src/modules/auth/queries/auth-callback-query.ts b/src/modules/auth/queries/auth-callback-query.ts new file mode 100644 index 0000000..62e285e --- /dev/null +++ b/src/modules/auth/queries/auth-callback-query.ts @@ -0,0 +1,7 @@ +import { loginCallback } from '@/modules/auth/services/login-callback' + +import { createQuery } from '@/utils/signal/query/create-query' + +export const authCallbackQuery = createQuery( + loginCallback +) diff --git a/src/modules/auth/queries/auth-ensure-query.ts b/src/modules/auth/queries/auth-ensure-query.ts new file mode 100644 index 0000000..ccdbd02 --- /dev/null +++ b/src/modules/auth/queries/auth-ensure-query.ts @@ -0,0 +1,5 @@ +import { ensureAuth } from '@/modules/auth/services/ensure-auth' + +import { createQuery } from '@/utils/signal/query/create-query' + +export const authEnsureQuery = createQuery(ensureAuth) diff --git a/src/modules/auth/services/ensure-auth.ts b/src/modules/auth/services/ensure-auth.ts new file mode 100644 index 0000000..b90ea45 --- /dev/null +++ b/src/modules/auth/services/ensure-auth.ts @@ -0,0 +1,31 @@ +import { authClient } from '@/modules/auth/client/auth-client' +import { isCallbackRoute } from '@/modules/auth/utils/is-callback-route' + +import { createLogger } from '@/utils/logging/create-logger' + +const logger = createLogger('ensure-auth') + +export const ensureAuth = async ( + redirectTo: string = window.location.pathname +): Promise => { + logger.debug('checking auth') + + if (isCallbackRoute()) { + logger.debug('is callback route, do nothing') + return false + } + + logger.debug('is not callback route, checking auth session') + + const sessionStatus = await authClient.tryKeepExistingSessionAsync() + logger.debug(`session found: ${sessionStatus}`) + + if (!authClient.tokens) { + logger.debug(`not authenticated, authenticating...`) + await authClient.loginAsync(redirectTo) + return false + } + + logger.debug('is authenticated') + return true +} diff --git a/src/modules/auth/services/login-callback.ts b/src/modules/auth/services/login-callback.ts new file mode 100644 index 0000000..df9c970 --- /dev/null +++ b/src/modules/auth/services/login-callback.ts @@ -0,0 +1,18 @@ +import { authClient } from '@/modules/auth/client/auth-client' + +import { createLogger } from '@/utils/logging/create-logger' + +const logger = createLogger('login-callback') + +export const loginCallback = async (): Promise => { + logger.debug('executing oidc callback') + const { callbackPath } = await authClient.loginCallbackAsync() + + if (!authClient.tokens) { + logger.debug('callback unsuccessful. see development console') + return false + } + + logger.debug('callback successful') + return callbackPath +} diff --git a/src/modules/auth/utils/is-callback-route.ts b/src/modules/auth/utils/is-callback-route.ts new file mode 100644 index 0000000..f370e9e --- /dev/null +++ b/src/modules/auth/utils/is-callback-route.ts @@ -0,0 +1,5 @@ +import { config } from '@/config' + +export const isCallbackRoute = (): boolean => { + return window.location.href.includes(config.oidc.redirectPath) +} diff --git a/src/modules/chat/chat-service.ts b/src/modules/chat/chat-service.ts new file mode 100644 index 0000000..8152533 --- /dev/null +++ b/src/modules/chat/chat-service.ts @@ -0,0 +1,75 @@ +import { chatSubscriptionsSignal } from './signals/chat-subscriptions-signal' +import { + RocketChatEvent, + onRocketChatMessage, +} from './utils/on-rocket-chat-event' + +export type ChatServiceResult = { + disconnect: () => void +} + +type RocketChatNormalEvent = { + eventName: string +} + +const isRocketChatMessage = ( + e: MessageEvent | MessageEvent +): e is MessageEvent => { + return Boolean((e as MessageEvent).data.type) +} + +const isRocketChatNormalEvent = ( + e: MessageEvent | MessageEvent +): e is MessageEvent => { + return Boolean((e as MessageEvent).data.eventName) +} + +const ONE_MINUTE = 1000 * 60 + +export const chatService = (iframe: HTMLIFrameElement): ChatServiceResult => { + let init = false + let interval: NodeJS.Timeout | null = null + + window.addEventListener( + 'message', + ( + e: MessageEvent | MessageEvent + ) => { + isRocketChatMessage(e) && onRocketChatMessage(e) + + if (init) { + return + } + + if ( + isRocketChatNormalEvent(e) && + e.data.eventName !== 'Custom_Script_Logged_In' + ) { + return + } + + init = true + + chatSubscriptionsSignal.refresh(iframe) + + interval = setInterval(() => { + chatSubscriptionsSignal.refresh(iframe) + }, ONE_MINUTE) + } + ) + + return { + disconnect: () => { + if (interval) { + clearInterval(interval) + } + + window.removeEventListener( + 'message', + (e: MessageEvent) => { + onRocketChatMessage(e) + } + ) + }, + } +} diff --git a/src/modules/chat/normalizers/normalize-chat-subscription.ts b/src/modules/chat/normalizers/normalize-chat-subscription.ts new file mode 100644 index 0000000..7c3be68 --- /dev/null +++ b/src/modules/chat/normalizers/normalize-chat-subscription.ts @@ -0,0 +1,13 @@ +import { ChatSubscription } from '../types/chat-subscription' +import { RocketChatSubscription } from '../types/rocket-chat-subscription' + +export const normalizeChatSubscription = ( + raw: RocketChatSubscription +): ChatSubscription => { + return { + project: raw.name, + unread: raw.unread, + mentions: raw.userMentions, + lastUpdatedAt: raw._updatedAt, + } +} diff --git a/src/modules/chat/signals/chat-subscriptions-signal.ts b/src/modules/chat/signals/chat-subscriptions-signal.ts new file mode 100644 index 0000000..29fba02 --- /dev/null +++ b/src/modules/chat/signals/chat-subscriptions-signal.ts @@ -0,0 +1,31 @@ +import { signal as createSignal } from '@lit-labs/preact-signals' + +import { ChatSubscription } from '../types/chat-subscription' + +export type ChatSubscriptionMap = Record + +type ChatSubscriptionsSignal = { + subscriptions: ChatSubscriptionMap + setValue: (value: ChatSubscriptionMap) => void + refresh: (iframe: HTMLIFrameElement) => void +} + +const subscriptionsSignal = createSignal({}) + +export const chatSubscriptionsSignal: ChatSubscriptionsSignal = { + get subscriptions() { + return subscriptionsSignal.value + }, + setValue: (value) => { + subscriptionsSignal.value = value + }, + refresh: (iframe) => { + iframe.contentWindow?.postMessage( + { + type: 'meteor-call', + method: 'subscriptions/get', + }, + '*' + ) + }, +} diff --git a/src/modules/chat/types/chat-subscription.ts b/src/modules/chat/types/chat-subscription.ts new file mode 100644 index 0000000..eb4c598 --- /dev/null +++ b/src/modules/chat/types/chat-subscription.ts @@ -0,0 +1,6 @@ +export type ChatSubscription = { + project: string + unread: number + mentions: number + lastUpdatedAt: Date +} diff --git a/src/modules/chat/types/rocket-chat-subscription.ts b/src/modules/chat/types/rocket-chat-subscription.ts new file mode 100644 index 0000000..3231942 --- /dev/null +++ b/src/modules/chat/types/rocket-chat-subscription.ts @@ -0,0 +1,18 @@ +export type RocketChatSubscription = { + _id: string + rid: string + u: { + _id: string + username: string + } + _updatedAt: Date + alert: boolean + fname: string + groupMentions: number + name: string + open: boolean + t: string + unread: number + userMentions: number + ls: Date +} diff --git a/src/modules/chat/utils/on-rocket-chat-event.ts b/src/modules/chat/utils/on-rocket-chat-event.ts new file mode 100644 index 0000000..141e756 --- /dev/null +++ b/src/modules/chat/utils/on-rocket-chat-event.ts @@ -0,0 +1,29 @@ +import { groupBy } from '@/utils/array/group-by' + +import { normalizeChatSubscription } from '../normalizers/normalize-chat-subscription' +import { chatSubscriptionsSignal } from '../signals/chat-subscriptions-signal' +import { RocketChatSubscription } from '../types/rocket-chat-subscription' + +type RocketChatEventBase = { + type: 'meteor-call-response' + error?: string +} + +type RocketChatSubscriptionEvent = RocketChatEventBase & { + method: 'subscriptions/get' + data: RocketChatSubscription[] +} + +export type RocketChatEvent = RocketChatSubscriptionEvent + +export const onRocketChatMessage = (e: MessageEvent) => { + if (e.data.type !== 'meteor-call-response') { + return + } + + if (e.data.method === 'subscriptions/get') { + const normalized = e.data.data.map(normalizeChatSubscription) + const grouped = groupBy((x) => x.project, normalized) + chatSubscriptionsSignal.setValue(grouped) + } +} diff --git a/src/modules/projects/constants/labels.ts b/src/modules/projects/constants/labels.ts new file mode 100644 index 0000000..1f22274 --- /dev/null +++ b/src/modules/projects/constants/labels.ts @@ -0,0 +1,2 @@ +export const FINDING_LABEL = 'finding' +export const NON_FINDING_LABEL = 'non-finding' diff --git a/src/modules/projects/constants/namespaces.ts b/src/modules/projects/constants/namespaces.ts new file mode 100644 index 0000000..471843b --- /dev/null +++ b/src/modules/projects/constants/namespaces.ts @@ -0,0 +1,7 @@ +export const PM_GROUP_PATH = 'pm' +export const TEMPLATE_GROUP_PATH = 'pentext' + +export const IGNORED_NAMESPACES_MAP: Record = { + [PM_GROUP_PATH]: true, + [TEMPLATE_GROUP_PATH]: true, +} diff --git a/src/modules/projects/constants/projects.ts b/src/modules/projects/constants/projects.ts new file mode 100644 index 0000000..1e62bbb --- /dev/null +++ b/src/modules/projects/constants/projects.ts @@ -0,0 +1 @@ +export const IMPORT_URL_USERNAME = 'gitlab-ci-token' diff --git a/src/modules/projects/constants/variables.ts b/src/modules/projects/constants/variables.ts new file mode 100644 index 0000000..d224671 --- /dev/null +++ b/src/modules/projects/constants/variables.ts @@ -0,0 +1 @@ +export const PDF_PASSWORD_KEY = 'PDF_PASSWORD' diff --git a/src/modules/projects/normalizers/normalize-build.ts b/src/modules/projects/normalizers/normalize-build.ts new file mode 100644 index 0000000..10322a6 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-build.ts @@ -0,0 +1,20 @@ +import { GitLabJob } from '@/api/gitlab/types/gitlab-job' + +import { ProjectBuild } from '../types/project-build' + +export const normalizeBuild = (raw: GitLabJob): ProjectBuild => { + return { + id: raw.id, + url: raw.web_url, + status: raw.status, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any + files: raw.artifacts as any, + createdAt: new Date(raw.created_at), + finishedAt: new Date(raw.finished_at), + user: { + avatar: raw.user.avatar_url, + url: raw.user.web_url, + username: raw.user.name, + }, + } +} diff --git a/src/modules/projects/normalizers/normalize-event.ts b/src/modules/projects/normalizers/normalize-event.ts new file mode 100644 index 0000000..ac1be28 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-event.ts @@ -0,0 +1,57 @@ +import { format } from 'date-fns' + +import { GitLabEvent } from '@/api/gitlab/types/gitlab-event' + +import { ProjectDetailsEvent } from '../types/project-details' + +export const normalizeEvent = (raw: GitLabEvent): ProjectDetailsEvent => { + const base = { + date: new Date(raw.created_at), + time: format(new Date(raw.created_at), 'H:mm'), + user: raw.author.name, + userUrl: raw.author.web_url, + avatar: raw.author.avatar_url, + action: raw.action_name, + path: '/', + } + + if (raw.action_name === 'pushed to') { + return { + ...base, + action: 'pushed to', + commitFrom: raw.push_data.commit_from, + commitTo: raw.push_data.commit_to, + commitCount: raw.push_data.commit_count, + commitTitle: raw.push_data.commit_title, + path: `/compare/${raw.push_data.commit_from}...${raw.push_data.commit_to}`, + } + } else if ( + raw.action_name === 'opened' || + raw.action_name === 'closed' || + raw.action_name === 'updated' + ) { + return { + ...base, + action: raw.action_name, + targetTitle: raw.target_title, + targetIid: raw.target_iid, + path: `/issues/${raw.target_iid}`, + } + } else if (raw.action_name === 'commented on') { + return { + ...base, + action: raw.action_name, + noteId: raw.note.noteable_iid, + targetTitle: raw.target_title, + targetIid: raw.target_iid, + path: `/issues/${raw.note.noteable_iid}#node_${raw.target_id}`, + } + } else if (raw.action_name === 'created') { + return { + ...base, + action: 'created', + } + } + + return base +} diff --git a/src/modules/projects/normalizers/normalize-finding-label.ts b/src/modules/projects/normalizers/normalize-finding-label.ts new file mode 100644 index 0000000..4e2302a --- /dev/null +++ b/src/modules/projects/normalizers/normalize-finding-label.ts @@ -0,0 +1,38 @@ +import { ProjectDetailsFindingLabel } from '@/modules/projects/types/project-details' + +import { NON_FINDING_LABEL } from '../constants/labels' + +const PREFIX = 'threatLevel:' + +export const normalizeFindingLabel = ( + labels: string[] +): ProjectDetailsFindingLabel | null => { + if (labels.filter((x) => x === NON_FINDING_LABEL).length) { + return null + } + + const [threatLevelValue] = labels.filter((x) => x.startsWith(PREFIX)) + + if (!threatLevelValue) { + return ProjectDetailsFindingLabel.ToDo + } + + const [, value] = threatLevelValue.split(PREFIX) + + switch (value) { + case ProjectDetailsFindingLabel.Extreme.toString(): + return ProjectDetailsFindingLabel.Extreme + case ProjectDetailsFindingLabel.High.toString(): + return ProjectDetailsFindingLabel.High + case ProjectDetailsFindingLabel.Elevated.toString(): + return ProjectDetailsFindingLabel.Elevated + case ProjectDetailsFindingLabel.Moderate.toString(): + return ProjectDetailsFindingLabel.Moderate + case ProjectDetailsFindingLabel.Low.toString(): + return ProjectDetailsFindingLabel.Low + case ProjectDetailsFindingLabel.Unknown.toString(): + return ProjectDetailsFindingLabel.Unknown + default: + return ProjectDetailsFindingLabel.ToDo + } +} diff --git a/src/modules/projects/normalizers/normalize-finding.ts b/src/modules/projects/normalizers/normalize-finding.ts new file mode 100644 index 0000000..cfff058 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-finding.ts @@ -0,0 +1,17 @@ +import { type GitLabIssue } from '@/api/gitlab/types/gitlab-issue' + +import { type ProjectDetailsFinding } from '@/modules/projects/types/project-details' + +import { normalizeFindingLabel } from '@/modules/projects/normalizers/normalize-finding-label' + +export const normalizeFinding = (raw: GitLabIssue): ProjectDetailsFinding => { + return { + id: raw.id, + iid: raw.iid, + title: raw.title, + label: normalizeFindingLabel(raw.labels), + updatedAt: new Date(raw.updated_at), + url: raw.web_url, + description: raw.description, + } +} diff --git a/src/modules/projects/normalizers/normalize-grouped-findings.ts b/src/modules/projects/normalizers/normalize-grouped-findings.ts new file mode 100644 index 0000000..4ea5f28 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-grouped-findings.ts @@ -0,0 +1,36 @@ +import { + type ProjectDetailsFindingFinding, + ProjectDetailsFindingLabel, + type ProjectDetailsGroupedFindings, +} from '@/modules/projects/types/project-details' + +import { groupByMultiple } from '@/utils/array/group-by' + +const FINDINGS_GROUP_ORDER = [ + ProjectDetailsFindingLabel.ToDo, + ProjectDetailsFindingLabel.Extreme, + ProjectDetailsFindingLabel.High, + ProjectDetailsFindingLabel.Elevated, + ProjectDetailsFindingLabel.Moderate, + ProjectDetailsFindingLabel.Low, + ProjectDetailsFindingLabel.Unknown, +] + +export const normalizeGroupedFindings = ( + findingsFindings: ProjectDetailsFindingFinding[] +): ProjectDetailsGroupedFindings[] => { + const findings = ( + Object.entries(groupByMultiple((x) => x.label, findingsFindings)).map( + ([key, values]) => ({ + group: key, + findings: values, + }) + ) as ProjectDetailsGroupedFindings[] + ).sort( + (a, b) => + FINDINGS_GROUP_ORDER.indexOf(a.group) - + FINDINGS_GROUP_ORDER.indexOf(b.group) + ) + + return findings +} diff --git a/src/modules/projects/normalizers/normalize-history.ts b/src/modules/projects/normalizers/normalize-history.ts new file mode 100644 index 0000000..bf82284 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-history.ts @@ -0,0 +1,25 @@ +import { format } from 'date-fns' + +import { GitLabEvent } from '@/api/gitlab/types/gitlab-event' + +import { groupByMultiple } from '@/utils/array/group-by' + +import { ProjectDetailsHistory } from '../types/project-details' + +import { normalizeEvent } from './normalize-event' + +export const normalizeHistory = ( + events: GitLabEvent[] +): ProjectDetailsHistory[] => { + return Object.entries( + groupByMultiple((x) => format(new Date(x.created_at), 'yyyy-MM-dd'), events) + ) + .map(([date, events]) => ({ + date, + dateDisplay: format(new Date(date), 'eeee, dd-MM-yyyy'), + events: events + .map(normalizeEvent) + .sort((a, b) => b.date.getTime() - a.date.getTime()), + })) + .sort((a, b) => b.date.localeCompare(a.date)) +} diff --git a/src/modules/projects/normalizers/normalize-member.ts b/src/modules/projects/normalizers/normalize-member.ts new file mode 100644 index 0000000..241c465 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-member.ts @@ -0,0 +1,9 @@ +import { GitLabMember } from '@/api/gitlab/types/gitlab-member' + +import { ProjectDetailsMember } from '@/modules/projects/types/project-details' + +export const normalizeMember = (raw: GitLabMember): ProjectDetailsMember => ({ + name: raw.name, + avatar: raw.avatar_url, + url: raw.web_url, +}) diff --git a/src/modules/projects/normalizers/normalize-namespace.ts b/src/modules/projects/normalizers/normalize-namespace.ts new file mode 100644 index 0000000..86c1c2d --- /dev/null +++ b/src/modules/projects/normalizers/normalize-namespace.ts @@ -0,0 +1,10 @@ +import { GitLabGroup } from '@/api/gitlab/types/gitlab-group' + +import { Namespace } from '../types/namespace' + +export const normalizeNamespace = (raw: GitLabGroup): Namespace => { + return { + id: raw.id, + path: raw.full_name, + } +} diff --git a/src/modules/projects/normalizers/normalize-pdf.ts b/src/modules/projects/normalizers/normalize-pdf.ts new file mode 100644 index 0000000..3b5cee6 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-pdf.ts @@ -0,0 +1,21 @@ +import { config } from '@/config' + +import { GitLabProject } from '@/api/gitlab/types/gitlab-project' + +const PDF_TYPE_MAP = { + quote: 'offerte', + report: 'report', +} + +export const normalizePdf = (raw: GitLabProject, type: 'quote' | 'report') => { + let title = raw.path + if (raw.name.startsWith('pen-') || raw.name.startsWith('off-')) { + title = raw.name.slice(4) + } + + const prefix = PDF_TYPE_MAP[type] + + const url = `${config.services.gitlabUrl}/api/v4/projects/${raw.id}/jobs/artifacts/main/raw/target/${prefix}_${title}.pdf?job=build` + + return url +} diff --git a/src/modules/projects/normalizers/normalize-project-details.ts b/src/modules/projects/normalizers/normalize-project-details.ts new file mode 100644 index 0000000..9eaec20 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-project-details.ts @@ -0,0 +1,75 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import { BUILD_STAGE } from '@/api/gitlab/constants' +import { type GitLabEvent } from '@/api/gitlab/types/gitlab-event' +import { type GitLabIssue } from '@/api/gitlab/types/gitlab-issue' +import { GitLabJob } from '@/api/gitlab/types/gitlab-job' +import { type GitLabLabel } from '@/api/gitlab/types/gitlab-label' +import { type GitLabMember } from '@/api/gitlab/types/gitlab-member' +import { type GitLabVariable } from '@/api/gitlab/types/gitlab-variable' + +import { type ProjectDetails } from '@/modules/projects/types/project-details' + +import { normalizeFinding } from '@/modules/projects/normalizers/normalize-finding' +import { normalizeGroupedFindings } from '@/modules/projects/normalizers/normalize-grouped-findings' +import { normalizeHistory } from '@/modules/projects/normalizers/normalize-history' +import { normalizeMember } from '@/modules/projects/normalizers/normalize-member' +import { isNonFinding } from '@/modules/projects/utils/is-non-finding' + +import { FINDING_LABEL, NON_FINDING_LABEL } from '../constants/labels' +import { PDF_PASSWORD_KEY } from '../constants/variables' +import { isFinding } from '../utils/is-finding' + +import { normalizeBuild } from './normalize-build' + +const isEitherFinding = ({ labels }: { labels: string[] }) => + labels.some((label) => label === FINDING_LABEL || label === NON_FINDING_LABEL) + +const isFindingRaw = ({ labels }: { labels: string[] }) => + labels.some((label) => label === FINDING_LABEL) + +const isNonFindingRaw = ({ labels }: { labels: string[] }) => + labels.some((label) => label === NON_FINDING_LABEL) + +const isHuman = (member: GitLabMember) => member.name !== 'CI' +const isStaff = (member: GitLabMember) => member.access_level >= 40 +const isCustomer = (member: GitLabMember) => member.access_level < 40 + +export const normalizeProjectDetails = ( + id: number, + events: GitLabEvent[], + issues: GitLabIssue[], + _labels: GitLabLabel[], + members: GitLabMember[], + variables: GitLabVariable[], + jobs: GitLabJob[] +): ProjectDetails => { + const allFindingsRaw = issues.filter(isEitherFinding) + + const findingsFindings = allFindingsRaw + .filter(isFindingRaw) + .map((x) => normalizeFinding(x)) + + const findingsNonFindings = allFindingsRaw + .filter(isNonFindingRaw) + .map((x) => normalizeFinding(x)) + + const allFindings = findingsFindings.concat(findingsNonFindings) + + const crew = members.filter(isHuman) + + const pdfPassword = + variables.find((x) => x.key === PDF_PASSWORD_KEY)?.value ?? null + + return { + id, + pdfPassword, + findings: normalizeGroupedFindings(findingsFindings.filter(isFinding)), + history: normalizeHistory(events), + staff: crew.filter(isStaff).map(normalizeMember), + customers: crew.filter(isCustomer).map(normalizeMember), + allFindings, + nonFindings: findingsNonFindings.filter(isNonFinding), + builds: jobs.filter((x) => x.stage === BUILD_STAGE).map(normalizeBuild), + } +} diff --git a/src/modules/projects/normalizers/normalize-project-finding.ts b/src/modules/projects/normalizers/normalize-project-finding.ts new file mode 100644 index 0000000..3e3bfbb --- /dev/null +++ b/src/modules/projects/normalizers/normalize-project-finding.ts @@ -0,0 +1,49 @@ +import { GitLabDiscussion } from '@/api/gitlab/types/gitlab-discussion' + +import { ProjectFindingDetails } from '../types/project-findings' + +const TO_DO = 'ToDo' + +const getTopic = (topic: string, raw: GitLabDiscussion[]) => { + const issueDiscussionComments = raw.filter((comment) => + comment.notes[0].body.toLowerCase().startsWith(topic.toLowerCase()) + ) + + if (!issueDiscussionComments?.length) { + return TO_DO + } + + const [, ...lines]: string[] = issueDiscussionComments[0].notes[0].body + .split('\n') + .filter((line: string) => !line.match(/^\s*$/)) // remove empty lines + + const joined = lines.join('\n') + return joined +} + +export const normalizeProjectFinding = ( + raw: GitLabDiscussion[], + projectId: number, + issueId: number +): ProjectFindingDetails => { + const recommendation = getTopic('recommendation', raw) + const impact = getTopic('impact', raw) + const type = getTopic('type', raw) + const technicalDescriptionTopic = getTopic('technical description', raw) + + const firstRaw = raw[0]?.notes[0]?.body + const first = firstRaw?.length ? firstRaw : TO_DO + + // in case technical description is undefined try to use the first comment + const technicalDescription = + technicalDescriptionTopic === TO_DO ? first : technicalDescriptionTopic + + return { + projectId, + issueId, + recommendation, + impact, + technicalDescription, + type, + } +} diff --git a/src/modules/projects/normalizers/normalize-project-status.ts b/src/modules/projects/normalizers/normalize-project-status.ts new file mode 100644 index 0000000..cec6f9a --- /dev/null +++ b/src/modules/projects/normalizers/normalize-project-status.ts @@ -0,0 +1,46 @@ +import { capitalCaseToSpaceCase } from '@/utils/string/capital-case-to-space-case' + +import { ProjectStatus } from '../types/project' + +const QUOTE_PROJECT_STATUSES: ProjectStatus[] = [ + 'Staffing', + 'Scoping', + 'ProposalShared', + 'ProposalAccepted', + 'ProposalRejected', + 'ProposalNoResponse', +] + +export const QUOTE_STATUSES = QUOTE_PROJECT_STATUSES.map((status) => ({ + value: status, + label: capitalCaseToSpaceCase(status), +})) + +const PENTEST_PROJECT_STATUSES: ProjectStatus[] = [ + 'Running', + 'Delivered', + 'PmClosed', + 'ReTesting', +] + +export const PENTEST_STATUSES = PENTEST_PROJECT_STATUSES.map((status) => ({ + value: status, + label: capitalCaseToSpaceCase(status), +})) + +const VALUES: ProjectStatus[] = [ + ...QUOTE_PROJECT_STATUSES, + ...PENTEST_PROJECT_STATUSES, + 'Unknown', +] + +export const normalizeProjectStatus = (topics: string[]): ProjectStatus => { + const find = topics.find((x) => x.startsWith('projectStatus:')) + + if (!find) { + return 'Unknown' + } + + const split = find.split('projectStatus:').pop() as ProjectStatus + return VALUES.includes(split) ? split : 'Unknown' +} diff --git a/src/modules/projects/normalizers/normalize-project.ts b/src/modules/projects/normalizers/normalize-project.ts new file mode 100644 index 0000000..c37a476 --- /dev/null +++ b/src/modules/projects/normalizers/normalize-project.ts @@ -0,0 +1,103 @@ +import { config } from '@/config' + +import { GitLabProject } from '@/api/gitlab/types/gitlab-project' +import { GitLabProjectFile } from '@/api/gitlab/types/gitlab-project-file' + +import { Project } from '@/modules/projects/types/project' + +import { isPentest } from '@/modules/projects/utils/is-pentest' +import { isQuote } from '@/modules/projects/utils/is-quote' + +import { groupBy } from '@/utils/array/group-by' +import { uniqueBy } from '@/utils/array/unique-by' +import { getChannelUrls } from '@/utils/rocket-chat/get-channel-urls' + +import { normalizePdf } from './normalize-pdf' +import { normalizeProjectStatus } from './normalize-project-status' + +export const normalizeProject = ( + raw: GitLabProject, + quote: GitLabProjectFile | null +): Project => { + const tags = raw.topics.map((x) => x.toLowerCase()) + const channels = getChannelUrls(config.services.rocketChatUrl, raw.name) + + let startDate: Date | null = null + let endDate: Date | null = null + + if (quote) { + const text = atob(quote.content) + const xml = new window.DOMParser().parseFromString(text, 'text/xml') + const planning = xml.getElementsByTagName('planning')[0] + const start = planning.getElementsByTagName('start')[0].textContent + const end = planning.getElementsByTagName('end')[0].textContent + + if (start) { + startDate = new Date(start) + } + + if (end) { + endDate = new Date(end) + } + } + + return { + id: raw.id, + name: raw.name, + status: normalizeProjectStatus(raw.topics), + type: isPentest(tags, raw.name) ? 'pentest' : 'quote', + nameWithNamespace: raw.name_with_namespace, + path: raw.path, + pathWithNamespace: raw.path_with_namespace, + createdAt: new Date(raw.created_at), + defaultBranch: raw.default_branch, + ssh: raw.ssh_url_to_repo, + url: raw.web_url, + readme: raw.readme_url, + avatar: raw.avatar_url, + lastActivityAt: new Date(raw.last_activity_at), + isArchived: raw.archived, + updatedAt: new Date(raw.updated_at), + namespace: { + id: raw.namespace.id, + name: raw.namespace.name, + path: raw.namespace.path, + url: raw.namespace.web_url, + avatar: raw.namespace.avatar_url, + }, + tags, + isQuote: isQuote(tags, raw.name), + isPentest: isPentest(tags, raw.name), + quotePdf: normalizePdf(raw, 'quote'), + reportPdf: normalizePdf(raw, 'report'), + topics: raw.topics, + startDate, + endDate, + quoteChannel: channels.quote, + pentestChannel: channels.pentest, + } +} + +export type ProjectsData = { + quotes: Project[] + pentests: Project[] + all: Project[] + allById: Record + allByName: Record +} + +export const groupProjects = (result: Project[]): ProjectsData => { + const quotes = result.filter((x) => x.isQuote) + const pentests = result.filter((x) => x.isPentest) + const all = uniqueBy((x) => x.id, quotes.concat(pentests)) + const allById = groupBy((x) => x.id, all) + const allByName = groupBy((x) => x.pathWithNamespace, all) + + return { + quotes, + pentests, + all, + allById, + allByName, + } +} diff --git a/src/modules/projects/normalizers/normalize-template.ts b/src/modules/projects/normalizers/normalize-template.ts new file mode 100644 index 0000000..1bdd5ea --- /dev/null +++ b/src/modules/projects/normalizers/normalize-template.ts @@ -0,0 +1,11 @@ +import { GitLabProject } from '@/api/gitlab/types/gitlab-project' + +import { Template } from '../types/template' + +export const normalizeTemplate = (project: GitLabProject): Template => { + return { + name: project.name, + tagList: project.tag_list, + url: project.http_url_to_repo, + } +} diff --git a/src/modules/projects/queries/create-project-query.ts b/src/modules/projects/queries/create-project-query.ts new file mode 100644 index 0000000..8242fd4 --- /dev/null +++ b/src/modules/projects/queries/create-project-query.ts @@ -0,0 +1,9 @@ +import { createQuery } from '@/utils/signal/query/create-query' + +import { createProject } from '../services/create-project' +import { Project } from '../types/project' + +export const createProjectQuery = createQuery< + Project, + Parameters +>((params) => createProject(...params)) diff --git a/src/modules/projects/queries/create-variable-query.ts b/src/modules/projects/queries/create-variable-query.ts new file mode 100644 index 0000000..80ebd72 --- /dev/null +++ b/src/modules/projects/queries/create-variable-query.ts @@ -0,0 +1,14 @@ +import { createQuery } from '@/utils/signal/query/create-query' + +import { createVariable } from '../services/create-variable' + +import { projectDetailsQuery } from './project-details.query' + +export const createVariableQuery = createQuery< + void, + Parameters +>((params) => createVariable(...params), { + after: async ([projectId]) => { + await projectDetailsQuery.fetch([projectId, 'network']) + }, +}) diff --git a/src/modules/projects/queries/namespaces-query.ts b/src/modules/projects/queries/namespaces-query.ts new file mode 100644 index 0000000..9286972 --- /dev/null +++ b/src/modules/projects/queries/namespaces-query.ts @@ -0,0 +1,6 @@ +import { createQuery } from '@/utils/signal/query/create-query' + +import { namespaces } from '../services/namespaces' +import { Namespace } from '../types/namespace' + +export const namespacesQuery = createQuery(namespaces) diff --git a/src/modules/projects/queries/project-details.query.ts b/src/modules/projects/queries/project-details.query.ts new file mode 100644 index 0000000..8257a83 --- /dev/null +++ b/src/modules/projects/queries/project-details.query.ts @@ -0,0 +1,45 @@ +import { isDefined } from '@/utils/object/is-defined' +import { createQuery } from '@/utils/signal/query/create-query' + +import { projectDetails } from '../services/project-details' +import { ProjectDetails } from '../types/project-details' + +type ProjectDetailsQueryResult = Record< + number, + { isLoading: boolean; data: ProjectDetails | null } +> + +export const projectDetailsQuery = createQuery< + ProjectDetailsQueryResult, + Parameters, + ProjectDetails +>(([id, mode]) => projectDetails(id, mode), { + before: (cache, [param], setValue) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any + const [projectId, mode] = param as any + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const fromCache = cache?.[projectId] + if (mode === 'cache' && isDefined(fromCache)) { + return fromCache + } + + const update: ProjectDetailsQueryResult = { + [projectId]: { + isLoading: true, + data: fromCache?.data ?? null, + }, + } + + setValue(update) + return update + }, + transform: (details, current) => { + return { + ...current, + [details.id]: { + isLoading: false, + data: details, + }, + } + }, +}) diff --git a/src/modules/projects/queries/project-finding-query.ts b/src/modules/projects/queries/project-finding-query.ts new file mode 100644 index 0000000..68568fe --- /dev/null +++ b/src/modules/projects/queries/project-finding-query.ts @@ -0,0 +1,50 @@ +import { createQuery } from '@/utils/signal/query/create-query' + +import { projectFinding } from '../services/project-finding' +import { ProjectFindingDetails } from '../types/project-findings' +import { projectFindingKey } from '../utils/project-finding-key' + +type ProjectFindingsQueryData = Record< + string, + { isLoading: boolean; data: ProjectFindingDetails | null } +> + +export const projectFindingsQuery = createQuery< + ProjectFindingsQueryData, + Parameters, + ProjectFindingDetails +>( + ([projectId, issueId]) => { + return projectFinding(projectId, issueId) + }, + { + before: (cache, [projectId, issueId], setValue) => { + const key = projectFindingKey(projectId, issueId) + const fromCache = cache?.[key] + if (cache && fromCache?.isLoading) { + return cache + } + + const update: ProjectFindingsQueryData = { + [key]: { + isLoading: true, + data: null, + }, + } + + setValue(update) + return update + }, + transform: (details, current) => { + const key = projectFindingKey(details.projectId, details.issueId) + const value = current?.[key] + return { + ...current, + [key]: { + isLoading: value?.isLoading ?? false, + data: details, + }, + } + }, + } +) diff --git a/src/modules/projects/queries/projects-query.ts b/src/modules/projects/queries/projects-query.ts new file mode 100644 index 0000000..75ca9ab --- /dev/null +++ b/src/modules/projects/queries/projects-query.ts @@ -0,0 +1,6 @@ +import { createQuery } from '@/utils/signal/query/create-query' + +import { ProjectsData } from '../normalizers/normalize-project' +import { projects } from '../services/projects' + +export const projectsQuery = createQuery(projects) diff --git a/src/modules/projects/queries/templates-query.ts b/src/modules/projects/queries/templates-query.ts new file mode 100644 index 0000000..3892460 --- /dev/null +++ b/src/modules/projects/queries/templates-query.ts @@ -0,0 +1,6 @@ +import { createQuery } from '@/utils/signal/query/create-query' + +import { templates } from '../services/templates' +import { Template } from '../types/template' + +export const templatesQuery = createQuery(templates) diff --git a/src/modules/projects/queries/update-dates-query.ts b/src/modules/projects/queries/update-dates-query.ts new file mode 100644 index 0000000..0c41bdb --- /dev/null +++ b/src/modules/projects/queries/update-dates-query.ts @@ -0,0 +1,65 @@ +import { format } from 'date-fns' + +import { projectFile } from '@/api/gitlab/client/project-file' + +import { createQuery } from '@/utils/signal/query/create-query' + +import { groupProjects } from '../normalizers/normalize-project' +import { updateProjectFile } from '../services/update-project-file' +import { Project } from '../types/project' + +import { projectsQuery } from './projects-query' + +type UpdateDates = { + projectId: number + startDate?: Date | null + endDate?: Date | null +} + +export const updateDatesQuery = createQuery( + async ({ projectId, startDate, endDate }) => { + const currentFile = await projectFile({ + id: projectId, + path: 'source/offerte.xml', + branch: 'main', + }) + + let text = atob(currentFile.content) + + if (startDate) { + text = text.replace( + /()(.*?)(<\/start>)/, + `$1${format(startDate, 'yyyy-MM-dd')}$3` + ) + } + + if (endDate) { + text = text.replace( + /()(.*?)(<\/end>)/, + `$1${format(endDate, 'yyyy-MM-dd')}$3` + ) + } + + return updateProjectFile({ + id: projectId, + branch: 'main', + path: 'source/offerte.xml', + commitMessage: 'feat(offerte): update dates', + content: text, + }) + }, + { + after: (val, project) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any + const [thisProject] = val as any + const { projectId } = thisProject as UpdateDates + + // optimistic update + const all = projectsQuery.data?.all ?? [] + const filtered = all.filter((x) => x.id !== projectId) + const newList = [...filtered, project] + const grouped = groupProjects(newList) + projectsQuery.set(grouped) + }, + } +) diff --git a/src/modules/projects/queries/update-project-query.ts b/src/modules/projects/queries/update-project-query.ts new file mode 100644 index 0000000..f2c1795 --- /dev/null +++ b/src/modules/projects/queries/update-project-query.ts @@ -0,0 +1,33 @@ +import { createQuery } from '@/utils/signal/query/create-query' + +import { groupProjects } from '../normalizers/normalize-project' +import { updateProject } from '../services/update-project' +import { Project } from '../types/project' + +import { projectDetailsQuery } from './project-details.query' +import { projectsQuery } from './projects-query' + +export const updateProjectQuery = createQuery< + Project, + Parameters +>( + (params) => { + return updateProject(...params) + }, + { + after: async ([val], project) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any + const [projectId] = val as any + const detailsPromise = projectDetailsQuery.fetch([projectId, 'network']) + + // optimistic update + const all = projectsQuery.data?.all ?? [] + const filtered = all.filter((x) => x.id !== projectId) + const newList = [...filtered, project] + const grouped = groupProjects(newList) + projectsQuery.set(grouped) + + await detailsPromise + }, + } +) diff --git a/src/modules/projects/services/create-project.ts b/src/modules/projects/services/create-project.ts new file mode 100644 index 0000000..1b3f60f --- /dev/null +++ b/src/modules/projects/services/create-project.ts @@ -0,0 +1,65 @@ +import { gitlabClient } from '@/api/gitlab/gitlab-client' + +import { appSignal } from '@/modules/app/signals/app-signal' + +import { generatePassword } from '@/utils/string/generate-password' + +import { IMPORT_URL_USERNAME } from '../constants/projects' +import { normalizeProject } from '../normalizers/normalize-project' +import { Project } from '../types/project' + +export const createProject = async ( + templateUrl: string, + path: string, + topic: string, + namespaceId: number +): Promise => { + const importUrl = new URL(templateUrl) + importUrl.username = IMPORT_URL_USERNAME + importUrl.password = appSignal.gitLabToken + + const project = await gitlabClient.createProject({ + import_url: importUrl.toString(), + path, + topics: [topic], + namespace_id: namespaceId, + }) + + const nextYear = new Date() + nextYear.setDate(nextYear.getDate() + 365) + + const accessToken = await gitlabClient.createAccessToken(project.id, { + name: 'CI', + scopes: ['api'], + expires_at: nextYear, + }) + + await Promise.all([ + gitlabClient.createVariable(project.id, { + key: 'PROJECT_ACCESS_TOKEN', + value: accessToken.token, + protected: false, + masked: true, + }), + gitlabClient.createVariable(project.id, { + key: 'PDF_PASSWORD', + value: generatePassword(), + protected: false, + masked: true, + }), + ]) + + let quote = null + try { + quote = await gitlabClient.projectFile({ + id: project.id, + path: 'source/offerte.xml', + branch: 'main', + }) + } catch (err) { + /* empty */ + } + + const normalized = normalizeProject(project, quote) + return normalized +} diff --git a/src/modules/projects/services/create-variable.ts b/src/modules/projects/services/create-variable.ts new file mode 100644 index 0000000..41b500b --- /dev/null +++ b/src/modules/projects/services/create-variable.ts @@ -0,0 +1,14 @@ +import { CreateGitLabVariable } from '@/api/gitlab/client/create-variable' +import { gitlabClient } from '@/api/gitlab/gitlab-client' + +import { createLogger } from '@/utils/logging/create-logger' + +const logger = createLogger('create-variable') + +export const createVariable = async ( + id: number, + payload: CreateGitLabVariable +): Promise => { + logger.debug('creating project...') + await gitlabClient.createVariable(id, payload) +} diff --git a/src/modules/projects/services/namespaces.ts b/src/modules/projects/services/namespaces.ts new file mode 100644 index 0000000..e6a6766 --- /dev/null +++ b/src/modules/projects/services/namespaces.ts @@ -0,0 +1,16 @@ +import { gitlabClient } from '@/api/gitlab/gitlab-client' + +import { IGNORED_NAMESPACES_MAP } from '../constants/namespaces' +import { normalizeNamespace } from '../normalizers/normalize-namespace' + +export const namespaces = async () => { + const groups = await gitlabClient.groups({ + allAvailable: true, + minAccessLevel: 10, + }) + const filtered = groups.filter( + (group) => !IGNORED_NAMESPACES_MAP[group.full_path] + ) + const normalized = filtered.map(normalizeNamespace) + return normalized +} diff --git a/src/modules/projects/services/project-details.ts b/src/modules/projects/services/project-details.ts new file mode 100644 index 0000000..ebc1026 --- /dev/null +++ b/src/modules/projects/services/project-details.ts @@ -0,0 +1,48 @@ +import { gitlabClient } from '@/api/gitlab/gitlab-client' +import { fetchPaginated } from '@/api/gitlab/utils/fetch-paginated' + +import { normalizeProjectDetails } from '@/modules/projects/normalizers/normalize-project-details' + +import { createLogger } from '@/utils/logging/create-logger' + +import { ProjectDetails } from '../types/project-details' + +const logger = createLogger('sync-project-details') + +export const projectDetails = async ( + id: number, + // #TODO: move this to create-query + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _mode: 'cache' | 'network' = 'cache' +): Promise => { + logger.debug(`syncing project with name: ${id}...`) + + const [events, issues, labels, members, variables, jobs] = await Promise.all([ + gitlabClient.events({ id }), + gitlabClient.issues({ id }), + gitlabClient.labels({ id }), + gitlabClient.members({ id }), + gitlabClient.variables({ id }), + fetchPaginated(({ page, perPage }) => + gitlabClient.jobs({ + page, + perPage, + projectId: id, + scopes: ['success', 'pending', 'running'], + }) + ), + ]) + + const normalized = normalizeProjectDetails( + id, + events, + issues, + labels, + members, + variables, + jobs + ) + + logger.debug(`normalized project details with id: ${id}`, normalized) + return normalized +} diff --git a/src/modules/projects/services/project-finding.ts b/src/modules/projects/services/project-finding.ts new file mode 100644 index 0000000..1137aa7 --- /dev/null +++ b/src/modules/projects/services/project-finding.ts @@ -0,0 +1,43 @@ +import { gitlabClient } from '@/api/gitlab/gitlab-client' +import { fetchPaginated } from '@/api/gitlab/utils/fetch-paginated' + +import { createLogger } from '@/utils/logging/create-logger' +import { isDefined } from '@/utils/object/is-defined' + +import { normalizeProjectFinding } from '../normalizers/normalize-project-finding' +import { projectFindingsQuery } from '../queries/project-finding-query' +import { ProjectFindingDetails } from '../types/project-findings' +import { projectFindingKey } from '../utils/project-finding-key' + +const logger = createLogger('sync-project-discussion') + +export const projectFinding = async ( + projectId: number, + issueId: number +): Promise => { + const key = projectFindingKey(projectId, issueId) + + const fromCache = projectFindingsQuery.data?.[key] + + if (isDefined(fromCache?.data)) { + logger.debug( + `project finding already synced. project: ${projectId}, issue: ${issueId}` + ) + return fromCache.data + } + + logger.debug(`syncing project: ${projectId} and issue: ${issueId}`) + + const discussions = await fetchPaginated(({ perPage, page }) => + gitlabClient.discussions({ perPage, page, projectId, issueId }) + ) + + const normalized = normalizeProjectFinding(discussions, projectId, issueId) + + logger.debug( + `normalized finding for project: ${projectId} and issue: ${issueId}`, + normalized + ) + + return normalized +} diff --git a/src/modules/projects/services/projects.ts b/src/modules/projects/services/projects.ts new file mode 100644 index 0000000..b8893ec --- /dev/null +++ b/src/modules/projects/services/projects.ts @@ -0,0 +1,55 @@ +import { PENTEST_PROJECT_TAG, QUOTE_PROJECT_TAG } from '@/constants/projects' + +import { gitlabClient } from '@/api/gitlab/gitlab-client' +import { fetchPaginated } from '@/api/gitlab/utils/fetch-paginated' + +import { IGNORED_NAMESPACES_MAP } from '@/modules/projects/constants/namespaces' +import { + groupProjects, + normalizeProject, +} from '@/modules/projects/normalizers/normalize-project' + +import { createLogger } from '@/utils/logging/create-logger' + +const logger = createLogger('sync-projects') + +export const projects = async () => { + logger.debug('syncing...') + + const projects = await fetchPaginated(({ page, perPage }) => + gitlabClient.projects({ page, perPage }) + ) + + const withoutIgnored = projects + .filter((x) => !IGNORED_NAMESPACES_MAP[x.namespace.path]) + .filter( + (x) => + x.topics.includes(QUOTE_PROJECT_TAG) || + x.topics.includes(PENTEST_PROJECT_TAG) || + x.topics.includes('quote') + ) + + const projectsWithFile = await Promise.all( + withoutIgnored.map(async (project) => { + try { + const quote = await gitlabClient.projectFile({ + id: project.id, + path: 'source/offerte.xml', + branch: 'main', + }) + return { quote, project } + } catch (err) { + return { quote: null, project } + } + }) + ) + + const normalized = projectsWithFile.map((x) => + normalizeProject(x.project, x.quote) + ) + + const grouped = groupProjects(normalized) + logger.debug('data', grouped) + + return grouped +} diff --git a/src/modules/projects/services/templates.ts b/src/modules/projects/services/templates.ts new file mode 100644 index 0000000..054ffad --- /dev/null +++ b/src/modules/projects/services/templates.ts @@ -0,0 +1,14 @@ +import { gitlabClient } from '@/api/gitlab/gitlab-client' + +import { TEMPLATE_GROUP_PATH } from '../constants/namespaces' +import { normalizeTemplate } from '../normalizers/normalize-template' +import { Template } from '../types/template' + +export const templates = async (): Promise => { + const projects = await gitlabClient.groupProjects({ + path: TEMPLATE_GROUP_PATH, + scope: 'projects', + }) + const normalized = projects.map(normalizeTemplate) + return normalized +} diff --git a/src/modules/projects/services/update-project-file.ts b/src/modules/projects/services/update-project-file.ts new file mode 100644 index 0000000..537d83f --- /dev/null +++ b/src/modules/projects/services/update-project-file.ts @@ -0,0 +1,33 @@ +import { UpdateProjectFile } from '@/api/gitlab/client/update-project-file' +import { gitlabClient } from '@/api/gitlab/gitlab-client' + +import { createLogger } from '@/utils/logging/create-logger' + +import { normalizeProject } from '../normalizers/normalize-project' +import { Project } from '../types/project' + +const logger = createLogger('update-project-file') + +export const updateProjectFile = async ( + payload: UpdateProjectFile +): Promise => { + logger.debug('updating project file...') + const [project] = await Promise.all([ + gitlabClient.project({ id: payload.id }), + gitlabClient.updateProjectFile(payload), + ]) + + let quote = null + try { + quote = await gitlabClient.projectFile({ + id: project.id, + path: 'source/offerte.xml', + branch: 'main', + }) + } catch (err) { + /* empty */ + } + + const normalized = normalizeProject(project, quote) + return normalized +} diff --git a/src/modules/projects/services/update-project.ts b/src/modules/projects/services/update-project.ts new file mode 100644 index 0000000..ace7e73 --- /dev/null +++ b/src/modules/projects/services/update-project.ts @@ -0,0 +1,31 @@ +import { UpdateGitLabProject } from '@/api/gitlab/client/update-project' +import { gitlabClient } from '@/api/gitlab/gitlab-client' + +import { createLogger } from '@/utils/logging/create-logger' + +import { normalizeProject } from '../normalizers/normalize-project' +import { Project } from '../types/project' + +const logger = createLogger('update-project') + +export const updateProject = async ( + id: number, + payload: UpdateGitLabProject +): Promise => { + logger.debug('updating project...') + const project = await gitlabClient.updateProject(id, payload) + + let quote = null + try { + quote = await gitlabClient.projectFile({ + id: project.id, + path: 'source/offerte.xml', + branch: 'main', + }) + } catch (err) { + /* empty */ + } + + const normalized = normalizeProject(project, quote) + return normalized +} diff --git a/src/modules/projects/types/namespace.ts b/src/modules/projects/types/namespace.ts new file mode 100644 index 0000000..d59cbe8 --- /dev/null +++ b/src/modules/projects/types/namespace.ts @@ -0,0 +1,4 @@ +export type Namespace = { + id: number + path: string +} diff --git a/src/modules/projects/types/project-build.ts b/src/modules/projects/types/project-build.ts new file mode 100644 index 0000000..21ecb8c --- /dev/null +++ b/src/modules/projects/types/project-build.ts @@ -0,0 +1,18 @@ +export type ProjectBuildFile = { + type: 'report' | 'quote' | 'csv' + url: string +} + +export type ProjectBuild = { + id: number + url: string + status: string + files: ProjectBuildFile[] + createdAt: Date + finishedAt: Date + user: { + username: string + avatar: string | null + url: string + } +} diff --git a/src/modules/projects/types/project-details.ts b/src/modules/projects/types/project-details.ts new file mode 100644 index 0000000..1ad7272 --- /dev/null +++ b/src/modules/projects/types/project-details.ts @@ -0,0 +1,106 @@ +import { ProjectBuild } from './project-build' + +export enum ProjectDetailsFindingLabel { + ToDo = 'ToDo', + Extreme = 'Extreme', + High = 'High', + Elevated = 'Elevated', + Moderate = 'Moderate', + Low = 'Low', + Unknown = 'Unknown', +} + +export type ProjectDetailsMember = { + name: string + avatar: string | null + url: string +} + +export type ProjectDetailsFindingBase = { + id: number + iid: number + title: string + updatedAt: Date + url: string + description: string +} + +export type ProjectDetailsFindingFinding = ProjectDetailsFindingBase & { + label: ProjectDetailsFindingLabel +} + +export type ProjectDetailsFindingNonFinding = ProjectDetailsFindingBase & { + label: null +} + +export type ProjectDetailsFinding = + | ProjectDetailsFindingFinding + | ProjectDetailsFindingNonFinding + +export type ProjectDetailsPushEvent = { + action: 'pushed to' + commitFrom: string + commitTo: string + commitCount: number + commitTitle: string +} + +export type ProjectDetailsStateEvent = { + action: 'opened' | 'closed' | 'updated' + targetTitle: string + targetIid: string +} + +export type ProjectDetailsCommentEvent = { + action: 'commented on' + noteId: string + targetTitle: string + targetIid: string +} + +export type ProjectDetailsCreatedEvent = { + action: 'created' +} + +type ProjectDetailsUnknownEvent = { + action: string +} + +export type ProjectDetailsEvent = { + date: Date + time: string + user: string + userUrl: string + avatar: string + action: string + path: string +} & ( + | ProjectDetailsPushEvent + | ProjectDetailsStateEvent + | ProjectDetailsCommentEvent + | ProjectDetailsCreatedEvent + | ProjectDetailsUnknownEvent +) + +export type ProjectDetailsHistory = { + date: string + dateDisplay: string + events: ProjectDetailsEvent[] +} + +export type ProjectDetailsGroupedFindings = { + group: ProjectDetailsFindingLabel + findings: ProjectDetailsFindingFinding[] +} + +export type ProjectDetails = { + id: number + pdfPassword: string | null + staff: ProjectDetailsMember[] + customers: ProjectDetailsMember[] + allFindings: ProjectDetailsFinding[] + findings: ProjectDetailsGroupedFindings[] + nonFindings: ProjectDetailsFindingNonFinding[] + history: ProjectDetailsHistory[] + builds: ProjectBuild[] +} diff --git a/src/modules/projects/types/project-findings.ts b/src/modules/projects/types/project-findings.ts new file mode 100644 index 0000000..795b11b --- /dev/null +++ b/src/modules/projects/types/project-findings.ts @@ -0,0 +1,8 @@ +export type ProjectFindingDetails = { + projectId: number + issueId: number + recommendation: string + impact: string + technicalDescription: string + type: string +} diff --git a/src/modules/projects/types/project.ts b/src/modules/projects/types/project.ts new file mode 100644 index 0000000..0432ec2 --- /dev/null +++ b/src/modules/projects/types/project.ts @@ -0,0 +1,49 @@ +export type ProjectStatus = + | 'Staffing' + | 'Scoping' + | 'ProposalShared' + | 'ProposalAccepted' + | 'ProposalRejected' + | 'ProposalNoResponse' + | 'ToStart' + | 'Running' + | 'Delivered' + | 'PmClosed' + | 'ReTesting' + | 'Unknown' + +export type Project = { + id: number + status: ProjectStatus + type: 'quote' | 'pentest' + name: string + nameWithNamespace: string + path: string + pathWithNamespace: string + createdAt: Date + defaultBranch: string + ssh: string + url: string + readme: string + avatar: string + lastActivityAt: Date + isArchived: boolean + updatedAt: Date + namespace: { + id: number + name: string + path: string + url: string + avatar: string | null + } + tags: string[] + topics: string[] + isQuote: boolean + isPentest: boolean + quotePdf: string + reportPdf: string + startDate: Date | null + endDate: Date | null + quoteChannel: string + pentestChannel: string +} diff --git a/src/modules/projects/types/template.ts b/src/modules/projects/types/template.ts new file mode 100644 index 0000000..c0f4758 --- /dev/null +++ b/src/modules/projects/types/template.ts @@ -0,0 +1,5 @@ +export type Template = { + url: string + name: string + tagList: string[] +} diff --git a/src/modules/projects/utils/add-channel-names.ts b/src/modules/projects/utils/add-channel-names.ts new file mode 100644 index 0000000..de8718a --- /dev/null +++ b/src/modules/projects/utils/add-channel-names.ts @@ -0,0 +1,34 @@ +import { Project } from '../types/project' + +import { ChatSubscriptionMap } from '@/modules/chat/signals/chat-subscriptions-signal' +import { ChatSubscription } from '@/modules/chat/types/chat-subscription' + +export type ProjectWithChannelNames = { + project: Project + quoteChannel?: ChatSubscription + pentestChannel?: ChatSubscription +} + +export const addChannelNames = ( + projects: Project[], + chatMap: ChatSubscriptionMap +): ProjectWithChannelNames[] => { + const result = projects.map((project) => { + let baseName = project.name + + if (baseName.startsWith('pen-') || baseName.startsWith('off-')) { + baseName = baseName.slice(4) + } + + const quoteChannel = chatMap[`off-${baseName}`] + const pentestChannel = chatMap[`pen-${baseName}`] + + return { + project, + quoteChannel, + pentestChannel, + } + }) + + return result +} diff --git a/src/modules/projects/utils/build-artifacts.ts b/src/modules/projects/utils/build-artifacts.ts new file mode 100644 index 0000000..f0e6122 --- /dev/null +++ b/src/modules/projects/utils/build-artifacts.ts @@ -0,0 +1,50 @@ +import { config } from '@/config' + +const getArtifactBaseUrl = (projectId: number, jobId: number | 'main') => + `${config.app.gitlabBaseUrl}/projects/${projectId}/jobs/${jobId}/artifacts` + +export const getQuotePdfUrl = ( + projectId: number, + jobId: number | 'main', + projectName: string +) => `${getArtifactBaseUrl(projectId, jobId)}/target/offerte_${projectName}.pdf` + +export const getReportPdfUrl = ( + projectId: number, + jobId: number | 'main', + projectName: string +) => `${getArtifactBaseUrl(projectId, jobId)}/target/report_${projectName}.pdf` + +const getArtifactBaseUrlFromName = ( + namespace: string, + name: string, + jobId: number +) => + `${config.services.gitlabUrl}/${namespace}/${name}/-/jobs/${jobId}/artifacts/raw` + +export const getQuotePdfUrlFromName = ( + namespace: string, + name: string, + jobId: number +) => + `${getArtifactBaseUrlFromName(namespace, name, jobId)}/target/offerte_${name}.pdf` + +export const getReportPdfUrlFromName = ( + namespace: string, + name: string, + jobId: number +) => + `${getArtifactBaseUrlFromName(namespace, name, jobId)}/target/report_${name}.pdf` + +export const getCsvUrl = ( + projectId: number, + jobId: number, + projectName: string +) => `${getArtifactBaseUrl(projectId, jobId)}/target/report_${projectName}.csv` + +export const getPdfPageFromName = ( + namespace: string, + name: string, + jobId: number | 'main', + type: 'quote' | 'report' +) => `/projects/${namespace}/${name}/pdf/${jobId}/${type}` diff --git a/src/modules/projects/utils/is-comment-event.ts b/src/modules/projects/utils/is-comment-event.ts new file mode 100644 index 0000000..8195f94 --- /dev/null +++ b/src/modules/projects/utils/is-comment-event.ts @@ -0,0 +1,10 @@ +import { + type ProjectDetailsCommentEvent, + type ProjectDetailsEvent, +} from '@/modules/projects/types/project-details' + +export const isCommentEvent = ( + event: ProjectDetailsEvent +): event is ProjectDetailsEvent & ProjectDetailsCommentEvent => { + return event.action === 'commented on' +} diff --git a/src/modules/projects/utils/is-created-event.ts b/src/modules/projects/utils/is-created-event.ts new file mode 100644 index 0000000..20a7ecb --- /dev/null +++ b/src/modules/projects/utils/is-created-event.ts @@ -0,0 +1,10 @@ +import { + type ProjectDetailsCreatedEvent, + type ProjectDetailsEvent, +} from '@/modules/projects/types/project-details' + +export const isCreatedEvent = ( + event: ProjectDetailsEvent +): event is ProjectDetailsEvent & ProjectDetailsCreatedEvent => { + return event.action === 'created' +} diff --git a/src/modules/projects/utils/is-finding.ts b/src/modules/projects/utils/is-finding.ts new file mode 100644 index 0000000..21ce6a1 --- /dev/null +++ b/src/modules/projects/utils/is-finding.ts @@ -0,0 +1,10 @@ +import { + ProjectDetailsFinding, + ProjectDetailsFindingFinding, +} from '../types/project-details' + +export const isFinding = ( + finding: ProjectDetailsFinding +): finding is ProjectDetailsFindingFinding => { + return Boolean(finding.label) +} diff --git a/src/modules/projects/utils/is-non-finding.ts b/src/modules/projects/utils/is-non-finding.ts new file mode 100644 index 0000000..f183656 --- /dev/null +++ b/src/modules/projects/utils/is-non-finding.ts @@ -0,0 +1,10 @@ +import { + ProjectDetailsFinding, + ProjectDetailsFindingNonFinding, +} from '../types/project-details' + +export const isNonFinding = ( + finding: ProjectDetailsFinding +): finding is ProjectDetailsFindingNonFinding => { + return Boolean(!finding.label) +} diff --git a/src/modules/projects/utils/is-pentest.ts b/src/modules/projects/utils/is-pentest.ts new file mode 100644 index 0000000..8535990 --- /dev/null +++ b/src/modules/projects/utils/is-pentest.ts @@ -0,0 +1,28 @@ +import { + PENTEST_PROJECT_TAG, + PENTEST_PROJECT_PREFIX, +} from '@/constants/projects' + +import { type Project } from '@/modules/projects/types/project' + +export function isPentest(project: Project): boolean +export function isPentest(tags: string[], name: string): boolean +export function isPentest( + projectOrTags: Project | string[], + name?: string +): boolean { + let _tags: string[] = [] + let _name = name ?? '' + + if (!Array.isArray(projectOrTags)) { + _tags = projectOrTags.tags + _name = projectOrTags.name + } else { + _tags = projectOrTags + } + + return ( + _tags.includes(PENTEST_PROJECT_TAG) || + _name.startsWith(PENTEST_PROJECT_PREFIX) + ) +} diff --git a/src/modules/projects/utils/is-push-event.ts b/src/modules/projects/utils/is-push-event.ts new file mode 100644 index 0000000..bc04703 --- /dev/null +++ b/src/modules/projects/utils/is-push-event.ts @@ -0,0 +1,10 @@ +import { + type ProjectDetailsEvent, + type ProjectDetailsPushEvent, +} from '@/modules/projects/types/project-details' + +export const isPushEvent = ( + event: ProjectDetailsEvent +): event is ProjectDetailsEvent & ProjectDetailsPushEvent => { + return event.action === 'pushed to' +} diff --git a/src/modules/projects/utils/is-quote.ts b/src/modules/projects/utils/is-quote.ts new file mode 100644 index 0000000..68090dc --- /dev/null +++ b/src/modules/projects/utils/is-quote.ts @@ -0,0 +1,24 @@ +import { QUOTE_PROJECT_TAG, QUOTE_PROJECT_PREFIX } from '@/constants/projects' + +import { type Project } from '@/modules/projects/types/project' + +export function isQuote(project: Project): boolean +export function isQuote(tags: string[], name: string): boolean +export function isQuote( + projectOrTags: Project | string[], + name?: string +): boolean { + let _tags: string[] = [] + let _name = name ?? '' + + if (!Array.isArray(projectOrTags)) { + _tags = projectOrTags.tags + _name = projectOrTags.name + } else { + _tags = projectOrTags + } + + return ( + _tags.includes(QUOTE_PROJECT_TAG) || _name.startsWith(QUOTE_PROJECT_PREFIX) + ) +} diff --git a/src/modules/projects/utils/is-state-event.ts b/src/modules/projects/utils/is-state-event.ts new file mode 100644 index 0000000..8758a96 --- /dev/null +++ b/src/modules/projects/utils/is-state-event.ts @@ -0,0 +1,14 @@ +import { + type ProjectDetailsEvent, + type ProjectDetailsStateEvent, +} from '@/modules/projects/types/project-details' + +export const isStateEvent = ( + event: ProjectDetailsEvent +): event is ProjectDetailsEvent & ProjectDetailsStateEvent => { + return ( + event.action === 'opened' || + event.action === 'closed' || + event.action === 'updated' + ) +} diff --git a/src/modules/projects/utils/project-finding-key.ts b/src/modules/projects/utils/project-finding-key.ts new file mode 100644 index 0000000..7057ad4 --- /dev/null +++ b/src/modules/projects/utils/project-finding-key.ts @@ -0,0 +1,2 @@ +export const projectFindingKey = (projectId: number, issueId: number): string => + `${projectId}/${issueId}` diff --git a/src/modules/projects/utils/rewrite-uploads.ts b/src/modules/projects/utils/rewrite-uploads.ts new file mode 100644 index 0000000..57b028a --- /dev/null +++ b/src/modules/projects/utils/rewrite-uploads.ts @@ -0,0 +1,2 @@ +export const rewriteUploads = (value: string, baseUrl: string): string => + value.replaceAll('(/uploads/', `(${baseUrl}/uploads/`) diff --git a/src/modules/user/queries/user-query.ts b/src/modules/user/queries/user-query.ts new file mode 100644 index 0000000..bbcc885 --- /dev/null +++ b/src/modules/user/queries/user-query.ts @@ -0,0 +1,6 @@ +import { createQuery } from '@/utils/signal/query/create-query' + +import { getUser } from '../services/sync-user' +import { User } from '../types/user' + +export const userQuery = createQuery(getUser) diff --git a/src/modules/user/services/sync-user.ts b/src/modules/user/services/sync-user.ts new file mode 100644 index 0000000..3774cb1 --- /dev/null +++ b/src/modules/user/services/sync-user.ts @@ -0,0 +1,18 @@ +import { gitlabClient } from '@/api/gitlab/gitlab-client' + +import { authClient } from '@/modules/auth/client/auth-client' + +import { User } from '@/modules/user/types/user' + +export const getUser = async (): Promise => { + const [userInfo, gitlabUser] = await Promise.all([ + authClient.userInfoAsync(), + gitlabClient.user(), + ]) + + return { + name: userInfo.preferred_username ?? 'username_undefined', + groups: userInfo.groups ?? [], + avatar: gitlabUser.avatar_url, + } +} diff --git a/src/modules/user/types/user.ts b/src/modules/user/types/user.ts new file mode 100644 index 0000000..c0cb9c9 --- /dev/null +++ b/src/modules/user/types/user.ts @@ -0,0 +1,5 @@ +export type User = { + name: string + groups: string[] + avatar: string | null +} diff --git a/src/pages/auth/callback.ts b/src/pages/auth/callback.ts new file mode 100644 index 0000000..e67e833 --- /dev/null +++ b/src/pages/auth/callback.ts @@ -0,0 +1,54 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { authCallbackQuery } from '@/modules/auth/queries/auth-callback-query' +import { authEnsureQuery } from '@/modules/auth/queries/auth-ensure-query' + +import { updateTitle } from '@/modules/app/utils/update-title' + +const ELEMENT_NAME = 'auth-callback' + +@customElement(ELEMENT_NAME) +export class AuthCallback extends SignalWatcher(LitElement) { + protected async firstUpdated() { + updateTitle('login') + + const callbackPath = await authCallbackQuery.fetch() + if (callbackPath) { + // router.navigate doesn't navigate parent routes + window.location.href = callbackPath + } + } + + static styles = [...theme] + + render() { + const isAuthenticated = authEnsureQuery.data + + let text = '' + switch (isAuthenticated) { + case false: + text = 'Oops. An error occured while authenticating...' + break + + case true: + text = 'Authentication succesful. Redirecting...' + break + + default: + text = 'Authenticating...' + break + } + + return html`

${text}

` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: AuthCallback + } +} diff --git a/src/pages/home.ts b/src/pages/home.ts new file mode 100644 index 0000000..b21c91b --- /dev/null +++ b/src/pages/home.ts @@ -0,0 +1,83 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { html, css, LitElement } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { pageFlex } from '@/theme/shared/page' +import { theme } from '@/theme/theme' + +import { updateTitle } from '@/modules/app/utils/update-title' + +import { projectsQuery } from '@/modules/projects/queries/projects-query' + +import { userQuery } from '@/modules/user/queries/user-query' + +import '@/features/projects-overview/projects-overview' +import '@/features/project-list-item-large/project-list-item-large' + +const ELEMENT_NAME = 'home-page' + +@customElement(ELEMENT_NAME) +export class HomePage extends SignalWatcher(LitElement) { + protected firstUpdated() { + updateTitle() + } + + static styles = [ + ...theme, + ...pageFlex, + css` + #recently-updated { + margin-top: var(--sl-spacing-medium); + } + `, + ] + + render() { + const name = userQuery.data?.name + const pentests = projectsQuery.data?.pentests ?? [] + const quotes = projectsQuery.data?.quotes ?? [] + + const countPentestProjects = pentests.length + const countPentestSuffix = `pentest${countPentestProjects === 1 ? '' : 's'}` + + const countQuoteProjects = quotes.length + + const all = projectsQuery.data?.all ?? [] + + const byRecentlyUpdated = all + .slice() + .sort((a, b) => b.lastActivityAt.getTime() - a.lastActivityAt.getTime()) + .slice(0, 10) + + const now = new Date() + + return html` +

Hi ${name}!

+

+ You have access to ${countPentestProjects} ${countPentestSuffix} and + ${countQuoteProjects} quote projects. +

+
+ + + + +

Recently Updated

+
+ ${byRecentlyUpdated.map( + (project) => + html`` + )} +
+
` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: HomePage + } +} diff --git a/src/pages/not-found.ts b/src/pages/not-found.ts new file mode 100644 index 0000000..e4a9837 --- /dev/null +++ b/src/pages/not-found.ts @@ -0,0 +1,25 @@ +import { LitElement, html } from 'lit' +import { customElement } from 'lit/decorators.js' + +import { theme } from '@/theme/theme' + +import { updateTitle } from '@/modules/app/utils/update-title' + +@customElement('not-found-page') +export class NotFoundPage extends LitElement { + static styles = [...theme] + + protected firstUpdated() { + updateTitle('Not found') + } + + render() { + return html`

Not Found

` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'not-found-page': NotFoundPage + } +} diff --git a/src/pages/projects/[:namespace.:name]/index.ts b/src/pages/projects/[:namespace.:name]/index.ts new file mode 100644 index 0000000..f1f6939 --- /dev/null +++ b/src/pages/projects/[:namespace.:name]/index.ts @@ -0,0 +1,83 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { LitElement, html } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { updateTitle } from '@/modules/app/utils/update-title' + +import { projectDetailsQuery } from '@/modules/projects/queries/project-details.query' +import { projectsQuery } from '@/modules/projects/queries/projects-query' + +import '@/features/project-detail/project-detail' +import '@/features/project-detail/project-detail.skeleton' + +@customElement('project-detail-page') +export class ProjectDetailPage extends SignalWatcher(LitElement) { + @property({ type: String }) + private projectName = '' + @property({ type: String }) + private projectNamespace = '' + + protected firstUpdated() { + this.setTitle() + } + + protected updated(changedProperties: Map): void { + if ( + changedProperties.has('projectName') || + changedProperties.has('projectNamespace') + ) { + this.setTitle() + } + } + + private setTitle() { + updateTitle(`${this.projectNamespace}/${this.projectName}`) + } + + render() { + if (!this.projectName || !this.projectNamespace) { + return html`` + } + + const nameWithNamespace = `${this.projectNamespace}/${this.projectName}` + + const project = projectsQuery.data?.allByName[nameWithNamespace] + + if (!project) { + return html`` + } + + const projectDetailsMap = projectDetailsQuery.data?.[project.id] + + const notFound = + projectsQuery.status !== 'loading' && + !project && + projectDetailsQuery.status === 'loading' && + !projectDetailsMap?.isLoading && + !projectDetailsMap?.data + + if (notFound) { + return html`` + } + + if ( + (!projectsQuery.data && projectsQuery.status === 'loading') || + (!projectDetailsMap?.data && projectDetailsQuery.status === 'loading') + ) { + return html`` + } + + return html` projectDetailsQuery.fetch([project.id, 'network'])} + .isDetailsLoading=${projectDetailsQuery.status === 'loading'} + >` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'project-detail-page': ProjectDetailPage + } +} diff --git a/src/pages/projects/[:namespace.:name]/pdf.ts b/src/pages/projects/[:namespace.:name]/pdf.ts new file mode 100644 index 0000000..8dae96b --- /dev/null +++ b/src/pages/projects/[:namespace.:name]/pdf.ts @@ -0,0 +1,102 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { LitElement, html } from 'lit' +import { customElement, property } from 'lit/decorators.js' + +import { updateTitle } from '@/modules/app/utils/update-title' + +import { projectDetailsQuery } from '@/modules/projects/queries/project-details.query' +import { projectsQuery } from '@/modules/projects/queries/projects-query' +import { + getQuotePdfUrl, + getReportPdfUrl, +} from '@/modules/projects/utils/build-artifacts' + +import { capitalize } from '@/utils/string/capitalize' + +import '@/elements/pdf-preview/pdf-preview' + +const ELEMENT_NAME = 'project-pdf-page' + +@customElement(ELEMENT_NAME) +export class ProjectPdfPage extends SignalWatcher(LitElement) { + @property() + private projectName = '' + @property() + private projectNamespace = '' + @property() + private job: number | 'main' = 'main' + @property() + private type!: 'quote' | 'report' + + protected firstUpdated() { + this.setTitle() + } + + protected updated(changedProperties: Map): void { + if ( + ['projectName', 'projectNamespace', 'job', 'type'].some((x) => + changedProperties.has(x) + ) + ) { + this.setTitle() + } + } + + private setTitle() { + updateTitle( + `${capitalize(this.type)} PDF ${this.projectNamespace}/${this.projectName}` + ) + } + + render() { + if (!this.projectName || !this.projectNamespace || !this.type) { + return html`` + } + + const nameWithNamespace = `${this.projectNamespace}/${this.projectName}` + + const project = projectsQuery.data?.allByName[nameWithNamespace] + + if (!project) { + return html`` + } + + const projectDetailsMap = projectDetailsQuery.data?.[project.id] + + const notFound = + projectsQuery.status !== 'loading' && + !project && + projectDetailsQuery.status === 'loading' && + !projectDetailsMap?.isLoading && + !projectDetailsMap?.data + + if (notFound) { + return html`` + } + + if ( + (!projectsQuery.data && projectsQuery.status === 'loading') || + (!projectDetailsMap?.data && projectDetailsQuery.status === 'loading') + ) { + return html`` + } + + let url = '' + if (this.job === 'main') { + url = this.type === 'quote' ? project.quotePdf : project.reportPdf + } else { + const fn = this.type === 'quote' ? getQuotePdfUrl : getReportPdfUrl + url = fn(project.id, this.job, project.name) + } + + const password = projectDetailsMap?.data?.pdfPassword + + return html`` + } +} + +declare global { + interface HTMLElementTagNameMap { + [ELEMENT_NAME]: ProjectPdfPage + } +} diff --git a/src/pages/projects/new.ts b/src/pages/projects/new.ts new file mode 100644 index 0000000..44a4b6f --- /dev/null +++ b/src/pages/projects/new.ts @@ -0,0 +1,149 @@ +import { SignalWatcher } from '@lit-labs/preact-signals' +import { SlInput, SlSelect } from '@shoelace-style/shoelace' +import { LitElement, css, html } from 'lit' +import { customElement } from 'lit/decorators.js' +import { map } from 'lit/directives/map.js' +import { Ref, createRef, ref } from 'lit/directives/ref.js' + +import { theme } from '@/theme/theme' + +import { QUOTE_PROJECT_TAG } from '@/constants/projects' + +import { appSignal } from '@/modules/app/signals/app-signal' +import { updateTitle } from '@/modules/app/utils/update-title' + +import { createProjectQuery } from '@/modules/projects/queries/create-project-query' +import { namespacesQuery } from '@/modules/projects/queries/namespaces-query' +import { templatesQuery } from '@/modules/projects/queries/templates-query' + +@customElement('project-new-page') +export class ProjectNewPage extends SignalWatcher(LitElement) { + private formRef: Ref = createRef() + + private namespaceRef: Ref = createRef() + private nameRef: Ref = createRef() + private templateRef: Ref = createRef() + + disconnectedCallback(): void { + super.disconnectedCallback() + this.formRef.value?.removeEventListener('submit', (e) => { + void this.onSubmit(e) + }) + } + + protected firstUpdated(): void { + updateTitle('New project') + + this.formRef.value?.addEventListener('submit', (e) => { + void this.onSubmit(e) + }) + } + + static styles = [ + ...theme, + css` + #inputs { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-x-large); + margin-top: var(--sl-spacing-x-large); + } + + #button { + text-align: right; + } + + #button::part(base) { + width: auto; + } + `, + ] + + protected async onSubmit(e: Event) { + e.preventDefault() + e.stopPropagation() + + if (!appSignal.gitLabToken) { + return appSignal.setShowGitLabTokenDialog(true) + } + + const namespaceValue = this.namespaceRef.value?.value + const name = this.nameRef.value?.value + const templateValue = this.templateRef.value?.value + + if (!namespaceValue || !name || !templateValue) { + throw new Error('values not defined') + } + + const namespaceData = namespacesQuery.data ?? [] + const templateData = templatesQuery.data ?? [] + + const namespace = namespaceData.find( + (namespace) => namespace.id === Number(namespaceValue) + ) + const template = templateData.find( + (template) => template.name === templateValue + ) + + if (!namespace || !template) { + throw new Error('namespace or template not found') + } + + const result = await createProjectQuery.fetch([ + template.url, + name, + QUOTE_PROJECT_TAG, + namespace.id, + ]) + + window.location.href = `/projects/${result.pathWithNamespace}` + } + + render() { + return html` + +

Create new project

+
+ + ${map( + namespacesQuery.data ?? [], + (namespace) => html` + ${namespace.path} + ` + )} + + + + ${map( + templatesQuery.data ?? [], + (template) => html` + ${template.name} + ` + )} + + Create +
+
+ ` + } +} + +declare global { + interface HTMLElementTagNameMap { + 'project-new-page': ProjectNewPage + } +} diff --git a/src/polyfills.ts b/src/polyfills.ts new file mode 100644 index 0000000..fb4cf32 --- /dev/null +++ b/src/polyfills.ts @@ -0,0 +1,9 @@ +// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access +if (!(globalThis as any).URLPattern) { + // used by @lit-labs/router + // https://developer.mozilla.org/en-US/docs/Web/API/URLPattern#browser_compatibility + await import('urlpattern-polyfill') +} + +// top level await only allowed in modules +export {} diff --git a/src/routes.ts b/src/routes.ts new file mode 100644 index 0000000..7350f99 --- /dev/null +++ b/src/routes.ts @@ -0,0 +1,147 @@ +import { createContext } from '@lit/context' +import { RouteConfig, Router } from '@lit-labs/router' +import { html } from 'lit' + +import { authEnsureQuery } from './modules/auth/queries/auth-ensure-query' +import { namespacesQuery } from './modules/projects/queries/namespaces-query' +import { projectDetailsQuery } from './modules/projects/queries/project-details.query' +import { projectsQuery } from './modules/projects/queries/projects-query' +import { templatesQuery } from './modules/projects/queries/templates-query' +import { userQuery } from './modules/user/queries/user-query' + +export const routerContext = createContext('router') + +export enum AppRoute { + Home = '/', + NewProject = '/projects/new', + ProjectDetail = '/projects/:namespace/:name', + ProjectPdf = '/projects/:namespace/:name/pdf/:job/:type', + AuthCallback = '/auth/callback', +} + +export const routes: RouteConfig[] = [ + { + path: AppRoute.Home, + render: () => html``, + enter: async () => { + await import('@/pages/home') + if (!(await authEnsureQuery.fetch())) { + return true + } + + await Promise.all([ + userQuery.data ? Promise.resolve() : userQuery.fetch(), + projectsQuery.data ? Promise.resolve() : projectsQuery.fetch(), + ]) + + return true + }, + }, + { + path: AppRoute.NewProject, + render: () => html``, + enter: async () => { + await import('@/pages/projects/new') + + if (!(await authEnsureQuery.fetch())) { + return true + } + + await Promise.all([ + userQuery.data ? Promise.resolve() : userQuery.fetch(), + projectsQuery.data ? Promise.resolve() : projectsQuery.fetch(), + namespacesQuery.fetch(), + templatesQuery.fetch(), + ]) + return true + }, + }, + { + path: AppRoute.ProjectDetail, + render: ({ name, namespace }) => + html``, + enter: async ({ name, namespace }) => { + await import('@/pages/projects/[:namespace.:name]/index') + + if (!(await authEnsureQuery.fetch())) { + return true + } + + if (!name || !namespace) { + return true + } + + await Promise.all([ + userQuery.data ? Promise.resolve() : userQuery.fetch(), + projectsQuery.data ? Promise.resolve() : projectsQuery.fetch(), + ]) + + const nameWithNamespace = `${namespace}/${name}` + + const project = projectsQuery.data?.allByName[nameWithNamespace] + + const projectDetailsPromise = project + ? projectDetailsQuery.fetch([project.id]) + : Promise.resolve() + + await projectDetailsPromise + return true + }, + }, + { + path: AppRoute.ProjectPdf, + render: ({ name, namespace, job, type }) => + html``, + enter: async ({ name, namespace }) => { + await import('@/pages/projects/[:namespace.:name]/pdf') + + if (!(await authEnsureQuery.fetch())) { + return true + } + + if (!name || !namespace) { + return true + } + + await Promise.all([ + userQuery.data ? Promise.resolve() : userQuery.fetch(), + projectsQuery.data ? Promise.resolve() : projectsQuery.fetch(), + ]) + + const nameWithNamespace = `${namespace}/${name}` + + const project = projectsQuery.data?.allByName[nameWithNamespace] + + if (project && projectDetailsQuery.data?.[project.id]) { + return true + } + + const projectDetailsPromise = project + ? projectDetailsQuery.fetch([project.id]) + : Promise.resolve() + + await projectDetailsPromise + return true + }, + }, + { + path: AppRoute.AuthCallback, + render: () => html``, + enter: async () => { + await import('@/pages/auth/callback') + return true + }, + }, + { + path: '/*', + render: () => html``, + }, +] diff --git a/src/theme/base.css b/src/theme/base.css new file mode 100644 index 0000000..bbe9cd5 --- /dev/null +++ b/src/theme/base.css @@ -0,0 +1,12 @@ +:root { + --content-padding: var(--sl-spacing-large); +} + +:root, +:host, +html, +body { + height: 100vh; + padding: 0; + margin: 0; +} diff --git a/src/theme/dark.css b/src/theme/dark.css new file mode 100644 index 0000000..5607d0f --- /dev/null +++ b/src/theme/dark.css @@ -0,0 +1,500 @@ +/** + * Adapted from + * @shoelace-style/shoelace/dist/themes/dark.css + * Same license applies + */ + +/* stylelint-disable */ + +:host, +.sl-theme-dark { + color-scheme: dark; + + --sl-color-gray-50: hsl(240 5.1% 15%); + --sl-color-gray-100: hsl(240 5.7% 18.2%); + --sl-color-gray-200: hsl(240 4.6% 22%); + --sl-color-gray-300: hsl(240 5% 27.6%); + --sl-color-gray-400: hsl(240 5% 35.5%); + --sl-color-gray-500: hsl(240 3.7% 44%); + --sl-color-gray-600: hsl(240 5.3% 58%); + --sl-color-gray-700: hsl(240 5.6% 73%); + --sl-color-gray-800: hsl(240 7.3% 84%); + --sl-color-gray-900: hsl(240 9.1% 91.8%); + --sl-color-gray-950: hsl(0 0% 95%); + + --sl-color-red-50: hsl(0 56% 23.9%); + --sl-color-red-100: hsl(0.6 60% 33.9%); + --sl-color-red-200: hsl(0.9 67.2% 37.1%); + --sl-color-red-300: hsl(1.1 71.3% 43.7%); + --sl-color-red-400: hsl(1 76% 52.5%); + --sl-color-red-500: hsl(0.7 89.6% 57.2%); + --sl-color-red-600: hsl(0 98.6% 67.9%); + --sl-color-red-700: hsl(0 100% 72.3%); + --sl-color-red-800: hsl(0 100% 85.6%); + --sl-color-red-900: hsl(0 100% 90.3%); + --sl-color-red-950: hsl(0 100% 95.9%); + + --sl-color-orange-50: rgb(65 29 9); + --sl-color-orange-100: rgb(105 46 14); + --sl-color-orange-200: rgb(147 65 20); + --sl-color-orange-300: rgb(178 79 24); + --sl-color-orange-400: rgb(216 96 29); + --sl-color-orange-500: rgb(244 128 64); + --sl-color-orange-600: rgb(247 167 123); + --sl-color-orange-700: rgb(250 195 165); + --sl-color-orange-800: rgb(251 215 195); + --sl-color-orange-900: rgb(253 232 221); + --sl-color-orange-950: rgb(254 248 245); + + --sl-color-orange-50: hsl(15 64.2% 23.3%); + --sl-color-orange-100: hsl(15.1 70.9% 31.1%); + --sl-color-orange-200: hsl(15.3 75.7% 35.5%); + --sl-color-orange-300: hsl(17.1 83.5% 42.7%); + --sl-color-orange-400: hsl(20.1 88% 50.8%); + --sl-color-orange-500: hsl(24.3 100% 50.5%); + --sl-color-orange-600: hsl(27.2 100% 57.7%); + --sl-color-orange-700: hsl(31.3 100% 68.7%); + --sl-color-orange-800: hsl(33.8 100% 79.3%); + --sl-color-orange-900: hsl(38.9 100% 87.7%); + --sl-color-orange-950: hsl(46.2 100% 95%); + + --sl-color-amber-50: hsl(21.9 66.3% 21.1%); + --sl-color-amber-100: hsl(21.5 73.6% 29.7%); + --sl-color-amber-200: hsl(22.3 77.6% 33.3%); + --sl-color-amber-300: hsl(25.4 84.2% 39.6%); + --sl-color-amber-400: hsl(31.4 87.4% 46.7%); + --sl-color-amber-500: hsl(37 96.6% 48.3%); + --sl-color-amber-600: hsl(43.3 100% 53.4%); + --sl-color-amber-700: hsl(46.5 100% 61.1%); + --sl-color-amber-800: hsl(49.3 100% 73%); + --sl-color-amber-900: hsl(51.8 100% 85%); + --sl-color-amber-950: hsl(60 100% 94.6%); + + --sl-color-yellow-50: hsl(32.5 60% 18.2%); + --sl-color-yellow-100: hsl(28.1 68.6% 29%); + --sl-color-yellow-200: hsl(31.3 75.8% 30.8%); + --sl-color-yellow-300: hsl(34.7 84.4% 35.3%); + --sl-color-yellow-400: hsl(40.1 87.3% 43.3%); + --sl-color-yellow-500: hsl(44.7 88% 46%); + --sl-color-yellow-600: hsl(47.7 100% 50.9%); + --sl-color-yellow-700: hsl(51.3 100% 59.9%); + --sl-color-yellow-800: hsl(54.6 100% 73%); + --sl-color-yellow-900: hsl(58.9 100% 84.2%); + --sl-color-yellow-950: hsl(60 100% 94%); + + --sl-color-lime-50: hsl(86.5 54.4% 18%); + --sl-color-lime-100: hsl(87.6 56.8% 23.3%); + --sl-color-lime-200: hsl(85.8 63.2% 24.5%); + --sl-color-lime-300: hsl(86.1 72% 29.4%); + --sl-color-lime-400: hsl(85.5 76.8% 37.3%); + --sl-color-lime-500: hsl(84.3 74.2% 42.1%); + --sl-color-lime-600: hsl(82.8 81.5% 52.6%); + --sl-color-lime-700: hsl(82 89.9% 64%); + --sl-color-lime-800: hsl(80.9 97.9% 76.6%); + --sl-color-lime-900: hsl(77.9 100% 85.8%); + --sl-color-lime-950: hsl(69.5 100% 93.8%); + + --sl-color-green-50: hsl(144.3 53.6% 16%); + --sl-color-green-100: hsl(143.2 55.4% 23.5%); + --sl-color-green-200: hsl(141.5 58.2% 26.3%); + --sl-color-green-300: hsl(140.8 64.2% 31.8%); + --sl-color-green-400: hsl(140.3 68% 39.2%); + --sl-color-green-500: hsl(141.1 64.9% 43%); + --sl-color-green-600: hsl(141.6 72.4% 55.2%); + --sl-color-green-700: hsl(141.7 82.7% 70.1%); + --sl-color-green-800: hsl(141 90.9% 82.1%); + --sl-color-green-900: hsl(142 100% 89.1%); + --sl-color-green-950: hsl(144 100% 95.5%); + + --sl-color-emerald-50: hsl(164.3 75% 13.5%); + --sl-color-emerald-100: hsl(163.5 72.6% 20.1%); + --sl-color-emerald-200: hsl(162.1 73.7% 22.4%); + --sl-color-emerald-300: hsl(161.3 77.3% 27.6%); + --sl-color-emerald-400: hsl(159.6 77.1% 34.3%); + --sl-color-emerald-500: hsl(159.1 73.5% 37.9%); + --sl-color-emerald-600: hsl(157.8 66.8% 48.9%); + --sl-color-emerald-700: hsl(156.2 76.1% 63.8%); + --sl-color-emerald-800: hsl(152.4 84.4% 77.4%); + --sl-color-emerald-900: hsl(149.3 100% 87%); + --sl-color-emerald-950: hsl(158.6 100% 94.8%); + + --sl-color-teal-50: hsl(176.5 51.5% 15.4%); + --sl-color-teal-100: hsl(175.9 54.7% 22.3%); + --sl-color-teal-200: hsl(175.9 60.7% 23.9%); + --sl-color-teal-300: hsl(174.5 67.3% 28.8%); + --sl-color-teal-400: hsl(174.4 71.9% 34.9%); + --sl-color-teal-500: hsl(173.1 71% 38.3%); + --sl-color-teal-600: hsl(172.3 68.2% 48.1%); + --sl-color-teal-700: hsl(170.5 81.3% 61.5%); + --sl-color-teal-800: hsl(168.4 92.1% 75.2%); + --sl-color-teal-900: hsl(168.3 100% 86%); + --sl-color-teal-950: hsl(180 100% 95.5%); + + --sl-color-cyan-50: hsl(197.1 53.8% 20.3%); + --sl-color-cyan-100: hsl(196.8 57.3% 27.2%); + --sl-color-cyan-200: hsl(195.3 62.7% 29.4%); + --sl-color-cyan-300: hsl(193.5 71.3% 34.1%); + --sl-color-cyan-400: hsl(192.5 76.8% 40.6%); + --sl-color-cyan-500: hsl(189.4 78.6% 42.6%); + --sl-color-cyan-600: hsl(188.2 89.1% 51.7%); + --sl-color-cyan-700: hsl(187 98.6% 66.2%); + --sl-color-cyan-800: hsl(184.9 100% 78.3%); + --sl-color-cyan-900: hsl(180 100% 86.6%); + --sl-color-cyan-950: hsl(180 100% 94.8%); + + --sl-color-sky-50: hsl(203 63.8% 20.9%); + --sl-color-sky-100: hsl(203.4 70.4% 28%); + --sl-color-sky-200: hsl(202.7 75.8% 30.8%); + --sl-color-sky-300: hsl(203.1 80.4% 36.1%); + --sl-color-sky-400: hsl(202.1 80.5% 44.3%); + --sl-color-sky-500: hsl(199.7 85.9% 47.7%); + --sl-color-sky-600: hsl(198.7 97.9% 57.2%); + --sl-color-sky-700: hsl(198.7 100% 70.5%); + --sl-color-sky-800: hsl(198.8 100% 82.5%); + --sl-color-sky-900: hsl(198.5 100% 89.9%); + --sl-color-sky-950: hsl(186 100% 95.5%); + + --sl-color-blue-50: hsl(227.1 49.5% 22.7%); + --sl-color-blue-100: hsl(225.8 58.9% 36.8%); + --sl-color-blue-200: hsl(227.7 64.4% 42.9%); + --sl-color-blue-300: hsl(226.1 72.7% 51.2%); + --sl-color-blue-400: hsl(222.6 86.5% 56.3%); + --sl-color-blue-500: hsl(217.8 95.8% 57.4%); + --sl-color-blue-600: hsl(213.3 100% 65%); + --sl-color-blue-700: hsl(210.9 100% 74.8%); + --sl-color-blue-800: hsl(211.5 100% 83.4%); + --sl-color-blue-900: hsl(211 100% 88.9%); + --sl-color-blue-950: hsl(201.8 100% 95.3%); + + --sl-color-indigo-50: hsl(243.5 40.8% 27%); + --sl-color-indigo-100: hsl(242.9 45.7% 37.6%); + --sl-color-indigo-200: hsl(244.7 52.7% 43.1%); + --sl-color-indigo-300: hsl(245.3 60.5% 52.4%); + --sl-color-indigo-400: hsl(244.1 79.2% 60.4%); + --sl-color-indigo-500: hsl(239.6 88.7% 63.8%); + --sl-color-indigo-600: hsl(234.5 96.7% 70.9%); + --sl-color-indigo-700: hsl(229.4 100% 78.3%); + --sl-color-indigo-800: hsl(227.1 100% 85%); + --sl-color-indigo-900: hsl(223.8 100% 89.9%); + --sl-color-indigo-950: hsl(220 100% 95.1%); + + --sl-color-violet-50: hsl(265.1 57.3% 25.4%); + --sl-color-violet-100: hsl(263.5 63.8% 39.4%); + --sl-color-violet-200: hsl(263.4 66.2% 44.1%); + --sl-color-violet-300: hsl(263.7 72.8% 52.4%); + --sl-color-violet-400: hsl(262.5 87.3% 59.8%); + --sl-color-violet-500: hsl(258.3 95.1% 63.2%); + --sl-color-violet-600: hsl(255.1 100% 67.2%); + --sl-color-violet-700: hsl(253 100% 81.5%); + --sl-color-violet-800: hsl(251.7 100% 87.9%); + --sl-color-violet-900: hsl(254.1 100% 91.7%); + --sl-color-violet-950: hsl(257.1 100% 96.1%); + + --sl-color-purple-50: hsl(276 54.3% 20.5%); + --sl-color-purple-100: hsl(273.6 61.8% 35.4%); + --sl-color-purple-200: hsl(272.9 64% 41.4%); + --sl-color-purple-300: hsl(271.9 68.1% 49.2%); + --sl-color-purple-400: hsl(271.5 85.1% 57.8%); + --sl-color-purple-500: hsl(270.7 96.4% 62.1%); + --sl-color-purple-600: hsl(270.5 100% 71.9%); + --sl-color-purple-700: hsl(270.9 100% 81.3%); + --sl-color-purple-800: hsl(272.4 100% 87.7%); + --sl-color-purple-900: hsl(276.7 100% 91.5%); + --sl-color-purple-950: hsl(300 100% 96.5%); + + --sl-color-fuchsia-50: hsl(297.1 51.2% 18.6%); + --sl-color-fuchsia-100: hsl(296.7 59.5% 31.5%); + --sl-color-fuchsia-200: hsl(295.4 65.4% 35.1%); + --sl-color-fuchsia-300: hsl(294.6 67.4% 42.2%); + --sl-color-fuchsia-400: hsl(293.3 68.7% 51.2%); + --sl-color-fuchsia-500: hsl(292.1 88.4% 57.7%); + --sl-color-fuchsia-600: hsl(292 98.5% 59.5%); + --sl-color-fuchsia-700: hsl(292.4 100% 79.5%); + --sl-color-fuchsia-800: hsl(292.9 100% 86.8%); + --sl-color-fuchsia-900: hsl(300 100% 91.5%); + --sl-color-fuchsia-950: hsl(300 100% 96.3%); + + --sl-color-pink-50: hsl(336.2 59.6% 20%); + --sl-color-pink-100: hsl(336.8 63.9% 34%); + --sl-color-pink-200: hsl(336.8 68.7% 37.6%); + --sl-color-pink-300: hsl(336.1 71.8% 44.5%); + --sl-color-pink-400: hsl(333.9 74.9% 53.1%); + --sl-color-pink-500: hsl(330.7 86.3% 57.7%); + --sl-color-pink-600: hsl(328.6 91.5% 67.2%); + --sl-color-pink-700: hsl(327.4 97.6% 78.7%); + --sl-color-pink-800: hsl(325.1 100% 86.6%); + --sl-color-pink-900: hsl(322.1 100% 91.3%); + --sl-color-pink-950: hsl(315 100% 95.9%); + + --sl-color-rose-50: hsl(342.3 62.9% 21.5%); + --sl-color-rose-100: hsl(342.8 68.9% 34.2%); + --sl-color-rose-200: hsl(344.8 72.6% 37.3%); + --sl-color-rose-300: hsl(346.9 75.8% 43.7%); + --sl-color-rose-400: hsl(348.2 80.1% 52.7%); + --sl-color-rose-500: hsl(350.4 94.8% 57.5%); + --sl-color-rose-600: hsl(351.2 100% 58.1%); + --sl-color-rose-700: hsl(352.3 100% 78.1%); + --sl-color-rose-800: hsl(352 100% 86.2%); + --sl-color-rose-900: hsl(354.5 100% 90.7%); + --sl-color-rose-950: hsl(353.3 100% 95.7%); + + --sl-color-primary-50: var(--sl-color-orange-50); + --sl-color-primary-100: var(--sl-color-orange-100); + --sl-color-primary-200: var(--sl-color-orange-200); + --sl-color-primary-300: var(--sl-color-orange-300); + --sl-color-primary-400: var(--sl-color-orange-400); + --sl-color-primary-500: var(--sl-color-orange-500); + --sl-color-primary-600: var(--sl-color-orange-600); + --sl-color-primary-700: var(--sl-color-orange-700); + --sl-color-primary-800: var(--sl-color-orange-800); + --sl-color-primary-900: var(--sl-color-orange-900); + --sl-color-primary-950: var(--sl-color-orange-950); + + --sl-color-success-50: var(--sl-color-green-50); + --sl-color-success-100: var(--sl-color-green-100); + --sl-color-success-200: var(--sl-color-green-200); + --sl-color-success-300: var(--sl-color-green-300); + --sl-color-success-400: var(--sl-color-green-400); + --sl-color-success-500: var(--sl-color-green-500); + --sl-color-success-600: var(--sl-color-green-600); + --sl-color-success-700: var(--sl-color-green-700); + --sl-color-success-800: var(--sl-color-green-800); + --sl-color-success-900: var(--sl-color-green-900); + --sl-color-success-950: var(--sl-color-green-950); + + --sl-color-warning-50: var(--sl-color-amber-50); + --sl-color-warning-100: var(--sl-color-amber-100); + --sl-color-warning-200: var(--sl-color-amber-200); + --sl-color-warning-300: var(--sl-color-amber-300); + --sl-color-warning-400: var(--sl-color-amber-400); + --sl-color-warning-500: var(--sl-color-amber-500); + --sl-color-warning-600: var(--sl-color-amber-600); + --sl-color-warning-700: var(--sl-color-amber-700); + --sl-color-warning-800: var(--sl-color-amber-800); + --sl-color-warning-900: var(--sl-color-amber-900); + --sl-color-warning-950: var(--sl-color-amber-950); + + --sl-color-danger-50: var(--sl-color-red-50); + --sl-color-danger-100: var(--sl-color-red-100); + --sl-color-danger-200: var(--sl-color-red-200); + --sl-color-danger-300: var(--sl-color-red-300); + --sl-color-danger-400: var(--sl-color-red-400); + --sl-color-danger-500: var(--sl-color-red-500); + --sl-color-danger-600: var(--sl-color-red-600); + --sl-color-danger-700: var(--sl-color-red-700); + --sl-color-danger-800: var(--sl-color-red-800); + --sl-color-danger-900: var(--sl-color-red-900); + --sl-color-danger-950: var(--sl-color-red-950); + + --sl-color-neutral-50: var(--sl-color-gray-50); + --sl-color-neutral-100: var(--sl-color-gray-100); + --sl-color-neutral-200: var(--sl-color-gray-200); + --sl-color-neutral-300: var(--sl-color-gray-300); + --sl-color-neutral-400: var(--sl-color-gray-400); + --sl-color-neutral-500: var(--sl-color-gray-500); + --sl-color-neutral-600: var(--sl-color-gray-600); + --sl-color-neutral-700: var(--sl-color-gray-700); + --sl-color-neutral-800: var(--sl-color-gray-800); + --sl-color-neutral-900: var(--sl-color-gray-900); + --sl-color-neutral-950: var(--sl-color-gray-950); + + --sl-color-neutral-0: hsl(240, 5.9%, 11%); + --sl-color-neutral-1000: hsl(0, 0%, 100%); + + --sl-border-radius-small: 0.1875rem; + --sl-border-radius-medium: 0.25rem; + --sl-border-radius-large: 0.5rem; + --sl-border-radius-x-large: 1rem; + + --sl-border-radius-circle: 50%; + --sl-border-radius-pill: 9999px; + + --sl-shadow-x-small: 0 1px 2px rgb(0 0 0 / 18%); + --sl-shadow-small: 0 1px 2px rgb(0 0 0 / 24%); + --sl-shadow-medium: 0 2px 4px rgb(0 0 0 / 24%); + --sl-shadow-large: 0 2px 8px rgb(0 0 0 / 24%); + --sl-shadow-x-large: 0 4px 16px rgb(0 0 0 / 24%); + + --sl-spacing-3x-small: 0.125rem; + --sl-spacing-2x-small: 0.25rem; + --sl-spacing-x-small: 0.5rem; + --sl-spacing-small: 0.75rem; + --sl-spacing-medium: 1rem; + --sl-spacing-large: 1.25rem; + --sl-spacing-x-large: 1.75rem; + --sl-spacing-2x-large: 2.25rem; + --sl-spacing-3x-large: 3rem; + --sl-spacing-4x-large: 4.5rem; + + --sl-transition-x-slow: 1000ms; + --sl-transition-slow: 500ms; + --sl-transition-medium: 250ms; + --sl-transition-fast: 150ms; + --sl-transition-x-fast: 50ms; + + --sl-font-mono: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace; + --sl-font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol'; + --sl-font-serif: Georgia, 'Times New Roman', serif; + + --sl-font-size-2x-small: 0.625rem; + --sl-font-size-x-small: 0.75rem; + --sl-font-size-small: 0.875rem; + --sl-font-size-medium: 1rem; + --sl-font-size-large: 1.25rem; + --sl-font-size-x-large: 1.5rem; + --sl-font-size-2x-large: 2.25rem; + --sl-font-size-3x-large: 3rem; + --sl-font-size-4x-large: 4.5rem; + + --sl-font-weight-light: 300; + --sl-font-weight-normal: 400; + --sl-font-weight-semibold: 500; + --sl-font-weight-bold: 700; + + --sl-letter-spacing-denser: -0.03em; + --sl-letter-spacing-dense: -0.015em; + --sl-letter-spacing-normal: normal; + --sl-letter-spacing-loose: 0.075em; + --sl-letter-spacing-looser: 0.15em; + + --sl-line-height-denser: 1; + --sl-line-height-dense: 1.4; + --sl-line-height-normal: 1.8; + --sl-line-height-loose: 2.2; + --sl-line-height-looser: 2.6; + + --sl-focus-ring-color: var(--sl-color-primary-700); + --sl-focus-ring-style: solid; + --sl-focus-ring-width: 3px; + --sl-focus-ring: var(--sl-focus-ring-style) var(--sl-focus-ring-width) + var(--sl-focus-ring-color); + --sl-focus-ring-offset: 1px; + + --sl-button-font-size-small: var(--sl-font-size-x-small); + --sl-button-font-size-medium: var(--sl-font-size-small); + --sl-button-font-size-large: var(--sl-font-size-medium); + + --sl-input-height-small: 1.875rem; + --sl-input-height-medium: 2.5rem; + --sl-input-height-large: 3.125rem; + + --sl-input-background-color: var(--sl-color-neutral-0); + --sl-input-background-color-hover: var(--sl-input-background-color); + --sl-input-background-color-focus: var(--sl-input-background-color); + --sl-input-background-color-disabled: var(--sl-color-neutral-100); + --sl-input-border-color: var(--sl-color-neutral-300); + --sl-input-border-color-hover: var(--sl-color-neutral-400); + --sl-input-border-color-focus: var(--sl-color-primary-500); + --sl-input-border-color-disabled: var(--sl-color-neutral-300); + --sl-input-border-width: 1px; + --sl-input-required-content: '*'; + --sl-input-required-content-offset: -2px; + --sl-input-required-content-color: var(--sl-input-label-color); + + --sl-input-border-radius-small: var(--sl-border-radius-medium); + --sl-input-border-radius-medium: var(--sl-border-radius-medium); + --sl-input-border-radius-large: var(--sl-border-radius-medium); + + --sl-input-font-family: var(--sl-font-sans); + --sl-input-font-weight: var(--sl-font-weight-normal); + --sl-input-font-size-small: var(--sl-font-size-small); + --sl-input-font-size-medium: var(--sl-font-size-medium); + --sl-input-font-size-large: var(--sl-font-size-large); + --sl-input-letter-spacing: var(--sl-letter-spacing-normal); + + --sl-input-color: var(--sl-color-neutral-700); + --sl-input-color-hover: var(--sl-color-neutral-700); + --sl-input-color-focus: var(--sl-color-neutral-700); + --sl-input-color-disabled: var(--sl-color-neutral-900); + --sl-input-icon-color: var(--sl-color-neutral-500); + --sl-input-icon-color-hover: var(--sl-color-neutral-600); + --sl-input-icon-color-focus: var(--sl-color-neutral-600); + --sl-input-placeholder-color: var(--sl-color-neutral-500); + --sl-input-placeholder-color-disabled: var(--sl-color-neutral-600); + --sl-input-spacing-small: var(--sl-spacing-small); + --sl-input-spacing-medium: var(--sl-spacing-medium); + --sl-input-spacing-large: var(--sl-spacing-large); + + --sl-input-focus-ring-color: hsl(198.6 88.7% 48.4% / 40%); + --sl-input-focus-ring-offset: 0; + + --sl-input-filled-background-color: var(--sl-color-neutral-100); + --sl-input-filled-background-color-hover: var(--sl-color-neutral-100); + --sl-input-filled-background-color-focus: var(--sl-color-neutral-100); + --sl-input-filled-background-color-disabled: var(--sl-color-neutral-100); + --sl-input-filled-color: var(--sl-color-neutral-800); + --sl-input-filled-color-hover: var(--sl-color-neutral-800); + --sl-input-filled-color-focus: var(--sl-color-neutral-700); + --sl-input-filled-color-disabled: var(--sl-color-neutral-800); + + --sl-input-label-font-size-small: var(--sl-font-size-small); + --sl-input-label-font-size-medium: var(--sl-font-size-medium); + --sl-input-label-font-size-large: var(--sl-font-size-large); + --sl-input-label-color: inherit; + + --sl-input-help-text-font-size-small: var(--sl-font-size-x-small); + --sl-input-help-text-font-size-medium: var(--sl-font-size-small); + --sl-input-help-text-font-size-large: var(--sl-font-size-medium); + --sl-input-help-text-color: var(--sl-color-neutral-500); + + --sl-toggle-size-small: 0.875rem; + --sl-toggle-size-medium: 1.125rem; + --sl-toggle-size-large: 1.375rem; + + --sl-overlay-background-color: hsl(0 0% 0% / 43%); + + --sl-panel-background-color: var(--sl-color-neutral-50); + --sl-panel-border-color: var(--sl-color-neutral-200); + --sl-panel-border-width: 1px; + + --sl-tooltip-border-radius: var(--sl-border-radius-medium); + --sl-tooltip-background-color: var(--sl-color-neutral-800); + --sl-tooltip-color: var(--sl-color-neutral-0); + --sl-tooltip-font-family: var(--sl-font-sans); + --sl-tooltip-font-weight: var(--sl-font-weight-normal); + --sl-tooltip-font-size: var(--sl-font-size-small); + --sl-tooltip-line-height: var(--sl-line-height-dense); + --sl-tooltip-padding: var(--sl-spacing-2x-small) var(--sl-spacing-x-small); + --sl-tooltip-arrow-size: 6px; + + --sl-z-index-drawer: 700; + --sl-z-index-dialog: 800; + --sl-z-index-dropdown: 900; + --sl-z-index-toast: 950; + --sl-z-index-tooltip: 1000; +} + +@supports (scrollbar-gutter: stable) { + .sl-scroll-lock { + scrollbar-gutter: stable !important; + overflow: hidden !important; + } +} + +@supports not (scrollbar-gutter: stable) { + .sl-scroll-lock body { + padding-right: var(--sl-scroll-lock-size) !important; + overflow: hidden !important; + } +} + +.sl-toast-stack { + position: fixed; + top: 0; + inset-inline-end: 0; + z-index: var(--sl-z-index-toast); + width: 28rem; + max-width: 100%; + max-height: 100%; + overflow: auto; +} + +.sl-toast-stack sl-alert { + margin: var(--sl-spacing-medium); +} + +.sl-toast-stack sl-alert::part(base) { + box-shadow: var(--sl-shadow-large); +} diff --git a/src/theme/light.css b/src/theme/light.css new file mode 100644 index 0000000..531be12 --- /dev/null +++ b/src/theme/light.css @@ -0,0 +1,488 @@ +/** + * Adapted from + * @shoelace-style/shoelace/dist/themes/light.css + */ + +/* stylelint-disable */ + +:root, +:host, +.sl-theme-light { + color-scheme: light; + + --sl-color-gray-50: hsl(0 0% 97.5%); + --sl-color-gray-100: hsl(240 4.8% 95.9%); + --sl-color-gray-200: hsl(240 5.9% 90%); + --sl-color-gray-300: hsl(240 4.9% 83.9%); + --sl-color-gray-400: hsl(240 5% 64.9%); + --sl-color-gray-500: hsl(240 3.8% 46.1%); + --sl-color-gray-600: hsl(240 5.2% 33.9%); + --sl-color-gray-700: hsl(240 5.3% 26.1%); + --sl-color-gray-800: hsl(240 3.7% 15.9%); + --sl-color-gray-900: hsl(240 5.9% 10%); + --sl-color-gray-950: hsl(240 7.3% 8%); + + --sl-color-red-50: hsl(0 85.7% 97.3%); + --sl-color-red-100: hsl(0 93.3% 94.1%); + --sl-color-red-200: hsl(0 96.3% 89.4%); + --sl-color-red-300: hsl(0 93.5% 81.8%); + --sl-color-red-400: hsl(0 90.6% 70.8%); + --sl-color-red-500: hsl(0 84.2% 60.2%); + --sl-color-red-600: hsl(0 72.2% 50.6%); + --sl-color-red-700: hsl(0 73.7% 41.8%); + --sl-color-red-800: hsl(0 70% 35.3%); + --sl-color-red-900: hsl(0 62.8% 30.6%); + --sl-color-red-950: hsl(0 60% 19.6%); + + --sl-color-orange-50: rgb(254 248 245); + --sl-color-orange-100: rgb(253 232 221); + --sl-color-orange-200: rgb(251 215 195); + --sl-color-orange-300: rgb(250 195 165); + --sl-color-orange-400: rgb(247 167 123); + --sl-color-orange-500: rgb(244 128 64); + --sl-color-orange-600: rgb(216 96 29); + --sl-color-orange-700: rgb(178 79 24); + --sl-color-orange-800: rgb(147 65 20); + --sl-color-orange-900: rgb(105 46 14); + --sl-color-orange-950: rgb(65 29 9); + + --sl-color-amber-50: hsl(48 100% 96.1%); + --sl-color-amber-100: hsl(48 96.5% 88.8%); + --sl-color-amber-200: hsl(48 96.6% 76.7%); + --sl-color-amber-300: hsl(45.9 96.7% 64.5%); + --sl-color-amber-400: hsl(43.3 96.4% 56.3%); + --sl-color-amber-500: hsl(37.7 92.1% 50.2%); + --sl-color-amber-600: hsl(32.1 94.6% 43.7%); + --sl-color-amber-700: hsl(26 90.5% 37.1%); + --sl-color-amber-800: hsl(22.7 82.5% 31.4%); + --sl-color-amber-900: hsl(21.7 77.8% 26.5%); + --sl-color-amber-950: hsl(22.9 74.1% 16.7%); + + --sl-color-yellow-50: hsl(54.5 91.7% 95.3%); + --sl-color-yellow-100: hsl(54.9 96.7% 88%); + --sl-color-yellow-200: hsl(52.8 98.3% 76.9%); + --sl-color-yellow-300: hsl(50.4 97.8% 63.5%); + --sl-color-yellow-400: hsl(47.9 95.8% 53.1%); + --sl-color-yellow-500: hsl(45.4 93.4% 47.5%); + --sl-color-yellow-600: hsl(40.6 96.1% 40.4%); + --sl-color-yellow-700: hsl(35.5 91.7% 32.9%); + --sl-color-yellow-800: hsl(31.8 81% 28.8%); + --sl-color-yellow-900: hsl(28.4 72.5% 25.7%); + --sl-color-yellow-950: hsl(33.1 69% 13.9%); + + --sl-color-lime-50: hsl(78.3 92% 95.1%); + --sl-color-lime-100: hsl(79.6 89.1% 89.2%); + --sl-color-lime-200: hsl(80.9 88.5% 79.6%); + --sl-color-lime-300: hsl(82 84.5% 67.1%); + --sl-color-lime-400: hsl(82.7 78% 55.5%); + --sl-color-lime-500: hsl(83.7 80.5% 44.3%); + --sl-color-lime-600: hsl(84.8 85.2% 34.5%); + --sl-color-lime-700: hsl(85.9 78.4% 27.3%); + --sl-color-lime-800: hsl(86.3 69% 22.7%); + --sl-color-lime-900: hsl(87.6 61.2% 20.2%); + --sl-color-lime-950: hsl(86.5 60.6% 13.9%); + + --sl-color-green-50: hsl(138.5 76.5% 96.7%); + --sl-color-green-100: hsl(140.6 84.2% 92.5%); + --sl-color-green-200: hsl(141 78.9% 85.1%); + --sl-color-green-300: hsl(141.7 76.6% 73.1%); + --sl-color-green-400: hsl(141.9 69.2% 58%); + --sl-color-green-500: hsl(142.1 70.6% 45.3%); + --sl-color-green-600: hsl(142.1 76.2% 36.3%); + --sl-color-green-700: hsl(142.4 71.8% 29.2%); + --sl-color-green-800: hsl(142.8 64.2% 24.1%); + --sl-color-green-900: hsl(143.8 61.2% 20.2%); + --sl-color-green-950: hsl(144.3 60.7% 12%); + + --sl-color-emerald-50: hsl(151.8 81% 95.9%); + --sl-color-emerald-100: hsl(149.3 80.4% 90%); + --sl-color-emerald-200: hsl(152.4 76% 80.4%); + --sl-color-emerald-300: hsl(156.2 71.6% 66.9%); + --sl-color-emerald-400: hsl(158.1 64.4% 51.6%); + --sl-color-emerald-500: hsl(160.1 84.1% 39.4%); + --sl-color-emerald-600: hsl(161.4 93.5% 30.4%); + --sl-color-emerald-700: hsl(162.9 93.5% 24.3%); + --sl-color-emerald-800: hsl(163.1 88.1% 19.8%); + --sl-color-emerald-900: hsl(164.2 85.7% 16.5%); + --sl-color-emerald-950: hsl(164.3 87.5% 9.4%); + + --sl-color-teal-50: hsl(166.2 76.5% 96.7%); + --sl-color-teal-100: hsl(167.2 85.5% 89.2%); + --sl-color-teal-200: hsl(168.4 83.8% 78.2%); + --sl-color-teal-300: hsl(170.6 76.9% 64.3%); + --sl-color-teal-400: hsl(172.5 66% 50.4%); + --sl-color-teal-500: hsl(173.4 80.4% 40%); + --sl-color-teal-600: hsl(174.7 83.9% 31.6%); + --sl-color-teal-700: hsl(175.3 77.4% 26.1%); + --sl-color-teal-800: hsl(176.1 69.4% 21.8%); + --sl-color-teal-900: hsl(175.9 60.8% 19%); + --sl-color-teal-950: hsl(176.5 58.6% 11.4%); + + --sl-color-cyan-50: hsl(183.2 100% 96.3%); + --sl-color-cyan-100: hsl(185.1 95.9% 90.4%); + --sl-color-cyan-200: hsl(186.2 93.5% 81.8%); + --sl-color-cyan-300: hsl(187 92.4% 69%); + --sl-color-cyan-400: hsl(187.9 85.7% 53.3%); + --sl-color-cyan-500: hsl(188.7 94.5% 42.7%); + --sl-color-cyan-600: hsl(191.6 91.4% 36.5%); + --sl-color-cyan-700: hsl(192.9 82.3% 31%); + --sl-color-cyan-800: hsl(194.4 69.6% 27.1%); + --sl-color-cyan-900: hsl(196.4 63.6% 23.7%); + --sl-color-cyan-950: hsl(196.8 61% 16.1%); + + --sl-color-sky-50: hsl(204 100% 97.1%); + --sl-color-sky-100: hsl(204 93.8% 93.7%); + --sl-color-sky-200: hsl(200.6 94.4% 86.1%); + --sl-color-sky-300: hsl(199.4 95.5% 73.9%); + --sl-color-sky-400: hsl(198.4 93.2% 59.6%); + --sl-color-sky-500: hsl(198.6 88.7% 48.4%); + --sl-color-sky-600: hsl(200.4 98% 39.4%); + --sl-color-sky-700: hsl(201.3 96.3% 32.2%); + --sl-color-sky-800: hsl(201 90% 27.5%); + --sl-color-sky-900: hsl(202 80.3% 23.9%); + --sl-color-sky-950: hsl(202.3 73.8% 16.5%); + + --sl-color-blue-50: hsl(213.8 100% 96.9%); + --sl-color-blue-100: hsl(214.3 94.6% 92.7%); + --sl-color-blue-200: hsl(213.3 96.9% 87.3%); + --sl-color-blue-300: hsl(211.7 96.4% 78.4%); + --sl-color-blue-400: hsl(213.1 93.9% 67.8%); + --sl-color-blue-500: hsl(217.2 91.2% 59.8%); + --sl-color-blue-600: hsl(221.2 83.2% 53.3%); + --sl-color-blue-700: hsl(224.3 76.3% 48%); + --sl-color-blue-800: hsl(225.9 70.7% 40.2%); + --sl-color-blue-900: hsl(224.4 64.3% 32.9%); + --sl-color-blue-950: hsl(226.2 55.3% 18.4%); + + --sl-color-indigo-50: hsl(225.9 100% 96.7%); + --sl-color-indigo-100: hsl(226.5 100% 93.9%); + --sl-color-indigo-200: hsl(228 96.5% 88.8%); + --sl-color-indigo-300: hsl(229.7 93.5% 81.8%); + --sl-color-indigo-400: hsl(234.5 89.5% 73.9%); + --sl-color-indigo-500: hsl(238.7 83.5% 66.7%); + --sl-color-indigo-600: hsl(243.4 75.4% 58.6%); + --sl-color-indigo-700: hsl(244.5 57.9% 50.6%); + --sl-color-indigo-800: hsl(243.7 54.5% 41.4%); + --sl-color-indigo-900: hsl(242.2 47.4% 34.3%); + --sl-color-indigo-950: hsl(243.5 43.6% 22.9%); + + --sl-color-violet-50: hsl(250 100% 97.6%); + --sl-color-violet-100: hsl(251.4 91.3% 95.5%); + --sl-color-violet-200: hsl(250.5 95.2% 91.8%); + --sl-color-violet-300: hsl(252.5 94.7% 85.1%); + --sl-color-violet-400: hsl(255.1 91.7% 76.3%); + --sl-color-violet-500: hsl(258.3 89.5% 66.3%); + --sl-color-violet-600: hsl(262.1 83.3% 57.8%); + --sl-color-violet-700: hsl(263.4 70% 50.4%); + --sl-color-violet-800: hsl(263.4 69.3% 42.2%); + --sl-color-violet-900: hsl(263.5 67.4% 34.9%); + --sl-color-violet-950: hsl(265.1 61.5% 21.4%); + + --sl-color-purple-50: hsl(270 100% 98%); + --sl-color-purple-100: hsl(268.7 100% 95.5%); + --sl-color-purple-200: hsl(268.6 100% 91.8%); + --sl-color-purple-300: hsl(269.2 97.4% 85.1%); + --sl-color-purple-400: hsl(270 95.2% 75.3%); + --sl-color-purple-500: hsl(270.7 91% 65.1%); + --sl-color-purple-600: hsl(271.5 81.3% 55.9%); + --sl-color-purple-700: hsl(272.1 71.7% 47.1%); + --sl-color-purple-800: hsl(272.9 67.2% 39.4%); + --sl-color-purple-900: hsl(273.6 65.6% 32%); + --sl-color-purple-950: hsl(276 59.5% 16.5%); + + --sl-color-fuchsia-50: hsl(289.1 100% 97.8%); + --sl-color-fuchsia-100: hsl(287 100% 95.5%); + --sl-color-fuchsia-200: hsl(288.3 95.8% 90.6%); + --sl-color-fuchsia-300: hsl(291.1 93.1% 82.9%); + --sl-color-fuchsia-400: hsl(292 91.4% 72.5%); + --sl-color-fuchsia-500: hsl(292.2 84.1% 60.6%); + --sl-color-fuchsia-600: hsl(293.4 69.5% 48.8%); + --sl-color-fuchsia-700: hsl(294.7 72.4% 39.8%); + --sl-color-fuchsia-800: hsl(295.4 70.2% 32.9%); + --sl-color-fuchsia-900: hsl(296.7 63.6% 28%); + --sl-color-fuchsia-950: hsl(297.1 56.8% 14.5%); + + --sl-color-pink-50: hsl(327.3 73.3% 97.1%); + --sl-color-pink-100: hsl(325.7 77.8% 94.7%); + --sl-color-pink-200: hsl(325.9 84.6% 89.8%); + --sl-color-pink-300: hsl(327.4 87.1% 81.8%); + --sl-color-pink-400: hsl(328.6 85.5% 70.2%); + --sl-color-pink-500: hsl(330.4 81.2% 60.4%); + --sl-color-pink-600: hsl(333.3 71.4% 50.6%); + --sl-color-pink-700: hsl(335.1 77.6% 42%); + --sl-color-pink-800: hsl(335.8 74.4% 35.3%); + --sl-color-pink-900: hsl(335.9 69% 30.4%); + --sl-color-pink-950: hsl(336.2 65.4% 15.9%); + + --sl-color-rose-50: hsl(355.7 100% 97.3%); + --sl-color-rose-100: hsl(355.6 100% 94.7%); + --sl-color-rose-200: hsl(352.7 96.1% 90%); + --sl-color-rose-300: hsl(352.6 95.7% 81.8%); + --sl-color-rose-400: hsl(351.3 94.5% 71.4%); + --sl-color-rose-500: hsl(349.7 89.2% 60.2%); + --sl-color-rose-600: hsl(346.8 77.2% 49.8%); + --sl-color-rose-700: hsl(345.3 82.7% 40.8%); + --sl-color-rose-800: hsl(343.4 79.7% 34.7%); + --sl-color-rose-900: hsl(341.5 75.5% 30.4%); + --sl-color-rose-950: hsl(341.3 70.1% 17.1%); + + --sl-color-primary-50: var(--sl-color-orange-50); + --sl-color-primary-100: var(--sl-color-orange-100); + --sl-color-primary-200: var(--sl-color-orange-200); + --sl-color-primary-300: var(--sl-color-orange-300); + --sl-color-primary-400: var(--sl-color-orange-400); + --sl-color-primary-500: var(--sl-color-orange-500); + --sl-color-primary-600: var(--sl-color-orange-600); + --sl-color-primary-700: var(--sl-color-orange-700); + --sl-color-primary-800: var(--sl-color-orange-800); + --sl-color-primary-900: var(--sl-color-orange-900); + --sl-color-primary-950: var(--sl-color-orange-950); + + --sl-color-success-50: var(--sl-color-green-50); + --sl-color-success-100: var(--sl-color-green-100); + --sl-color-success-200: var(--sl-color-green-200); + --sl-color-success-300: var(--sl-color-green-300); + --sl-color-success-400: var(--sl-color-green-400); + --sl-color-success-500: var(--sl-color-green-500); + --sl-color-success-600: var(--sl-color-green-600); + --sl-color-success-700: var(--sl-color-green-700); + --sl-color-success-800: var(--sl-color-green-800); + --sl-color-success-900: var(--sl-color-green-900); + --sl-color-success-950: var(--sl-color-green-950); + + --sl-color-warning-50: var(--sl-color-amber-50); + --sl-color-warning-100: var(--sl-color-amber-100); + --sl-color-warning-200: var(--sl-color-amber-200); + --sl-color-warning-300: var(--sl-color-amber-300); + --sl-color-warning-400: var(--sl-color-amber-400); + --sl-color-warning-500: var(--sl-color-amber-500); + --sl-color-warning-600: var(--sl-color-amber-600); + --sl-color-warning-700: var(--sl-color-amber-700); + --sl-color-warning-800: var(--sl-color-amber-800); + --sl-color-warning-900: var(--sl-color-amber-900); + --sl-color-warning-950: var(--sl-color-amber-950); + + --sl-color-danger-50: var(--sl-color-red-50); + --sl-color-danger-100: var(--sl-color-red-100); + --sl-color-danger-200: var(--sl-color-red-200); + --sl-color-danger-300: var(--sl-color-red-300); + --sl-color-danger-400: var(--sl-color-red-400); + --sl-color-danger-500: var(--sl-color-red-500); + --sl-color-danger-600: var(--sl-color-red-600); + --sl-color-danger-700: var(--sl-color-red-700); + --sl-color-danger-800: var(--sl-color-red-800); + --sl-color-danger-900: var(--sl-color-red-900); + --sl-color-danger-950: var(--sl-color-red-950); + + --sl-color-neutral-50: var(--sl-color-gray-50); + --sl-color-neutral-100: var(--sl-color-gray-100); + --sl-color-neutral-200: var(--sl-color-gray-200); + --sl-color-neutral-300: var(--sl-color-gray-300); + --sl-color-neutral-400: var(--sl-color-gray-400); + --sl-color-neutral-500: var(--sl-color-gray-500); + --sl-color-neutral-600: var(--sl-color-gray-600); + --sl-color-neutral-700: var(--sl-color-gray-700); + --sl-color-neutral-800: var(--sl-color-gray-800); + --sl-color-neutral-900: var(--sl-color-gray-900); + --sl-color-neutral-950: var(--sl-color-gray-950); + + --sl-color-neutral-0: hsl(0, 0%, 100%); + --sl-color-neutral-1000: hsl(0, 0%, 0%); + + --sl-border-radius-small: 0.1875rem; + --sl-border-radius-medium: 0.25rem; + --sl-border-radius-large: 0.5rem; + --sl-border-radius-x-large: 1rem; + + --sl-border-radius-circle: 50%; + --sl-border-radius-pill: 9999px; + + --sl-shadow-x-small: 0 1px 2px hsl(240 3.8% 46.1% / 6%); + --sl-shadow-small: 0 1px 2px hsl(240 3.8% 46.1% / 12%); + --sl-shadow-medium: 0 2px 4px hsl(240 3.8% 46.1% / 12%); + --sl-shadow-large: 0 2px 8px hsl(240 3.8% 46.1% / 12%); + --sl-shadow-x-large: 0 4px 16px hsl(240 3.8% 46.1% / 12%); + + --sl-spacing-3x-small: 0.125rem; + --sl-spacing-2x-small: 0.25rem; + --sl-spacing-x-small: 0.5rem; + --sl-spacing-small: 0.75rem; + --sl-spacing-medium: 1rem; + --sl-spacing-large: 1.25rem; + --sl-spacing-x-large: 1.75rem; + --sl-spacing-2x-large: 2.25rem; + --sl-spacing-3x-large: 3rem; + --sl-spacing-4x-large: 4.5rem; + + --sl-transition-x-slow: 1000ms; + --sl-transition-slow: 500ms; + --sl-transition-medium: 250ms; + --sl-transition-fast: 150ms; + --sl-transition-x-fast: 50ms; + + --sl-font-mono: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace; + --sl-font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, + Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol'; + --sl-font-serif: Georgia, 'Times New Roman', serif; + + --sl-font-size-2x-small: 0.625rem; + --sl-font-size-x-small: 0.75rem; + --sl-font-size-small: 0.875rem; + --sl-font-size-medium: 1rem; + --sl-font-size-large: 1.25rem; + --sl-font-size-x-large: 1.5rem; + --sl-font-size-2x-large: 2.25rem; + --sl-font-size-3x-large: 3rem; + --sl-font-size-4x-large: 4.5rem; + + --sl-font-weight-light: 300; + --sl-font-weight-normal: 400; + --sl-font-weight-semibold: 500; + --sl-font-weight-bold: 700; + + --sl-letter-spacing-denser: -0.03em; + --sl-letter-spacing-dense: -0.015em; + --sl-letter-spacing-normal: normal; + --sl-letter-spacing-loose: 0.075em; + --sl-letter-spacing-looser: 0.15em; + + --sl-line-height-denser: 1; + --sl-line-height-dense: 1.4; + --sl-line-height-normal: 1.8; + --sl-line-height-loose: 2.2; + --sl-line-height-looser: 2.6; + + --sl-focus-ring-color: var(--sl-color-primary-600); + --sl-focus-ring-style: solid; + --sl-focus-ring-width: 3px; + --sl-focus-ring: var(--sl-focus-ring-style) var(--sl-focus-ring-width) + var(--sl-focus-ring-color); + --sl-focus-ring-offset: 1px; + + --sl-button-font-size-small: var(--sl-font-size-x-small); + --sl-button-font-size-medium: var(--sl-font-size-small); + --sl-button-font-size-large: var(--sl-font-size-medium); + + --sl-input-height-small: 1.875rem; + --sl-input-height-medium: 2.5rem; + --sl-input-height-large: 3.125rem; + + --sl-input-background-color: var(--sl-color-neutral-0); + --sl-input-background-color-hover: var(--sl-input-background-color); + --sl-input-background-color-focus: var(--sl-input-background-color); + --sl-input-background-color-disabled: var(--sl-color-neutral-100); + --sl-input-border-color: var(--sl-color-neutral-300); + --sl-input-border-color-hover: var(--sl-color-neutral-400); + --sl-input-border-color-focus: var(--sl-color-primary-500); + --sl-input-border-color-disabled: var(--sl-color-neutral-300); + --sl-input-border-width: 1px; + --sl-input-required-content: '*'; + --sl-input-required-content-offset: -2px; + --sl-input-required-content-color: var(--sl-input-label-color); + + --sl-input-border-radius-small: var(--sl-border-radius-medium); + --sl-input-border-radius-medium: var(--sl-border-radius-medium); + --sl-input-border-radius-large: var(--sl-border-radius-medium); + + --sl-input-font-family: var(--sl-font-sans); + --sl-input-font-weight: var(--sl-font-weight-normal); + --sl-input-font-size-small: var(--sl-font-size-small); + --sl-input-font-size-medium: var(--sl-font-size-medium); + --sl-input-font-size-large: var(--sl-font-size-large); + --sl-input-letter-spacing: var(--sl-letter-spacing-normal); + + --sl-input-color: var(--sl-color-neutral-700); + --sl-input-color-hover: var(--sl-color-neutral-700); + --sl-input-color-focus: var(--sl-color-neutral-700); + --sl-input-color-disabled: var(--sl-color-neutral-900); + --sl-input-icon-color: var(--sl-color-neutral-500); + --sl-input-icon-color-hover: var(--sl-color-neutral-600); + --sl-input-icon-color-focus: var(--sl-color-neutral-600); + --sl-input-placeholder-color: var(--sl-color-neutral-500); + --sl-input-placeholder-color-disabled: var(--sl-color-neutral-600); + --sl-input-spacing-small: var(--sl-spacing-small); + --sl-input-spacing-medium: var(--sl-spacing-medium); + --sl-input-spacing-large: var(--sl-spacing-large); + + --sl-input-focus-ring-color: hsl(198.6 88.7% 48.4% / 40%); + --sl-input-focus-ring-offset: 0; + + --sl-input-filled-background-color: var(--sl-color-neutral-100); + --sl-input-filled-background-color-hover: var(--sl-color-neutral-100); + --sl-input-filled-background-color-focus: var(--sl-color-neutral-100); + --sl-input-filled-background-color-disabled: var(--sl-color-neutral-100); + --sl-input-filled-color: var(--sl-color-neutral-800); + --sl-input-filled-color-hover: var(--sl-color-neutral-800); + --sl-input-filled-color-focus: var(--sl-color-neutral-700); + --sl-input-filled-color-disabled: var(--sl-color-neutral-800); + + --sl-input-label-font-size-small: var(--sl-font-size-small); + --sl-input-label-font-size-medium: var(--sl-font-size-medium); + --sl-input-label-font-size-large: var(--sl-font-size-large); + --sl-input-label-color: inherit; + + --sl-input-help-text-font-size-small: var(--sl-font-size-x-small); + --sl-input-help-text-font-size-medium: var(--sl-font-size-small); + --sl-input-help-text-font-size-large: var(--sl-font-size-medium); + --sl-input-help-text-color: var(--sl-color-neutral-500); + + --sl-toggle-size-small: 0.875rem; + --sl-toggle-size-medium: 1.125rem; + --sl-toggle-size-large: 1.375rem; + + --sl-overlay-background-color: hsl(240 3.8% 46.1% / 33%); + + --sl-panel-background-color: var(--sl-color-neutral-0); + --sl-panel-border-color: var(--sl-color-neutral-200); + --sl-panel-border-width: 1px; + + --sl-tooltip-border-radius: var(--sl-border-radius-medium); + --sl-tooltip-background-color: var(--sl-color-neutral-800); + --sl-tooltip-color: var(--sl-color-neutral-0); + --sl-tooltip-font-family: var(--sl-font-sans); + --sl-tooltip-font-weight: var(--sl-font-weight-normal); + --sl-tooltip-font-size: var(--sl-font-size-small); + --sl-tooltip-line-height: var(--sl-line-height-dense); + --sl-tooltip-padding: var(--sl-spacing-2x-small) var(--sl-spacing-x-small); + --sl-tooltip-arrow-size: 6px; + + --sl-z-index-drawer: 700; + --sl-z-index-dialog: 800; + --sl-z-index-dropdown: 900; + --sl-z-index-toast: 950; + --sl-z-index-tooltip: 1000; +} + +@supports (scrollbar-gutter: stable) { + .sl-scroll-lock { + scrollbar-gutter: stable !important; + overflow: hidden !important; + } +} + +@supports not (scrollbar-gutter: stable) { + .sl-scroll-lock body { + padding-right: var(--sl-scroll-lock-size) !important; + overflow: hidden !important; + } +} + +.sl-toast-stack { + position: fixed; + top: 0; + inset-inline-end: 0; + z-index: var(--sl-z-index-toast); + width: 28rem; + max-width: 100%; + max-height: 100%; + overflow: auto; +} + +.sl-toast-stack sl-alert { + margin: var(--sl-spacing-medium); +} + +.sl-toast-stack sl-alert::part(base) { + box-shadow: var(--sl-shadow-large); +} diff --git a/src/theme/shared/page.ts b/src/theme/shared/page.ts new file mode 100644 index 0000000..1eb582c --- /dev/null +++ b/src/theme/shared/page.ts @@ -0,0 +1,11 @@ +import { css } from 'lit' + +export const pageFlex = [ + css` + :host { + display: flex; + flex-direction: column; + gap: var(--sl-spacing-large); + } + `, +] diff --git a/src/theme/styles/link.ts b/src/theme/styles/link.ts new file mode 100644 index 0000000..090ca55 --- /dev/null +++ b/src/theme/styles/link.ts @@ -0,0 +1,14 @@ +import { css } from 'lit' + +export const linkStyles = css` + a, + a:link, + a:visited, + a:active { + color: var(--sl-color-blue-500); + } + + a:hover { + color: var(--sl-color-blue-800); + } +` diff --git a/src/theme/styles/sl-card.ts b/src/theme/styles/sl-card.ts new file mode 100644 index 0000000..981f8e6 --- /dev/null +++ b/src/theme/styles/sl-card.ts @@ -0,0 +1,17 @@ +import { css } from 'lit' + +export const slCardStyles = css` + sl-card { + position: relative; + z-index: 50; + width: 100%; + box-shadow: var(--sl-shadow-medium); + } + + sl-card h2 { + margin: 0; + font-size: var(--sl-font-size-2x-large); + font-weight: var(--sl-font-weight-normal); + color: var(--sl-color-primary-600); + } +` diff --git a/src/theme/styles/text.ts b/src/theme/styles/text.ts new file mode 100644 index 0000000..7d80301 --- /dev/null +++ b/src/theme/styles/text.ts @@ -0,0 +1,33 @@ +import { css } from 'lit' + +export const textStyles = css` + :host { + font-family: var(--sl-font-sans); + font-size: var(--sl-font-size-medium); + line-height: var(--sl-line-height-normal); + color: var(--sl-color-gray-900); + } + + h1, + h2, + h3, + h4, + h5, + h6 { + margin-top: 0; + font-weight: var(--sl-font-weight-normal); + color: var(--sl-color-primary-600); + } + + h1 { + font-size: var(--sl-font-size-2x-large); + } + + h2 { + font-size: var(--sl-font-size-x-large); + } + + h3 { + font-size: var(--sl-font-size-large); + } +` diff --git a/src/theme/theme.ts b/src/theme/theme.ts new file mode 100644 index 0000000..597df2b --- /dev/null +++ b/src/theme/theme.ts @@ -0,0 +1,7 @@ +import { CSSResult } from 'lit' + +import { linkStyles } from '@/theme/styles/link' +import { slCardStyles } from '@/theme/styles/sl-card' +import { textStyles } from '@/theme/styles/text' + +export const theme: CSSResult[] = [textStyles, linkStyles, slCardStyles] diff --git a/src/theme/utils/register-theme.ts b/src/theme/utils/register-theme.ts new file mode 100644 index 0000000..e4c845d --- /dev/null +++ b/src/theme/utils/register-theme.ts @@ -0,0 +1,107 @@ +import { themeSignal } from '@/modules/app/signals/theme-signal' + +import { createLogger } from '@/utils/logging/create-logger' + +const logger = createLogger('register-theme') + +const DARK_THEME_CLASS = 'sl-theme-dark' +const LIGHT_THEME_CLASS = 'sl-theme-light' +const LOCAL_STORAGE_KEY = 'theme' + +export enum Theme { + Light = 'light', + Dark = 'dark', + System = 'system', +} + +export const getTheme = (): Theme.Light | Theme.Dark => { + const htmlElement = document.querySelector('html') + return htmlElement?.classList.contains(LIGHT_THEME_CLASS) + ? Theme.Light + : Theme.Dark +} + +export const setTheme = (theme: Theme.Light | Theme.Dark): void => { + logger.debug(`setting theme to ${theme}`) + const toRemove = theme === Theme.Dark ? LIGHT_THEME_CLASS : DARK_THEME_CLASS + const toAdd = theme === Theme.Dark ? DARK_THEME_CLASS : LIGHT_THEME_CLASS + + const htmlElement = document.querySelector('html') + htmlElement?.classList.remove(toRemove) + htmlElement?.classList.add(toAdd) + themeSignal.set(theme) +} + +const parseTheme = (value: string): Theme => { + switch (value.toLowerCase()) { + case Theme.Light.toString(): + return Theme.Light + case Theme.Dark.toString(): + return Theme.Dark + default: + return Theme.System + } +} + +export const setLocalStorage = (value: Theme): void => { + localStorage.setItem(LOCAL_STORAGE_KEY, value) +} + +export const fromLocalStorage = (): Theme => { + const value = localStorage.getItem(LOCAL_STORAGE_KEY) + if (!value) { + logger.debug('theme not saved in localStorage, setting to "system"') + localStorage.setItem(LOCAL_STORAGE_KEY, Theme.System) + return Theme.System + } + + const parsed = parseTheme(value) + + logger.debug(`theme set in localStorage: "${parsed}"`) + return parsed +} + +const supportsMatchMedia = () => { + return typeof window.matchMedia !== 'undefined' +} + +const fromBrowser = (): Theme.Dark | Theme.Light => { + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches + const theme = prefersDark ? Theme.Dark : Theme.Light + logger.debug(`system preference: "${theme}"`) + return theme +} + +const watchForPreferenceChanges = () => { + logger.debug('watching for preference changes...') + window + .matchMedia('(prefers-color-scheme: dark)') + .addEventListener('change', (event) => { + const theme = event.matches ? Theme.Dark : Theme.Light + logger.debug(`theme preference changed to ${theme}, setting theme...`) + setTheme(theme) + return + }) +} + +export const registerTheme = (mode: 'watch' | 'once' = 'watch'): void => { + const valueFromLocalStorage = fromLocalStorage() + + if (valueFromLocalStorage !== Theme.System) { + setTheme(valueFromLocalStorage) + return + } + + if (!supportsMatchMedia) { + logger.debug(`browser doesn't support matchmedia`) + setTheme(Theme.Light) + return + } + + const valueFromBrowser = fromBrowser() + setTheme(valueFromBrowser) + + if (mode === 'watch') { + watchForPreferenceChanges() + } +} diff --git a/src/utils/array/chunks.ts b/src/utils/array/chunks.ts new file mode 100644 index 0000000..66e85c5 --- /dev/null +++ b/src/utils/array/chunks.ts @@ -0,0 +1,16 @@ +export const chunks = (size: number, arr: T[]): T[][] => { + const result = arr.reduce((acc, curr, index) => { + const mod = index % size + + if (mod === 0) { + return [...acc, [curr]] + } + + const last = acc[acc.length - 1] + const init = acc.slice(0, acc.length - 1) + + return [...init, [...last, curr]] + }, [] as T[][]) + + return result +} diff --git a/src/utils/array/group-by.ts b/src/utils/array/group-by.ts new file mode 100644 index 0000000..e53aeab --- /dev/null +++ b/src/utils/array/group-by.ts @@ -0,0 +1,35 @@ +type Key = string | number | symbol + +type GroupByFn = (obj: T) => Key + +type GroupByResult = Record + +export const groupBy = (fn: GroupByFn, arr: T[]): GroupByResult => { + const result: GroupByResult = {} + + for (const val of arr) { + const key = fn(val) + result[key] = val + } + + return result +} + +export const groupByMultiple = ( + fn: GroupByFn, + arr: T[] +): GroupByResult => { + const result: GroupByResult = {} + + for (const val of arr) { + const key = fn(val) + + if (!result[key]) { + result[key] = [] + } + + result[key].push(val) + } + + return result +} diff --git a/src/utils/array/unique-by.ts b/src/utils/array/unique-by.ts new file mode 100644 index 0000000..21c826c --- /dev/null +++ b/src/utils/array/unique-by.ts @@ -0,0 +1,16 @@ +export const uniqueBy = (fn: (obj: T) => string | number, arr: T[]): T[] => { + const dictionary: Record = {} + + const result = arr.filter((x) => { + const key = fn(x) + + if (dictionary[key]) { + return false + } + + dictionary[key] = true + return true + }) + + return result +} diff --git a/src/utils/fetch/handle-error.ts b/src/utils/fetch/handle-error.ts new file mode 100644 index 0000000..8d97699 --- /dev/null +++ b/src/utils/fetch/handle-error.ts @@ -0,0 +1,9 @@ +export const handleError = async (response: Response): Promise => { + const { url } = response + + if (!response.ok) { + const text = await response.text() + + throw new Error(`Fetch failed for ${url} with response ${text}`) + } +} diff --git a/src/utils/fetch/handle-response.ts b/src/utils/fetch/handle-response.ts new file mode 100644 index 0000000..7a88e86 --- /dev/null +++ b/src/utils/fetch/handle-response.ts @@ -0,0 +1,8 @@ +import { handleError } from './handle-error' + +// TODO: abort controller +export const handleResponse = async (response: Response): Promise => { + await handleError(response) + const json = (await response.json()) as unknown as T + return json +} diff --git a/src/utils/logging/create-logger.test.ts b/src/utils/logging/create-logger.test.ts new file mode 100644 index 0000000..11fa836 --- /dev/null +++ b/src/utils/logging/create-logger.test.ts @@ -0,0 +1,42 @@ +import { vi, test, expect } from 'vitest' + +import { createLoggerFactory } from '@/utils/logging/create-logger' +import { LogLevel } from '@/utils/logging/types' + +type Case = [LogLevel, 'debug' | 'info' | 'warn' | 'error', boolean] + +const cases: Case[] = [ + [LogLevel.DEBUG, 'debug', true], + [LogLevel.DEBUG, 'info', true], + [LogLevel.DEBUG, 'warn', true], + [LogLevel.DEBUG, 'error', true], + [LogLevel.INFO, 'debug', false], + [LogLevel.INFO, 'info', true], + [LogLevel.INFO, 'warn', true], + [LogLevel.INFO, 'error', true], + [LogLevel.WARN, 'debug', false], + [LogLevel.WARN, 'info', false], + [LogLevel.WARN, 'warn', true], + [LogLevel.WARN, 'error', true], + [LogLevel.ERROR, 'debug', false], + [LogLevel.ERROR, 'info', false], + [LogLevel.ERROR, 'warn', false], + [LogLevel.ERROR, 'error', true], +] + +test.each(cases)( + 'Log level %s calls %s = %s', + (logLevel, logFunction, shouldHaveBeenCalled) => { + const createLogger = createLoggerFactory(logLevel) + const logger = createLogger('test') + const loggerSpy = vi.spyOn(console, logFunction).mockImplementation(() => ({ + [logFunction]: () => undefined, + })) + + logger[logFunction]('test') + + shouldHaveBeenCalled + ? expect(loggerSpy).toHaveBeenCalled() + : expect(loggerSpy).not.toHaveBeenCalled() + } +) diff --git a/src/utils/logging/create-logger.ts b/src/utils/logging/create-logger.ts new file mode 100644 index 0000000..272dd8f --- /dev/null +++ b/src/utils/logging/create-logger.ts @@ -0,0 +1,31 @@ +import { config } from '@/config' + +import { LogFn, LogLevel, Logger } from '@/utils/logging//types' + +const noOp = () => undefined + +const logWith = + (logLevel: 'debug' | 'info' | 'warn' | 'error', prefix: string): LogFn => + (...data) => + // safe to ignore + // console log function parameter is defined as any[] + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + console[logLevel](`[${prefix}]`, ...data) + +export const createLoggerFactory = (logLevel: LogLevel): Logger => { + return (prefix) => { + const debug = logLevel === LogLevel.DEBUG ? logWith('debug', prefix) : noOp + const info = logLevel <= LogLevel.INFO ? logWith('info', prefix) : noOp + const warn = logLevel <= LogLevel.WARN ? logWith('warn', prefix) : noOp + const error = logWith('error', prefix) + + return { + debug, + info, + warn, + error, + } + } +} + +export const createLogger = createLoggerFactory(config.app.logLevel) diff --git a/src/utils/logging/parse-log-level.test.ts b/src/utils/logging/parse-log-level.test.ts new file mode 100644 index 0000000..fbd17a9 --- /dev/null +++ b/src/utils/logging/parse-log-level.test.ts @@ -0,0 +1,20 @@ +import { test, expect } from 'vitest' + +import { parseLogLevel } from '@/utils/logging/parse-log-level' +import { LogLevel } from '@/utils/logging/types' + +test.each([ + ['debug', LogLevel.DEBUG], + ['DeBuG', LogLevel.DEBUG], + ['info', LogLevel.INFO], + ['INFO', LogLevel.INFO], + ['warn', LogLevel.WARN], + ['WARN', LogLevel.WARN], + ['error', LogLevel.ERROR], + ['ERROR', LogLevel.ERROR], + ['FOO', LogLevel.WARN], + ['', LogLevel.WARN], +])('%s => Log Level %s', (input, expected) => { + const value = parseLogLevel(input) + expect(value).toBe(expected) +}) diff --git a/src/utils/logging/parse-log-level.ts b/src/utils/logging/parse-log-level.ts new file mode 100644 index 0000000..32c6505 --- /dev/null +++ b/src/utils/logging/parse-log-level.ts @@ -0,0 +1,16 @@ +import { LogLevel } from '@/utils/logging/types' + +export const parseLogLevel = (level: string | undefined): LogLevel => { + switch (level?.toLowerCase()) { + case 'debug': + return LogLevel.DEBUG + case 'info': + return LogLevel.INFO + case 'warn': + return LogLevel.WARN + case 'error': + return LogLevel.ERROR + default: + return LogLevel.WARN + } +} diff --git a/src/utils/logging/types.ts b/src/utils/logging/types.ts new file mode 100644 index 0000000..6b7bd95 --- /dev/null +++ b/src/utils/logging/types.ts @@ -0,0 +1,18 @@ +export enum LogLevel { + DEBUG, + INFO, + WARN, + ERROR, +} + +// safe to ignore +// console log function parameter is defined as any[] +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type LogFn = (...data: any[]) => void + +export type Logger = (prefix: string) => { + debug: LogFn + info: LogFn + warn: LogFn + error: LogFn +} diff --git a/src/utils/object/is-defined.ts b/src/utils/object/is-defined.ts new file mode 100644 index 0000000..e68a4cb --- /dev/null +++ b/src/utils/object/is-defined.ts @@ -0,0 +1,3 @@ +export const isDefined = (value: T | undefined | null): value is T => { + return typeof value !== 'undefined' && value !== null +} diff --git a/src/utils/promise/sleep.ts b/src/utils/promise/sleep.ts new file mode 100644 index 0000000..4da6ae3 --- /dev/null +++ b/src/utils/promise/sleep.ts @@ -0,0 +1,4 @@ +export const sleep = (ms: number) => + new Promise((resolve) => { + setTimeout(() => resolve(true), ms) + }) diff --git a/src/utils/rocket-chat/get-channel-urls.ts b/src/utils/rocket-chat/get-channel-urls.ts new file mode 100644 index 0000000..8a05bf3 --- /dev/null +++ b/src/utils/rocket-chat/get-channel-urls.ts @@ -0,0 +1,16 @@ +export const getChannelUrls = ( + baseUrl: string, + project: string +): { quote: string; pentest: string } => { + let baseName = project + + // old style project names + if (baseName.startsWith('off-') || baseName.startsWith('pen-')) { + baseName = baseName.slice(4) + } + + return { + quote: `${baseUrl}/group/off-${baseName}?layout=embedded`, + pentest: `${baseUrl}/group/pen-${baseName}?layout=embedded`, + } +} diff --git a/src/utils/signal/query/constants.ts b/src/utils/signal/query/constants.ts new file mode 100644 index 0000000..3d42030 --- /dev/null +++ b/src/utils/signal/query/constants.ts @@ -0,0 +1,7 @@ +import { QueryResult } from './types' + +export const DEFAULT_QUERY_RESULT: QueryResult = { + status: 'pending', + data: null, + error: null, +} diff --git a/src/utils/signal/query/create-query.ts b/src/utils/signal/query/create-query.ts new file mode 100644 index 0000000..a1170ee --- /dev/null +++ b/src/utils/signal/query/create-query.ts @@ -0,0 +1,99 @@ +import { signal as createSignal } from '@lit-labs/preact-signals' + +import { DEFAULT_QUERY_RESULT } from './constants' +import { Query, QueryOptions, QueryFetcher, QueryResult } from './types' + +export const createQuery = < + TResult, + TParams = undefined, + TIntermediate = TResult, +>( + fetcher: QueryFetcher, + options?: QueryOptions +): Query => { + const signal = createSignal>(DEFAULT_QUERY_RESULT) + + const setValue = ( + partial: Partial> + ): QueryResult => { + const result = { + ...signal.value, + ...partial, + } + signal.value = result + return result + } + + const setLoading = () => { + return setValue({ + status: 'loading', + error: null, + }) + } + + const setCompleted = (data: TResult) => { + return setValue({ + status: 'completed', + data, + }) + } + + const setError = (message: string) => { + return setValue({ + status: 'error', + error: message, + }) + } + + const fetch: QueryFetcher = async (...params) => { + try { + setLoading() + + // :( need to get this typescript inference stuff to work + // #TODO: Fix type inference + + if (options?.before) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any + options.before(signal.value.data, params as any, setValue) + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any + const data = await fetcher(...(params as any)) + + const result = options?.transform + ? options.transform(data, signal.value.data) + : (data as TResult) + + setCompleted(result) + + if (options?.after) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unnecessary-type-assertion + await options.after(params as any, result) + } + + return result + } catch (err) { + setError((err as Error).message) + throw err + } + } + + return { + get data() { + return signal.value.data + }, + get error() { + return signal.value.error + }, + get status() { + return signal.value.status + }, + fetch, + set: (current) => { + setValue({ + data: current, + }) + return signal.value + }, + } +} diff --git a/src/utils/signal/query/types.ts b/src/utils/signal/query/types.ts new file mode 100644 index 0000000..b987e0f --- /dev/null +++ b/src/utils/signal/query/types.ts @@ -0,0 +1,31 @@ +export type QueryStatus = 'pending' | 'loading' | 'completed' | 'error' + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type QueryResult = { + status: QueryStatus + data: TResult | null + error: string | null +} + +export type QueryFetcher = ( + ...params: TParams[] +) => Promise + +export type Query = { + status: QueryStatus + data: TResult | null + error: string | null + fetch: QueryFetcher + set: (current: TResult) => QueryResult +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type QueryOptions = { + before?: ( + cache: TResult | null, + params: TParams, + setValue: (partial: Partial>) => QueryResult + ) => TResult + after?: (params: TParams, result: TResult) => void | Promise + transform?: (result: TIntermediate, current: TResult | null) => TResult +} diff --git a/src/utils/string/capital-case-to-space-case.ts b/src/utils/string/capital-case-to-space-case.ts new file mode 100644 index 0000000..92ee140 --- /dev/null +++ b/src/utils/string/capital-case-to-space-case.ts @@ -0,0 +1,2 @@ +export const capitalCaseToSpaceCase = (str: string) => + str.replace(/([A-Z])/g, ' $1').trim() diff --git a/src/utils/string/capitalize.ts b/src/utils/string/capitalize.ts new file mode 100644 index 0000000..55a0374 --- /dev/null +++ b/src/utils/string/capitalize.ts @@ -0,0 +1,2 @@ +export const capitalize = (str: string) => + `${str[0].toUpperCase()}${str.slice(1)}` diff --git a/src/utils/string/ensure-string.ts b/src/utils/string/ensure-string.ts new file mode 100644 index 0000000..ed76db6 --- /dev/null +++ b/src/utils/string/ensure-string.ts @@ -0,0 +1,7 @@ +export const ensureString = (name: string, value?: string): string => { + if (!value) { + throw new Error(`${name} not defined`) + } + + return value +} diff --git a/src/utils/string/generate-password.ts b/src/utils/string/generate-password.ts new file mode 100644 index 0000000..e73a9bd --- /dev/null +++ b/src/utils/string/generate-password.ts @@ -0,0 +1,40 @@ +const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +const NUMBERS = '0123456789' +// not all special characters are supported in GL when masking +// https://git.staging.radical.sexy/help/ci/variables/index#mask-a-cicd-variable +const SYMBOLS = '@:.~-+/=' + +const PASSWORD_CHARACTERS = [ + ALPHABET.toUpperCase(), + ALPHABET.toLowerCase(), + NUMBERS, + SYMBOLS, +].join('') + +/** + * Generate password + * @param {number} length + * @returns string + */ +export const generatePassword = ( + length = 20, + characters = PASSWORD_CHARACTERS +) => { + let password = '' + + // Monte Carlo + while (password.length < length) { + const array = new Uint8Array(1) + const [randomInt] = crypto.getRandomValues(array) + + const char = characters[randomInt] + + if (!char) { + continue + } + + password += char + } + + return password +} diff --git a/src/utils/version/version-values.ts b/src/utils/version/version-values.ts new file mode 100644 index 0000000..88e22c1 --- /dev/null +++ b/src/utils/version/version-values.ts @@ -0,0 +1,49 @@ +import { config } from '@/config' + +export type VersionValue = { + text: string + href: string +} + +export const versionValues = () => { + const result: VersionValue[] = [] + + const { + app: { + version, + commit, + repositoryUrl, + publicReportIssueUrl, + internalReportIssueUrl, + }, + } = config + + if (repositoryUrl) { + result.push( + { + text: `version: ${version}`, + href: `${repositoryUrl}/releases/tag/v${version}`, + }, + { + text: `commit: ${commit}`, + href: `${repositoryUrl}/commit/${commit}`, + } + ) + } + + if (publicReportIssueUrl) { + result.push({ + text: 'report issue (public)', + href: publicReportIssueUrl, + }) + } + + if (internalReportIssueUrl) { + result.push({ + text: 'report issue (internal)', + href: internalReportIssueUrl, + }) + } + + return result +} diff --git a/style-dark-light.css b/style-dark-light.css deleted file mode 100644 index e74b39e..0000000 --- a/style-dark-light.css +++ /dev/null @@ -1,19 +0,0 @@ -@media (prefers-color-scheme: light) { - :root { - --ros-color-background: white; - --ros-color-menu: #FFF; - --ros-color-hover: #3D3D3D; - --ros-color-primary-text: black; - --ros-color-secondary-text: #3D3D3D; - } -} - -@media (prefers-color-scheme: dark) { - :root { - --ros-color-background: #181818; - --ros-color-menu: #212121; - --ros-color-hover: #3D3D3D; - --ros-color-primary-text: #FFF; - --ros-color-secondary-text: #AAA; - } -} diff --git a/stylelint.config.js b/stylelint.config.js new file mode 100644 index 0000000..f562f67 --- /dev/null +++ b/stylelint.config.js @@ -0,0 +1,10 @@ +/** @type {import('stylelint').Config} */ +export default { + extends: ['stylelint-config-standard', 'stylelint-config-recess-order'], + overrides: [ + { + files: ['*.ts'], + customSyntax: 'postcss-lit', + }, + ], +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c2d7c6a --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "plugins": [ + { + "name": "ts-lit-plugin" + } + ], + /* Library */ + "target": "ESNext", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": ["ES2021", "DOM", "DOM.Iterable", "WebWorker"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + + /* Paths */ + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "src", + "vite-env.d.ts", + "vite.config.ts", + "mock", + "commitlint.config.ts" + ] +} diff --git a/vite-env.d.ts b/vite-env.d.ts new file mode 100644 index 0000000..c1b5513 --- /dev/null +++ b/vite-env.d.ts @@ -0,0 +1,29 @@ +/// +/// + +declare global { + // eslint-disable-next-line @typescript-eslint/no-explicit-any, no-var + var pdfjsLib: any +} + +interface ImportMetaEnv { + readonly DEV_VITE_SERVER_CRT_PATH?: string + readonly DEV_VITE_SERVER_KEY_PATH?: string + readonly VITE_APP_URL: string + readonly VITE_LOG_LEVEL?: string + readonly VITE_GITLAB_CLIENT_ID: string + readonly VITE_GITLAB_AUTHORITY: string + readonly VITE_REPOSITORY_URL?: string + readonly VITE_PUBLIC_ISSUE_TRACKER?: string + readonly VITE_INTERNAL_ISSUE_TRACKER?: string + readonly VITE_SECRET_GITLAB_TOKEN?: string + readonly VITE_ROCKETCHAT_URL: string + readonly VITE_CODIMD_URL: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} + +declare const __APP_VERSION__: string +declare const __APP_COMMIT__: string diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..b217fab --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,90 @@ +import { execSync } from 'node:child_process' +import { readFileSync } from 'node:fs' + +import { defineConfig, loadEnv } from 'vite' +import { viteStaticCopy } from 'vite-plugin-static-copy' +import tsconfigPaths from 'vite-tsconfig-paths' + +const iconsPath = 'node_modules/@shoelace-style/shoelace/dist/assets/icons' + +const pdfWorker = 'node_modules/pdfjs-dist/build/pdf.worker.min.mjs' + +const PORT = 3443 + +const COMMIT_HASH = execSync('git rev-parse --short HEAD').toString() + +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +const { version } = JSON.parse(readFileSync('package.json', 'utf8')) + +// eslint-disable-next-line import/no-default-export +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd(), '') + + return { + base: '/', + resolve: { + alias: [ + { + find: /\/assets\/icons\/(.+)/, + replacement: `${iconsPath}/$1`, + }, + ], + }, + define: { + __APP_VERSION__: JSON.stringify(version), + __APP_COMMIT__: JSON.stringify(COMMIT_HASH), + }, + build: { + sourcemap: true, + assetsDir: 'dist', + target: ['esnext'], + cssMinify: false, + minify: false, + lib: false, + }, + preview: { + port: PORT, + }, + server: { + host: new URL(env.VITE_APP_URL).hostname, + port: PORT, + https: + mode === 'production' || mode === 'test' + ? undefined + : { + cert: readFileSync( + env.DEV_VITE_SERVER_CRT_PATH || '.internal/certs/crt.pem' + ), + key: readFileSync( + env.DEV_VITE_SERVER_KEY_PATH || '.internal/certs/key.pem' + ), + }, + headers: { + 'Content-Security-Policy': [ + // 'script-src self' prevents service worker unregister and token retrieval through iframes + // SHOULD BE SET IN PRODUCTION + // https://github.com/AxaFrance/oidc-client/blob/main/FAQ.md#good-security-practices--does-a-hacker-can-unregister-the-service-worker-and-retrieve-tokens-via-an-iframe- + // `script-src 'self'`, + // worker-src blob: is needed for local development + // SHOULD NOT BE SET IN PRODUCTION + `worker-src 'self' blob:`, + ].join(';'), + }, + }, + plugins: [ + tsconfigPaths(), + viteStaticCopy({ + targets: [ + { + src: iconsPath, + dest: 'assets', + }, + { + src: pdfWorker, + dest: 'assets', + }, + ], + }), + ], + } +})