余尚辉 5 mesi fa
commit
d0f0e3cb3e

+ 55 - 0
.commitlintrc.json

@@ -0,0 +1,55 @@
+{
+  "extends": ["@commitlint/config-conventional"],
+  "rules": {
+    "type-enum": [
+      2,
+      "always",
+      [
+        "feat",
+        "fix",
+        "docs",
+        "style",
+        "refactor",
+        "test",
+        "chore",
+        "revert",
+        "ci",
+        "types",
+        "perf",
+        "tmp",
+        "wip",
+        "update",
+        "build",
+        "release",
+        "scripts"
+      ]
+    ],
+    "type-case": [2, "always", "lower-case"],
+    "type-empty": [2, "never"],
+    "subject-empty": [2, "never"],
+    "subject-max-length": [2, "always", 100],
+    "header-max-length": [2, "always", 100],
+    "subject-min-length": [2, "always", 2],
+    "subject-full-stop": [2, "never", "."],
+    "subject-case": [
+      2,
+      "always",
+      [
+        "lower-case",
+        "upper-case",
+        "sentence-case",
+        "title-case",
+        "camel-case",
+        "pascal-case",
+        "kebab-case",
+        "snake-case",
+        "start-case"
+      ]
+    ]
+  },
+  "parserPreset": {
+    "headerPattern": "/^(\\u3010([\\u4e00-\\u9fa5\\w]+)\\u3011)?\\s*(\\w+)(?:\\((.*)\\))?:?\\s*(.*)$/",
+    "headerCorrespondence": ["type", "scope", "subject", "ticket"],
+    "issuePrefixes": ["#"]
+  }
+}

+ 19 - 0
.eslintrc.json

@@ -0,0 +1,19 @@
+{
+  "root": true,
+  "env": {
+    "browser": true,
+    "node": true,
+    "es6": true
+  },
+  "extends": [
+    "eslint:recommended",
+    "plugin:@typescript-eslint/recommended",
+    "plugin:prettier/recommended"
+  ],
+  "parserOptions": {
+    "parser": "@typescript-eslint/parser",
+    "ecmaVersion": 2020
+  },
+  "ignorePatterns": ["dist/", "cdn/", "node_modules/", "src/eventemitter.d.ts"],
+  "rules": {}
+}

+ 27 - 0
.gitignore

@@ -0,0 +1,27 @@
+# Editor directories and files
+.DS_Store
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw*
+
+# Contents
+dist
+cdn
+node_modules
+.gitlab
+
+# Local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Lock files
+package-lock.json

+ 4 - 0
.husky/commit-msg

@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+. "$(dirname -- "$0")/_/husky.sh"
+
+npx --no -- commitlint --edit 

+ 4 - 0
.husky/pre-commit

@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+. "$(dirname -- "$0")/_/husky.sh"
+
+npm run lint:lint-staged

+ 3 - 0
.prettierignore

@@ -0,0 +1,3 @@
+dist
+cdn
+node_modules

+ 11 - 0
.prettierrc.json

@@ -0,0 +1,11 @@
+{
+  "printWidth": 80,
+  "semi": false,
+  "vueIndentScriptAndStyle": true,
+  "singleQuote": true,
+  "trailingComma": "none",
+  "arrowParens": "avoid",
+  "proseWrap": "never",
+  "htmlWhitespaceSensitivity": "strict",
+  "endOfLine": "auto"
+}

+ 83 - 0
CHANGELOG.md

@@ -0,0 +1,83 @@
+## [1.0.9](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.9) (2024-01-02)
+
+### Bug Fixes
+
+- 修复没有走进只能调一次拨打方法的问题 ([5657052](https://git.shuiditech.com/platformfe/package/sd-cti/commits/5657052239195d777f8c46ff6b97b03570e4e4ed))
+- 修复异常导致sdk不可用时调用uninit方法报错的问题 ([5c30eb1](https://git.shuiditech.com/platformfe/package/sd-cti/commits/5c30eb19f653ae7f589ff08da5f3604726f93b7a))
+
+## [1.0.8](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.8) (2023-12-07)
+
+### Bug Fixes
+
+- 挂断事件推送bug修复 ([a5323d7](https://git.shuiditech.com/platformfe/package/sd-cti/commits/a5323d70d06af9961ba16f834a6a610c5043dd55))
+- 修复listen方法名错误定义 ([e9cb8cf](https://git.shuiditech.com/platformfe/package/sd-cti/commits/e9cb8cf74fa808a10f6e1b8014dc60de4581bedd))
+
+### Features
+
+- 当前未通话状态下调用挂断不给提示只上报埋点,manual-hang接口没有flowId不调用并上报埋点 ([df718ff](https://git.shuiditech.com/platformfe/package/sd-cti/commits/df718ff74384110a1f91bd50c24a2f10126ec78e))
+
+## [1.0.7](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.7) (2023-11-22)
+
+### Features
+
+- 基于sip.js最佳实践优化代码;增加平滑关闭UA;设置通话状态机;挂断逻辑优化 ([d08b8f4](https://git.shuiditech.com/platformfe/package/sd-cti/commits/d08b8f4139ddee0b82ab2c6bc188a66441ed84af))
+
+## [1.0.6](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.6) (2023-10-19)
+
+### Bug Fixes
+
+- 只有在人工外呼场景下才调用turnHang方法 ([2432bc2](https://git.shuiditech.com/platformfe/package/sd-cti/commits/2432bc22d39b188abab60c56ae1bd169573e7588))
+
+## [1.0.5](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.5) (2023-10-16)
+
+### Features
+
+- 增加重试机制 ([390239c](https://git.shuiditech.com/platformfe/package/sd-cti/commit/390239cc2c56acd132edda3ae6f49ae740375e89))
+
+## [1.0.4](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.4) (2023-09-19)
+
+### Bug Fixes
+
+- 修复log日志上报数据格式问题 ([996e4c](https://git.shuiditech.com/platformfe/package/sd-cti/commits/996e4c8cf573becd703096c3745b4548081a1b5f))
+
+## [1.0.3](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.3) (2023-09-18)
+
+### Bug Fixes
+
+- 修改socket断连事件上报&获取媒体状态异常初始化方法修正 ([2d46e91](https://git.shuiditech.com/platformfe/package/sd-cti/commits/2d46e91f5d4d50c0e4dec2adbdbb6797b0bd5453))
+
+## [1.0.2](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.2) (2023-09-14)
+
+### Features
+
+- 接口定义指定类型特定场景必填&拆分getInstance和init&getInstance增加必填参数校验修饰器 ([d4711fc](https://git.shuiditech.com/platformfe/package/sd-cti/commits/d4711fc7f9c223c18516603dc18eff43d652272a))
+- 增加错误状态码注释 ([bf6d3cb](https://git.shuiditech.com/platformfe/package/sd-cti/commits/bf6d3cbab478d5d7216a185ffaa157d941d5a69d))
+
+## [1.0.1](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.1) (2023-09-13)
+
+### Features
+
+- 接口定义指定类型特定场景必填&拆分getInstance和init&getInstance增加必填参数校验修饰器 ([f83c76f](https://git.shuiditech.com/platformfe/package/sd-cti/commits/f83c76f23ab50e867d34dec086822c1f7e0114ea))
+
+# [1.0.0](https://git.shuiditech.com/platformfe/package/sd-cti/compare/v1.0.0-beta.6...v1.0.0) (2023-09-12)
+
+### Bug Fixes
+
+- socket & sip error server track emit name ([0415b57](https://git.shuiditech.com/platformfe/package/sd-cti/commits/0415b571689bba80619b8a9b12ef276e700ad045))
+
+### Features
+
+- add sip heartbeat ([7fe2ea9](https://git.shuiditech.com/platformfe/package/sd-cti/commits/7fe2ea9fb467d995dba30853eaf340b1aee90850))
+- cdn 只输出 umd 格式 ([e5332eb](https://git.shuiditech.com/platformfe/package/sd-cti/commits/e5332ebe400a4b6b2a8ff227bead4b643683fcaf))
+- npm 和 cdn 分别打包, cdn 版本文件名称带版本号 ([c37bf8d](https://git.shuiditech.com/platformfe/package/sd-cti/commits/c37bf8d47c98102f99f2bc1a7868bc338abbb904))
+- set nodejs version ([adfd8da](https://git.shuiditech.com/platformfe/package/sd-cti/commits/adfd8dae25135b383127d90728f24e4a0566c5c3))
+- update rollup ([62da63e](https://git.shuiditech.com/platformfe/package/sd-cti/commits/62da63e046a9b0c50a7edfbca62cf86d4d59a2d1))
+- update sip heartbeat error code ([d45ba20](https://git.shuiditech.com/platformfe/package/sd-cti/commits/d45ba20016f81bad7114da6d25422dad33caf46f))
+- update ts config ([0bb42e7](https://git.shuiditech.com/platformfe/package/sd-cti/commits/0bb42e79f9e35b8ad95a00d5e85949f16969fc05))
+- update ts config && update packages && add zt module ([41679f1](https://git.shuiditech.com/platformfe/package/sd-cti/commits/41679f1d08019624d359689a0097c19ed0c78a19))
+- update-rollup-package ([069f47b](https://git.shuiditech.com/platformfe/package/sd-cti/commits/069f47bdc96d8405bf29bf300c2c8e8af703fdd3))
+- 上报埋点区分 env & 本地开发不上报服务端埋点 & 优化全局配置项 ([2c30155](https://git.shuiditech.com/platformfe/package/sd-cti/commits/2c30155f4d8f28d7c6c4f017be708a6355f5d4a6))
+- 优化 socket sip cti 状态流转函数 ([ec0fb1c](https://git.shuiditech.com/platformfe/package/sd-cti/commits/ec0fb1cfd5fbbbf4f5a3dffbe606dcb513038772))
+- 如果是被踢出的情况,只关闭 im socket 和 sip socket 的连接,不注销 sip ([4e2ec29](https://git.shuiditech.com/platformfe/package/sd-cti/commits/4e2ec29474f7172f31e484fe3d2d3a91ecd99f36))
+- 对业务抛出的 emit 增加 try catch ([855f387](https://git.shuiditech.com/platformfe/package/sd-cti/commits/855f387f438b9675f62cabf74bbac182acd5203f))
+- 精细化 sip status 状态流转报错, 状态流转中断时上报状态流转列表 ([34b2749](https://git.shuiditech.com/platformfe/package/sd-cti/commits/34b27492cfb29ab3ee9c2eaff4328f4bcbc4e4bf))

+ 8 - 0
README.md

@@ -0,0 +1,8 @@
+# SdCTI 基本介绍
+
+CTI 全称为 Computer Telephony Integration(计算机电话集成技术),红杉 CTI SDK 支持手动外呼、机器人外呼等多种使用场景。
+
+# 使用文档
+
+# SDK 设计方案
+

+ 6 - 0
babel.config.cjs

@@ -0,0 +1,6 @@
+module.exports = {
+  presets: [
+    ['@babel/preset-env', { modules: 'commonjs' }],
+    '@babel/preset-typescript'
+  ]
+}

+ 136 - 0
package.json

@@ -0,0 +1,136 @@
+{
+  "name": "hs-cti",
+  "version": "1.0.9",
+  "description": "红杉外呼",
+  "main": "dist/hs-cti.es6.umd.js",
+  "module": "dist/hs-cti.es6.esm.js",
+  "types": "dist/hs-cti.d.ts",
+  "exports": {
+    "./zt": {
+      "types": "./dist/cs-cti.d.ts",
+      "node": {
+        "import": {
+          "production": "./dist/cs-cti.es6.esm.prod.js",
+          "development": "./dist/cs-cti.es6.esm.js",
+          "default": "./dist/cs-cti.es6.esm.js"
+        },
+        "require": {
+          "production": "./dist/cs-cti.es6.umd.prod.js",
+          "development": "./dist/cs-cti.es6.umd.js",
+          "default": "./dist/cs-cti.es6.umd.js"
+        }
+      },
+      "import": "./dist/cs-cti.es6.esm.js",
+      "require": "./dist/cs-cti.es6.umd.js"
+    },
+    ".": {
+      "types": "./dist/hs-cti.d.ts",
+      "node": {
+        "import": {
+          "production": "./dist/hs-cti.es6.esm.prod.js",
+          "development": "./dist/hs-cti.es6.esm.js",
+          "default": "./dist/hs-cti.es6.esm.js"
+        },
+        "require": {
+          "production": "./dist/hs-cti.es6.umd.prod.js",
+          "development": "./dist/hs-cti.es6.umd.js",
+          "default": "./dist/hs-cti.es6.umd.js"
+        }
+      },
+      "import": "./dist/hs-cti.es6.esm.js",
+      "require": "./dist/hs-cti.es6.umd.js"
+    }
+  },
+  "files": [
+    "dist/"
+  ],
+  "type": "module",
+  "scripts": {
+    "dev": "rollup -c -w",
+    "test": "jest --config ./jest.config.ts",
+    "build": "rimraf dist && rollup -c",
+    "build:map": "rimraf dist && rollup -c --environment sourcemap:true",
+    "build:hs": "rimraf dist && rollup -c --environment model:hs",
+    "build:zt": "rimraf dist && rollup -c --environment model:zt",
+    "release": "node scripts/release.js",
+    "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
+    "lint:eslint": "eslint --max-warnings 0  \"src/**/*.{vue,ts,tsx,js,json,cjs,mjs,cts,mts}\" --fix",
+    "lint:stylelint": "stylelint \"**/*.{vue,less,postcss,css,scss}\" --fix",
+    "lint:lint-staged": "lint-staged",
+    "prepare": "husky install"
+  },
+  "repository": {
+    "type": "git",
+    "url": ""
+  },
+  "keywords": [
+    "hs",
+    "call",
+    "cti",
+    "sip"
+  ],
+  "author": "platformfe",
+  "license": "ISC",
+  "dependencies": {
+    "sip.js": "^0.21.2",
+    "socket.io-client": "2"
+  },
+  "devDependencies": {
+    "@babel/core": "^7.22.11",
+    "@babel/preset-env": "^7.22.10",
+    "@babel/preset-typescript": "^7.22.15",
+    "@commitlint/cli": "^17.7.1",
+    "@commitlint/config-conventional": "^17.7.0",
+    "@rollup/plugin-babel": "^6.0.3",
+    "@rollup/plugin-commonjs": "^25.0.4",
+    "@rollup/plugin-json": "^6.0.0",
+    "@rollup/plugin-node-resolve": "^15.2.1",
+    "@rollup/plugin-terser": "^0.4.3",
+    "@rollup/plugin-typescript": "^11.1.3",
+    "@types/node": "^20.5.7",
+    "@types/socket.io-client": "1.4.36",
+    "@typescript-eslint/eslint-plugin": "^6.4.1",
+    "@typescript-eslint/parser": "^6.4.1",
+    "@vue/eslint-config-prettier": "^8.0.0",
+    "@vue/eslint-config-typescript": "^11.0.3",
+    "conventional-changelog-cli": "^2.2.2",
+    "enquirer": "^2.4.1",
+    "eslint": "^8.48.0",
+    "eslint-config-prettier": "^9.0.0",
+    "eslint-plugin-prettier": "^5.0.0",
+    "husky": "^8.0.3",
+    "jsdom": "^22.1.0",
+    "lint-staged": "^14.0.1",
+    "prettier": "^3.0.2",
+    "rimraf": "^5.0.1",
+    "rollup": "^3.28.1",
+    "rollup-plugin-auto-external": "^2.0.0",
+    "rollup-plugin-dts": "^5.3.1",
+    "rollup-plugin-serve": "^1.1.1",
+    "ts-node": "^10.9.1",
+    "tslib": "^2.6.2",
+    "typescript": "5.1.6"
+  },
+  "lint-staged": {
+    "*.{js,jsx,ts,tsx,json}": [
+      "eslint --fix",
+      "prettier --write"
+    ],
+    "*.vue": [
+      "eslint --fix",
+      "prettier --write"
+    ],
+    "*.{scss,less,html}": [
+      "stylelint --fix",
+      "prettier --write"
+    ],
+    "*.md": [
+      "prettier --write"
+    ]
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ]
+}

+ 157 - 0
rollup.config.js

@@ -0,0 +1,157 @@
+import serve from 'rollup-plugin-serve'
+import typescript from '@rollup/plugin-typescript'
+import resolve from '@rollup/plugin-node-resolve'
+import commonjs from '@rollup/plugin-commonjs'
+import autoExternal from 'rollup-plugin-auto-external'
+import terser from '@rollup/plugin-terser'
+import babel from '@rollup/plugin-babel'
+import dts from 'rollup-plugin-dts'
+import json from './package.json' assert { type: 'json' }
+import { execSync } from 'child_process'
+
+// 需要 build 的目录
+const model = process.env.model
+
+function executeCommand(command) {
+  return execSync(command, { encoding: 'utf-8' }).trim()
+}
+// sdk 的版本号
+const version = json.version
+// sdk build 时间戳
+const newDate = new Date()
+
+// build 时的 git 信息
+const currentBranch = executeCommand('git rev-parse --abbrev-ref HEAD')
+const latestCommitID = executeCommand('git rev-parse HEAD')
+const latestCommitMessage = executeCommand('git log -1 --pretty=%B')
+
+// sdk 的头部信息
+const getBannerText = (module, standard) => {
+  return `/*! 
+- Name ${module.toUpperCase()}_CTI
+- FileName ${module}-cti
+- Version ${version} 
+- JS Standard ${standard}
+- Author ${json.author}
+- Built on ${newDate.toLocaleDateString()} ${newDate.toLocaleTimeString()}
+- GitHub ${json.repository.url}
+- Branch ${currentBranch}
+- CommitID ${latestCommitID}
+- CommitMessage ${latestCommitMessage}
+*/`
+}
+// 获取输出配置
+const getCommonOutput = (module, format, standard, terser) => {
+  return {
+    format,
+    name: `${module.toUpperCase()}_CTI`,
+    globals: {
+      'socket.io-client': 'io',
+      'sip.js': 'SIP'
+    },
+    exports: 'named',
+    file: `dist/${module}-cti.${standard}.${format}.${terser ? 'prod.' : ''}js`,
+    banner: getBannerText(module, standard),
+    sourcemap: !!process.env.sourcemap
+  }
+}
+// 获取插件配置
+const getCommonPlugin = standard => {
+  return [
+    resolve(),
+    commonjs(),
+    autoExternal(),
+    typescript({
+      tsconfig: `tsconfig.${standard}.json`
+    }),
+    babel({
+      extensions: ['.js', '.ts'],
+      babelHelpers: 'bundled',
+      presets: ['@babel/preset-env']
+    })
+  ]
+}
+// 需要生成的格式配置
+const configArr = [
+  { format: 'umd', standard: 'es5' },
+  { format: 'umd', standard: 'es6' },
+  { format: 'esm', standard: 'es5' },
+  { format: 'esm', standard: 'es6' }
+]
+
+// 后续增加其他的需手动增加
+const buildModuleList = ['hs']
+
+const generateExport = () => {
+  // 如果指定了需要打包的模块,则单独打包
+  if (model) {
+    return buildModel(model)
+  } else {
+    const exportAll = []
+    // 若未指定,则打包所有的模块
+    buildModuleList.forEach(module => {
+      exportAll.push(...buildModel(module))
+    })
+    return exportAll
+  }
+}
+
+function buildModel(module) {
+  const exportArr = []
+  // 类型声明文件
+  exportArr.push({
+    input: `src/${module}-cti/index.dts.ts`,
+    plugins: [dts()],
+    output: {
+      banner: getBannerText(module, 'any'),
+      file: `dist/${module}-cti.d.ts`,
+      format: 'es'
+    }
+  })
+  // 统一输出 js 模块的入口文件
+  const input = `src/${module}-cti/index.ts`
+  // 分别生成不混淆和混淆两版
+  configArr.forEach(item => {
+    exportArr.push(
+      {
+        input,
+        output: getCommonOutput(module, item.format, item.standard, false),
+        plugins: getCommonPlugin(item.standard)
+      },
+      {
+        input,
+        output: getCommonOutput(module, item.format, item.standard, true),
+        plugins: [...getCommonPlugin(item.standard), terser()]
+      }
+    )
+  })
+  return exportArr
+}
+// export default generateExport()
+
+export default [
+  ...generateExport(),
+  {
+    input: 'src/cs-cti/index.ts', // 指定一个任意入口,防止报错,可以是一个空的文件
+    output: {
+      dir: 'dist', // 输出目录,避免报错
+      format: 'esm', // 输出格式为 ES 模块
+      sourcemap: true // 开启 sourcemap 以便调试
+    },
+    plugins: [
+      resolve(), // 解析 node_modules 中的依赖
+      commonjs(), // 允许加载 CommonJS 模块
+      typescript(), // 处理 TypeScript 文件
+      serve({
+        open: true, // 自动打开浏览器
+        contentBase: ['dist', 'src'], // 包含 dist 和 src 作为静态资源的根目录
+        host: '0.0.0.0', // 可以通过局域网访问
+        port: 8099, // 服务器运行端口
+        openPage: '/sdk.html' // 直接打开的页面路径,src/sdk.html
+      })
+    ],
+    watch: {
+      include: 'src/**' // 监视 src 文件夹中的文件变动,但不自动打包
+    }
+  }
+]

+ 153 - 0
scripts/release.js

@@ -0,0 +1,153 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+// @ts-check
+import fs from 'node:fs'
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+import { createRequire } from 'node:module'
+import { execSync } from 'node:child_process'
+import enquirer from 'enquirer'
+import semver from 'semver'
+import minimist from 'minimist'
+import chalk from 'chalk'
+
+const step = msg => console.log(chalk.cyan(msg))
+
+// 获取当前目录
+const __dirname = path.dirname(fileURLToPath(import.meta.url))
+
+const { prompt } = enquirer
+const require = createRequire(import.meta.url)
+
+// 获取当前版本
+const currentVersion = require('../package.json').version
+const args = minimist(process.argv.slice(2))
+const preId = args.preid || semver.prerelease(currentVersion)?.[0]
+
+// 如果已经定义的版本号直接跳过
+let targetVersion = args._[0]
+
+// 如果未定义版本号,则生成推测版本号选项
+if (!targetVersion) {
+  const versionIncrements = [
+    'patch',
+    'minor',
+    'major',
+    ...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : [])
+  ]
+  const inc = i => semver.inc(currentVersion, i, preId)
+
+  // @ts-ignore
+  const { release } = await prompt({
+    type: 'select',
+    name: 'release',
+    message: '请选择 release type',
+    choices: versionIncrements.map(i => `${i} (${inc(i)})`).concat(['custom'])
+  })
+
+  // 目标版本号
+  if (release === 'custom') {
+    const result = await prompt({
+      type: 'input',
+      name: 'version',
+      message: '请自定义你的想要 release 的版本号',
+      initial: currentVersion
+    })
+    // @ts-ignore
+    targetVersion = result.version
+  } else {
+    targetVersion = release.match(/\((.*)\)/)[1]
+  }
+}
+
+// 再次确认版本号是否正确
+// @ts-ignore
+const { yes: confirmRelease } = await prompt({
+  type: 'confirm',
+  name: 'yes',
+  message: `你确认 release 版本号 v${targetVersion} 吗?`
+})
+
+if (confirmRelease) {
+  await updateVersions(targetVersion)
+  // 仅正式版本号生成 changelog
+  if (/^\d+(\.\d+)*$/.test(targetVersion)) {
+    await generateChangelog()
+  } else {
+    await pushCommit()
+  }
+} else {
+  console.warn(
+    `${chalk.bgYellow.white(' WARN ')} ${chalk.yellow(
+      `你已经取消了 v${targetVersion} 版本的 release`
+    )}`
+  )
+}
+
+function updateVersions(version) {
+  step('\nUpdate Versions...')
+  // 更新根目录
+  updatePackage(path.resolve(__dirname, '..'), version)
+}
+
+// 更新 package.json 的 version 字段
+function updatePackage(pkgRoot, version) {
+  const pkgPath = path.resolve(pkgRoot, 'package.json')
+  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
+  pkg.version = version
+  fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
+  step(`\nUpdate Versions Success ${pkg.name} ${version}`)
+}
+
+// 生成更新日志
+async function generateChangelog() {
+  step('\nGenerating Changelog...')
+  await execSync('yarn changelog')
+
+  // @ts-ignore
+  const { yes: changelogOk } = await prompt({
+    type: 'confirm',
+    name: 'yes',
+    message: `Changelog 已经生成完毕,请你检查是否 OK?`
+  })
+
+  if (changelogOk) {
+    step('\nGenerating Changelog Success')
+    await pushCommit()
+  } else {
+    console.warn(
+      `${chalk.bgYellow.white(' WARN ')} ${chalk.yellow(
+        `你已经取消了 v${targetVersion} 版本的代码推送`
+      )}`
+    )
+  }
+}
+
+// 推送 release 的版本号
+function pushCommit() {
+  step('\nPush Commit...')
+  const releaseMsg = `release(version): v${targetVersion}`
+  try {
+    execSync('git add .')
+    execSync(`git commit -m '${releaseMsg}'`)
+    execSync('git push')
+    console.log(
+      `${chalk.bgGreen.white(' SUCCESS ')} ${chalk.green(
+        `你成功推送了 v${targetVersion} 版本的 release 到远程仓库`
+      )}`
+    )
+  } catch (error) {
+    if (String(error).includes('No staged files found')) {
+      console.error(
+        `${chalk.bgRed.white(' ERROR ')} ${chalk.red(
+          `版本号重复且 SDK 内容没有变更!`
+        )}`
+      )
+    } else {
+      console.error(
+        `${chalk.bgRed.white(' ERROR ')} ${chalk.red(
+          `推送 release 的版本号时发生错误: ${error.message}`
+        )}`
+      )
+    }
+  }
+}

+ 92 - 0
src/Logger.ts

@@ -0,0 +1,92 @@
+import { getDateTime } from './utils'
+
+/** @enum LoggerLevels 日志输出等级 */
+export enum LoggerLevels {
+  error,
+  warn,
+  log,
+  debug
+}
+
+declare global {
+  interface Window {
+    ctiLoggerLevel?: LoggerLevels
+    HS_CTI_CLIENT_ID?: string
+  }
+}
+/** @class Logger 通用的日志模块 */
+export class Logger {
+  private _level: LoggerLevels
+  private category: string
+  private label: string | undefined
+  constructor(level: LoggerLevels, category: string, label?: string) {
+    this._level = level
+    this.category = category
+    this.label = label
+  }
+
+  public error(content: string): void {
+    this.genericLog(LoggerLevels.error, content)
+  }
+  public warn(content: string): void {
+    this.genericLog(LoggerLevels.warn, content)
+  }
+  public log(content: string): void {
+    this.genericLog(LoggerLevels.log, content)
+  }
+  public debug(content: string): void {
+    this.genericLog(LoggerLevels.debug, content)
+  }
+
+  public genericLog(levelToLog: LoggerLevels, content: string): void {
+    if (this._level >= levelToLog) {
+      this.print(levelToLog, this.category, this.label, content)
+    }
+  }
+
+  private print(
+    levelToLog: LoggerLevels,
+    category: string,
+    label: string | undefined,
+    content: string
+  ): void {
+    if (typeof content === 'string') {
+      const prefix: Array<string> = [getDateTime()]
+      if (label) {
+        prefix.push(label)
+      }
+      content = prefix.concat(content).join(' | ')
+    }
+    switch (levelToLog) {
+      case LoggerLevels.error:
+        console.error(`%c${category}`, 'color: blue;', content)
+        break
+      case LoggerLevels.warn:
+        console.warn(`%c${category}`, 'color: blue;', content)
+        break
+      case LoggerLevels.log:
+        console.log(`%c${category}`, 'color: blue;', content)
+        break
+      case LoggerLevels.debug:
+        console.debug(`%c${category}`, 'color: blue;', content)
+        break
+      default:
+        break
+    }
+  }
+  get level(): LoggerLevels {
+    return this._level
+  }
+  set level(newLevel: LoggerLevels) {
+    if (newLevel >= 0 && newLevel <= 3) {
+      this._level = newLevel
+    } else if (newLevel > 3) {
+      this._level = 3
+      // eslint-disable-next-line no-prototype-builtins
+    } else if (LoggerLevels.hasOwnProperty(newLevel)) {
+      this._level = newLevel
+    } else {
+      this.error("invalid 'level' parameter value: " + JSON.stringify(newLevel))
+    }
+  }
+}

+ 64 - 0
src/api/fetchApi.ts

@@ -0,0 +1,64 @@
+import { Logger } from '../Logger'
+import { getBaseOption, BaseOption } from '../hs-cti/storage'
+import type { CTIRes } from '../hs-cti/outputType'
+
+export interface Request {
+  baseUrl: string
+  url: string
+  data: object
+}
+
+const apiLogger = new Logger(
+  window.ctiLoggerLevel || getBaseOption(BaseOption.LoggerLevel),
+  'HsApi'
+)
+
+export const random16Hex = () =>
+  (0x10000 | (Math.random() * 0x10000)).toString(16).substr(1)
+export const random64Hex = () =>
+  random16Hex() + random16Hex() + random16Hex() + random16Hex()
+
+export async function JPOST({ baseUrl, url, data }: Request): Promise<CTIRes> {
+  const id = random64Hex()
+  const response = await fetch(baseUrl + url, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-B3-TraceId': id,
+      'X-B3-SpanId': id
+    },
+    body: JSON.stringify(data)
+  })
+
+  if (!response.ok) {
+    apiLogger.error(
+      `api response | ${url} | Request failed with status ${response.status}`
+    )
+  }
+
+  return response.json() as Promise<CTIRes>
+}
+
+export const hsTrackJPOST = ({
+  baseUrl,
+  url = '',
+  data
+}: Request): Promise<CTIRes> => {
+  // eslint-disable-next-line no-async-promise-executor
+  return new Promise(async (resolve, reject) => {
+    try {
+      const res = await JPOST({ baseUrl, url, data })
+      const { code, msg } = res
+      console.log(msg)
+      if (code === 0) {
+        apiLogger.log(`api response | ${url} | ${JSON.stringify(res)}`)
+      } else {
+        apiLogger.error(`api response | ${url} | ${JSON.stringify(res)}`)
+      }
+      resolve(res)
+    } catch (e) {
+      apiLogger.error(`api response | ${url} | ${JSON.stringify(e)}`)
+      reject(e)
+    }
+  })
+}

+ 103 - 0
src/api/hs-cti/ctiSdk.ts

@@ -0,0 +1,103 @@
+import { hsTrackJPOST } from '../fetchApi'
+
+import type {
+  CTIBaseOptions,
+  ctiManualCallParams,
+  ctiManualHangParams,
+  ctiListenParams,
+  ctiLoadAgentGroupsParams,
+  ctiSetActiveServiceParams
+} from './ctiSdkModel'
+// 获取初始化配置
+const baseUrl = ''
+
+export const getInitConf = (data: CTIBaseOptions) => {
+  return hsTrackJPOST({
+    baseUrl,
+    url: '/api/cti/biz/open/agent/get-init-config',
+    data
+  })
+}
+
+// 坐席签入
+export const agentCheckIn = (data: CTIBaseOptions) => {
+  return hsTrackJPOST({
+    baseUrl,
+    url: '/api/cti/biz/open/agent/check-in',
+    data
+  })
+}
+
+// 坐席签出
+export const agentCheckOut = (data: CTIBaseOptions) => {
+  return hsTrackJPOST({
+    baseUrl,
+    url: '/api/cti/biz/open/agent/check-out',
+    data
+  })
+}
+
+// 坐席置闲
+export const agentSetIdle = (data: CTIBaseOptions) => {
+  return hsTrackJPOST({ baseUrl, url: '/api/cti/biz/open/agent/idle', data })
+}
+
+// 坐席置忙
+export const agentSetBusy = (data: CTIBaseOptions) => {
+  return hsTrackJPOST({ baseUrl, url: '/api/cti/biz/open/agent/busy', data })
+}
+
+// 获取坐席状态
+export const getAgentStatus = (data: CTIBaseOptions) => {
+  return hsTrackJPOST({
+    baseUrl,
+    url: '/api/cti/biz/open/agent/agent-state',
+    data
+  })
+}
+
+// 外呼
+export const manualCall = (data: ctiManualCallParams) => {
+  return hsTrackJPOST({
+    baseUrl,
+    url: '/api/cti/biz/open/agent/manual-call',
+    data
+  })
+}
+
+// 挂断
+export const manualHang = (data: ctiManualHangParams) => {
+  return hsTrackJPOST({
+    baseUrl,
+    url: '/api/cti/biz/open/agent/manual-hang',
+    data
+  })
+}
+
+// 发起监听
+export const listen = (data: ctiListenParams) => {
+  return hsTrackJPOST({ baseUrl, url: '/api/cti/biz/open/agent/listen', data })
+}
+
+// 获取监控组成员信息
+export const loadAgentGroupData = (data: ctiLoadAgentGroupsParams) => {
+  return hsTrackJPOST({
+    baseUrl,
+    url: '/api/cti/biz/open/monitor/load-agent-group-data',
+    data
+  })
+}
+
+// 机器人外呼-签入人工组
+export const setActiveServiceTask = (data: ctiSetActiveServiceParams) => {
+  return hsTrackJPOST({
+    baseUrl,
+    url: '/api/cti/biz/open/human-service/member-active',
+    data
+  })
+}
+
+// 获取 cti 流程 ID
+export const getCtiFlowId = (data: CTIBaseOptions) => {
+  return hsTrackJPOST({ baseUrl, url: '/api/cti/biz/open/num/generate', data })
+}

+ 69 - 0
src/api/hs-cti/ctiSdkModel.ts

@@ -0,0 +1,69 @@
+export interface CTIBaseOptions {
+  agentId: string
+  vccId?: string
+  password: string
+  scene: string
+  ctiFlowId?: string
+  monitorScene?: string
+}
+
+export interface InitOptions {
+  // 租户隔离
+  vccId: string
+  // 坐席 ID
+  agentId: string
+  // 账户隔离
+  appCode: string
+  // 分机号
+  phoneNum: string
+  // 分机密码
+  phonePwd: string
+  // sip 服务地址
+  sipServer: string
+  // sip wss 服务地址
+  wssServer: string
+  // sip 通路服务地址
+  iceServer: string
+  // IM 心跳间隔
+  imHeartTime: number
+  // IM 重试次数
+  imRetryCount: number
+  // FS 心跳间隔
+  fsHeartTime: number
+  // FS 重试次数
+  fsRetryCount: number
+  // FS 重试间隔时间
+  fsRetryTime: number
+  // FS 注册过期时间
+  fsRegisterExpireTime: number
+  // 单次初始化唯一 ID
+  ctiSessionId: string
+  // IM websocket url
+  imWsServer: string
+  // IM websocket path
+  imWsPath: string
+}
+
+export interface CTIManualCallOptions {
+  called: string
+  circuitUid: string
+  ext?: object
+}
+
+export type ctiManualCallParams = CTIBaseOptions & CTIManualCallOptions
+
+export interface ctiManualHangParams extends CTIBaseOptions {
+  callId: string
+}
+
+export interface ctiListenParams extends CTIBaseOptions {
+  leaderAgentId: string
+}
+
+export interface ctiLoadAgentGroupsParams extends CTIBaseOptions {
+  monitorIds: string[]
+}
+
+export interface ctiSetActiveServiceParams extends CTIBaseOptions {
+  serviceId: string
+}

+ 1 - 0
src/cs-cti/index.dts.ts

@@ -0,0 +1 @@
+export * from './index'

+ 3 - 0
src/cs-cti/index.ts

@@ -0,0 +1,3 @@
+/** 这是一个测试文件,用于测试多模块打包 */
+
+export const newDate = new Date()

+ 136 - 0
src/eventemitter.d.ts

@@ -0,0 +1,136 @@
+/**
+ * Minimal `EventEmitter` interface that is molded against the Node.js
+ * `EventEmitter` interface.
+ */
+declare class EventEmitter<
+  EventTypes extends EventEmitter.ValidEventTypes = string | symbol,
+  // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint
+  Context extends any = any
+> {
+  static prefixed: string | boolean
+
+  /**
+   * Return an array listing the events for which the emitter has registered
+   * listeners.
+   */
+  eventNames(): Array<EventEmitter.EventNames<EventTypes>>
+
+  /**
+   * Return the listeners registered for a given event.
+   */
+  listeners<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T
+  ): Array<EventEmitter.EventListener<EventTypes, T>>
+
+  /**
+   * Return the number of listeners listening to a given event.
+   */
+  listenerCount(event: EventEmitter.EventNames<EventTypes>): number
+
+  /**
+   * Calls each of the listeners registered for a given event.
+   */
+  emit<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    ...args: EventEmitter.EventArgs<EventTypes, T>
+  ): boolean
+
+  /**
+   * Add a listener for a given event.
+   */
+  on<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context
+  ): this
+  addListener<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context
+  ): this
+
+  /**
+   * Add a one-time listener for a given event.
+   */
+  once<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context
+  ): this
+
+  /**
+   * Remove the listeners of a given event.
+   */
+  removeListener<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn?: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context,
+    once?: boolean
+  ): this
+  off<T extends EventEmitter.EventNames<EventTypes>>(
+    event: T,
+    fn?: EventEmitter.EventListener<EventTypes, T>,
+    context?: Context,
+    once?: boolean
+  ): this
+
+  /**
+   * Remove all listeners, or those of the specified event.
+   */
+  removeAllListeners(event?: EventEmitter.EventNames<EventTypes>): this
+}
+
+declare namespace EventEmitter {
+  export interface ListenerFn<Args extends any[] = any[]> {
+    (...args: Args): void
+  }
+
+  export interface EventEmitterStatic {
+    new <
+      EventTypes extends ValidEventTypes = string | symbol,
+      Context = any
+    >(): EventEmitter<EventTypes, Context>
+  }
+
+  /**
+   * `object` should be in either of the following forms:
+   * ```
+   * interface EventTypes {
+   *   'event-with-parameters': any[]
+   *   'event-with-example-handler': (...args: any[]) => void
+   * }
+   * ```
+   */
+  export type ValidEventTypes = string | symbol | object
+
+  export type EventNames<T extends ValidEventTypes> = T extends string | symbol
+    ? T
+    : keyof T
+
+  export type ArgumentMap<T extends object> = {
+    [K in keyof T]: T[K] extends (...args: any[]) => void
+      ? Parameters<T[K]>
+      : T[K] extends any[]
+      ? T[K]
+      : any[]
+  }
+
+  export type EventListener<
+    T extends ValidEventTypes,
+    K extends EventNames<T>
+  > = T extends string | symbol
+    ? (...args: any[]) => void
+    : (
+        ...args: ArgumentMap<Exclude<T, string | symbol>>[Extract<K, keyof T>]
+      ) => void
+
+  export type EventArgs<
+    T extends ValidEventTypes,
+    K extends EventNames<T>
+  > = Parameters<EventListener<T, K>>
+
+  export const EventEmitter: EventEmitterStatic
+}
+
+export { EventEmitter }
+export default EventEmitter

+ 364 - 0
src/eventemitter.js

@@ -0,0 +1,364 @@
+'use strict'
+
+let has = Object.prototype.hasOwnProperty,
+  prefix = '~'
+
+/**
+ * Constructor to create a storage for our `EE` objects.
+ * An `Events` instance is a plain object whose properties are event names.
+ *
+ * @constructor
+ * @private
+ */
+// eslint-disable-next-line @typescript-eslint/no-empty-function
+function Events() {}
+
+//
+// We try to not inherit from `Object.prototype`. In some engines creating an
+// instance in this way is faster than calling `Object.create(null)` directly.
+// If `Object.create(null)` is not supported we prefix the event names with a
+// character to make sure that the built-in object properties are not
+// overridden or used as an attack vector.
+//
+if (Object.create) {
+  Events.prototype = Object.create(null)
+
+  //
+  // This hack is needed because the `__proto__` property is still inherited in
+  // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5.
+  //
+  // eslint-disable-next-line no-const-assign
+  if (!new Events().__proto__) prefix = false
+}
+
+/**
+ * Representation of a single event listener.
+ *
+ * @param {Function} fn The listener function.
+ * @param {*} context The context to invoke the listener with.
+ * @param {Boolean} [once=false] Specify if the listener is a one-time listener.
+ * @constructor
+ * @private
+ */
+function EE(fn, context, once) {
+  this.fn = fn
+  this.context = context
+  this.once = once || false
+}
+
+/**
+ * Add a listener for a given event.
+ *
+ * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} context The context to invoke the listener with.
+ * @param {Boolean} once Specify if the listener is a one-time listener.
+ * @returns {EventEmitter}
+ * @private
+ */
+function addListener(emitter, event, fn, context, once) {
+  if (typeof fn !== 'function') {
+    throw new TypeError('The listener must be a function')
+  }
+
+  const listener = new EE(fn, context || emitter, once),
+    evt = prefix ? prefix + event : event
+
+  if (!emitter._events[evt])
+    (emitter._events[evt] = listener), emitter._eventsCount++
+  else if (!emitter._events[evt].fn) emitter._events[evt].push(listener)
+  else emitter._events[evt] = [emitter._events[evt], listener]
+
+  return emitter
+}
+
+/**
+ * Clear event by name.
+ *
+ * @param {EventEmitter} emitter Reference to the `EventEmitter` instance.
+ * @param {(String|Symbol)} evt The Event name.
+ * @private
+ */
+function clearEvent(emitter, evt) {
+  if (--emitter._eventsCount === 0) emitter._events = new Events()
+  else delete emitter._events[evt]
+}
+
+/**
+ * Minimal `EventEmitter` interface that is molded against the Node.js
+ * `EventEmitter` interface.
+ *
+ * @constructor
+ * @public
+ */
+function EventEmitter() {
+  this._events = new Events()
+  this._eventsCount = 0
+}
+
+/**
+ * Return an array listing the events for which the emitter has registered
+ * listeners.
+ *
+ * @returns {Array}
+ * @public
+ */
+EventEmitter.prototype.eventNames = function eventNames() {
+  let names = [],
+    events,
+    name
+
+  if (this._eventsCount === 0) return names
+
+  for (name in (events = this._events)) {
+    if (has.call(events, name)) names.push(prefix ? name.slice(1) : name)
+  }
+
+  if (Object.getOwnPropertySymbols) {
+    return names.concat(Object.getOwnPropertySymbols(events))
+  }
+
+  return names
+}
+
+/**
+ * Return the listeners registered for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Array} The registered listeners.
+ * @public
+ */
+EventEmitter.prototype.listeners = function listeners(event) {
+  const evt = prefix ? prefix + event : event,
+    handlers = this._events[evt]
+
+  if (!handlers) return []
+  if (handlers.fn) return [handlers.fn]
+
+  for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) {
+    ee[i] = handlers[i].fn
+  }
+
+  return ee
+}
+
+/**
+ * Return the number of listeners listening to a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Number} The number of listeners.
+ * @public
+ */
+EventEmitter.prototype.listenerCount = function listenerCount(event) {
+  const evt = prefix ? prefix + event : event,
+    listeners = this._events[evt]
+
+  if (!listeners) return 0
+  if (listeners.fn) return 1
+  return listeners.length
+}
+
+/**
+ * Calls each of the listeners registered for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @returns {Boolean} `true` if the event had listeners, else `false`.
+ * @public
+ */
+EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) {
+  const evt = prefix ? prefix + event : event
+
+  if (!this._events[evt]) return false
+
+  let listeners = this._events[evt],
+    len = arguments.length,
+    args,
+    i
+
+  if (listeners.fn) {
+    if (listeners.once)
+      this.removeListener(event, listeners.fn, undefined, true)
+
+    switch (len) {
+      case 1:
+        return listeners.fn.call(listeners.context), true
+      case 2:
+        return listeners.fn.call(listeners.context, a1), true
+      case 3:
+        return listeners.fn.call(listeners.context, a1, a2), true
+      case 4:
+        return listeners.fn.call(listeners.context, a1, a2, a3), true
+      case 5:
+        return listeners.fn.call(listeners.context, a1, a2, a3, a4), true
+      case 6:
+        return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true
+    }
+
+    for (i = 1, args = new Array(len - 1); i < len; i++) {
+      args[i - 1] = arguments[i]
+    }
+
+    listeners.fn.apply(listeners.context, args)
+  } else {
+    let length = listeners.length,
+      j
+
+    for (i = 0; i < length; i++) {
+      if (listeners[i].once)
+        this.removeListener(event, listeners[i].fn, undefined, true)
+
+      switch (len) {
+        case 1:
+          listeners[i].fn.call(listeners[i].context)
+          break
+        case 2:
+          listeners[i].fn.call(listeners[i].context, a1)
+          break
+        case 3:
+          listeners[i].fn.call(listeners[i].context, a1, a2)
+          break
+        case 4:
+          listeners[i].fn.call(listeners[i].context, a1, a2, a3)
+          break
+        default:
+          if (!args)
+            for (j = 1, args = new Array(len - 1); j < len; j++) {
+              args[j - 1] = arguments[j]
+            }
+
+          listeners[i].fn.apply(listeners[i].context, args)
+      }
+    }
+  }
+
+  return true
+}
+
+/**
+ * Add a listener for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} [context=this] The context to invoke the listener with.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+EventEmitter.prototype.on = function on(event, fn, context) {
+  return addListener(this, event, fn, context, false)
+}
+
+/**
+ * Add a one-time listener for a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn The listener function.
+ * @param {*} [context=this] The context to invoke the listener with.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+EventEmitter.prototype.once = function once(event, fn, context) {
+  return addListener(this, event, fn, context, true)
+}
+
+/**
+ * Remove the listeners of a given event.
+ *
+ * @param {(String|Symbol)} event The event name.
+ * @param {Function} fn Only remove the listeners that match this function.
+ * @param {*} context Only remove the listeners that have this context.
+ * @param {Boolean} once Only remove one-time listeners.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+EventEmitter.prototype.removeListener = function removeListener(
+  event,
+  fn,
+  context,
+  once
+) {
+  const evt = prefix ? prefix + event : event
+
+  if (!this._events[evt]) return this
+  if (!fn) {
+    clearEvent(this, evt)
+    return this
+  }
+
+  const listeners = this._events[evt]
+
+  if (listeners.fn) {
+    if (
+      listeners.fn === fn &&
+      (!once || listeners.once) &&
+      (!context || listeners.context === context)
+    ) {
+      clearEvent(this, evt)
+    }
+  } else {
+    for (var i = 0, events = [], length = listeners.length; i < length; i++) {
+      if (
+        listeners[i].fn !== fn ||
+        (once && !listeners[i].once) ||
+        (context && listeners[i].context !== context)
+      ) {
+        events.push(listeners[i])
+      }
+    }
+
+    //
+    // Reset the array, or remove it completely if we have no more listeners.
+    //
+    if (events.length)
+      this._events[evt] = events.length === 1 ? events[0] : events
+    else clearEvent(this, evt)
+  }
+
+  return this
+}
+
+/**
+ * Remove all listeners, or those of the specified event.
+ *
+ * @param {(String|Symbol)} [event] The event name.
+ * @returns {EventEmitter} `this`.
+ * @public
+ */
+EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) {
+  let evt
+
+  if (event) {
+    evt = prefix ? prefix + event : event
+    if (this._events[evt]) clearEvent(this, evt)
+  } else {
+    this._events = new Events()
+    this._eventsCount = 0
+  }
+
+  return this
+}
+
+//
+// Alias methods names because people roll like that.
+//
+EventEmitter.prototype.off = EventEmitter.prototype.removeListener
+EventEmitter.prototype.addListener = EventEmitter.prototype.on
+
+//
+// Expose the prefix.
+//
+EventEmitter.prefixed = prefix
+
+//
+// Allow `EventEmitter` to be imported as module namespace.
+//
+EventEmitter.EventEmitter = EventEmitter
+
+//
+// Expose the module.
+//
+if ('undefined' !== typeof module) {
+  module.exports = EventEmitter
+}
+
+export default EventEmitter

+ 920 - 0
src/hs-cti/HsCTI.ts

@@ -0,0 +1,920 @@
+import { SimpleUserPlus } from './SImpleUserPlus'
+import EventEmitter from '../eventemitter'
+import { Logger, LoggerLevels } from '../Logger'
+import {
+  Scene,
+  type HsCTIInitOptions,
+  type RequiredHsCTIInitOptions,
+  CTIStatus,
+  SIPStatus,
+  SocketStatus,
+  CTIEvent,
+  CTIErrorType,
+  HskTerminatedCode,
+  SdkErrorCode,
+  CTIRes,
+  CallStatus
+} from './outputType'
+import type {
+  InitOptions,
+  CTIBaseOptions,
+  CTIManualCallOptions
+} from '../api/hs-cti/ctiSdkModel'
+import HsSocket from './HsSocket'
+import {
+  setBaseOption,
+  resetBaseOption,
+  BaseOption,
+  getClientId
+} from './storage'
+import {
+  AudioName,
+  ExceptMessage,
+  type SIPStatusChangeParams,
+  type SocketStatusChangeParams,
+  SocketEvent,
+  CTIEventParams,
+  SimpleUserPlusDelegate,
+  SimpleUserPlusOptions
+} from './type'
+
+import { upperCamelToLowerSnake } from '@/utils'
+import {
+  agentCheckIn,
+  // agentCheckOut,
+  agentSetBusy,
+  agentSetIdle,
+  getAgentStatus,
+  getCtiFlowId,
+  getInitConf,
+  listen,
+  loadAgentGroupData,
+  manualCall,
+  manualHang,
+  setActiveServiceTask
+} from '../api/hs-cti/ctiSdk'
+import {
+  checkCTIStatus,
+  getUserMedia,
+  handleApiRes,
+  validateParams
+} from './tools'
+import { Invitation, SessionState, UserAgentState } from 'sip.js'
+
+/**
+ * 本地提示音
+ * _ringAudio 机器人外呼/监听,等待接起提示音
+ * _waitAudio 主动外呼,点击拨打时的等待音
+ * _byeAudio 结束通话提示音
+ */
+const audioList = {
+  _ringAudio: 'https://static.fuxicarbon.com/hs-cti/ring.wav',
+  _waitAudio: 'https://static.fuxicarbon.com/hs-cti/manual.wav',
+  _byeAudio: 'https://static.fuxicarbon.com/hs-cti/bye.wav'
+}
+
+/** @class HsCTI 红杉外呼类 */
+
+export class HsCTI extends EventEmitter {
+  private logger: Logger
+  public loggerLevel: LoggerLevels
+  /** HsCTI 实例 */
+  public static instance: HsCTI | undefined
+  public scene: Scene
+  public agentId: string
+  public vccId: string
+
+  /** 接口返回的初始化配置 */
+  private _initOptions: InitOptions | undefined
+
+  /** IM socket的实例 */
+  private _socket!: HsSocket
+  /** sip.js UA实例 */
+  private _sipUserAgent: SimpleUserPlus | undefined
+
+  private _callId!: string
+  private _ctiFlowIdList!: string[]
+
+  /** 基本参数 */
+  private _baseParams: CTIBaseOptions
+  /** 等待提示音播放器 */
+  private _waitAudio: HTMLAudioElement
+  /** 振铃提示音播放器 */
+  private _ringAudio: HTMLAudioElement
+  /** 结束通话提示音 */
+  private _byeAudio: HTMLAudioElement
+  /** 远端音频流播放器 */
+  private _remoteAudio: HTMLAudioElement
+  /** CTI状态 */
+  private _ctiStatus!: CTIStatus
+  private _ctiStatusList!: CTIStatus[]
+  /** sip状态 */
+  private _sipStatus!: SIPStatus
+  private _sipStatusList!: SIPStatus[]
+  /** socket状态 */
+  private _socketStatus!: SocketStatus
+  private _socketStatusList!: SocketStatus[]
+
+  private _callStatus: CallStatus
+  private _terminatedStatusList: string[]
+
+  private constructor(
+    hsCTIInitOptions: HsCTIInitOptions | RequiredHsCTIInitOptions
+  ) {
+    const { vccId, agentId, scene, password, env, loggerLevel } =
+      hsCTIInitOptions
+    super()
+    this.loggerLevel = window.ctiLoggerLevel || loggerLevel || LoggerLevels.log
+    this.logger = new Logger(this.loggerLevel, 'HsCTI')
+    this._waitAudio = new Audio()
+    this._ringAudio = new Audio()
+    this._byeAudio = new Audio()
+    this._remoteAudio = new Audio()
+
+    this.vccId = vccId
+    this.agentId = agentId
+    this.scene = scene
+    this._callId = ''
+    setBaseOption(BaseOption.ENV, env)
+    setBaseOption(BaseOption.LoggerLevel, this.loggerLevel)
+
+    this._baseParams = { agentId, vccId, scene, password }
+    const baseTrackParams = { agent_id: agentId, vcc_id: vccId, scene }
+    if (scene === Scene.Monitor) {
+      const { monitorScene } = hsCTIInitOptions
+      this._baseParams = { ...this._baseParams, monitorScene }
+      setBaseOption(
+        BaseOption.TrackParams,
+        { ...baseTrackParams, monitor_scene: monitorScene },
+        true
+      )
+    } else {
+      setBaseOption(BaseOption.TrackParams, baseTrackParams, true)
+    }
+
+    this.initInstanceOptions()
+    this.setCTIStatus(CTIStatus.Initial)
+    // 首次获取实例时,将外呼状态置为未开始
+    this._callStatus = CallStatus.Stopped
+    this._terminatedStatusList = []
+  }
+
+  get getCTIStatus() {
+    return this._ctiStatus
+  }
+  get getSocketStatus() {
+    return this._socketStatus
+  }
+  get getSIPStatus() {
+    return this._sipStatus
+  }
+
+  @validateParams()
+  public static getInstance(
+    hsCTIInitOptions: HsCTIInitOptions | RequiredHsCTIInitOptions
+  ): HsCTI {
+    if (!HsCTI.instance) {
+      HsCTI.instance = new HsCTI(hsCTIInitOptions)
+    }
+    setBaseOption(BaseOption.TrackParams, {
+      clientId: getClientId()
+    })
+    return HsCTI.instance
+  }
+
+  /** @private 重置实例 */
+  private initInstanceOptions() {
+    this._ctiFlowIdList = []
+    this._ctiStatusList = []
+    this._sipStatusList = []
+    this._socketStatusList = []
+    HsCTI.instance = undefined
+    this._initOptions = undefined
+    resetBaseOption()
+  }
+
+  /** @private 优雅关闭 SIP 和 socket */
+  private clearSocketAndSip() {
+    this._socket?.closeSocket()
+    this._sipUserAgent?.unregister()
+    this._sipUserAgent?.disconnect()
+    this._sipUserAgent = undefined
+  }
+
+  /**
+   * @private 设置等待音 src
+   * @param {AudioName} audioName
+   * @param {boolean} loop
+   */
+  private setAudioSrc(audioName: AudioName, loop: boolean) {
+    this.logger.debug(`media | 设置等待音 src: ${audioList[audioName]}`)
+    this[audioName].src = audioList[audioName]
+    this[audioName].currentTime = 0
+    this[audioName].autoplay = false
+    this[audioName].loop = loop
+  }
+
+  private playAudio(audioName: AudioName) {
+    this.logger.debug(`media | 播放音频:${audioName}`)
+    this[audioName].play()
+  }
+
+  private stopLocalAudio() {
+    switch (this.scene) {
+      case Scene.Robot:
+      case Scene.Monitor:
+        this.stopAudio(AudioName.RingAudio, true)
+        break
+      case Scene.Manual:
+        this.stopAudio(AudioName.WaitAudio, true)
+        break
+    }
+  }
+
+  private stopAudio(audioName: AudioName, loop: boolean) {
+    this.logger.debug(`media | 停止音频播放:${audioName}`)
+    this[audioName].src = ''
+    setTimeout(() => {
+      this.setAudioSrc(audioName, loop)
+    }, 1000)
+  }
+
+  @getUserMedia()
+  public init() {
+    // 不允许重复初始化,必须在销毁sdk后才能重新初始化
+    const [lastStatus] = this._ctiStatusList.slice(-1)
+    if (lastStatus !== CTIStatus.Initial) return
+    this.setAudioSrc(AudioName.ByeAudio, false)
+    switch (this.scene) {
+      case Scene.Robot:
+      case Scene.Monitor:
+        this.setAudioSrc(AudioName.RingAudio, true)
+        break
+      case Scene.Manual:
+        this.setAudioSrc(AudioName.WaitAudio, true)
+        break
+    }
+    this.getInitConfig()
+  }
+
+  private async getInitConfig() {
+    const res = await getInitConf(this._baseParams)
+    const { code, data: initOptions, msg } = res
+    /**
+     * 由于后端接口层面对于查询来说,查不到不代表异常,所以即使查不到也是正向流程
+     * 因此如果传入一个不存在的agentId,后端也会返回code=0,但是data=null
+     * 这里需要根据code===0&&initOptions存在进行联合判断,只有两个条件都成立,才表示该坐席获取fs以及im等配置成功
+     */
+    if (code === 0 && initOptions) {
+      setBaseOption(BaseOption.TrackParams, {
+        sip_server: initOptions.sipServer,
+        cti_session_id: initOptions.ctiSessionId
+      })
+      this._initOptions = initOptions
+      this.initSocket(initOptions)
+      this.initSip(initOptions)
+    } else {
+      this.eventEmitAndTrack(CTIEvent.OnCtiError, {
+        type: CTIErrorType.ServerTerminated,
+        code: HskTerminatedCode.GetInitConfig,
+        // 如果code===0并且initOptions不存在时,代表未查到对应坐席,此时接口返回的msg是空字符串,所以提示文案要特定处理
+        msg: code === 0 && !initOptions ? '未查到坐席' : msg,
+        method: 'getInitConfig',
+        res_code: code
+      })
+    }
+  }
+
+  private initSocket(initOptions: InitOptions) {
+    this.setSocketStatus({ status: SocketStatus.Initial })
+    this._socket = new HsSocket({
+      agentId: this.agentId,
+      vccId: this.vccId,
+      // appCode: initOptions.appCode,
+      imWsServer: initOptions.imWsServer,
+      imWsPath: initOptions.imWsPath,
+      imHeartTime: initOptions.imHeartTime,
+      imRetryCount: initOptions.imRetryCount,
+      ctiSessionId: initOptions.ctiSessionId,
+      loggerLevel: this.loggerLevel
+    })
+    this._socket.on(SocketEvent.SocketDownEvent, ({ eventData }) => {
+      const { eventName, ext } = eventData
+      this.logger.log(
+        `socket server down | ${eventName} | ${JSON.stringify(ext)}`
+      )
+
+      this.handleSocketDownEvent({ eventName, ext })
+    })
+
+    this._socket.on(SocketEvent.SetSocketStatus, res => {
+      this.setSocketStatus(res)
+    })
+
+    this._socket.initSocket()
+  }
+
+  private handleSocketDownEvent({ eventName, ext }: CTIEventParams) {
+    const ctiFlowId = this._baseParams.ctiFlowId
+    const extCtiFlowId = ext.ctiFlowId
+    if (
+      [Scene.Manual, Scene.Monitor].includes(this.scene) &&
+      ctiFlowId &&
+      extCtiFlowId &&
+      ctiFlowId !== extCtiFlowId
+    ) {
+      this.logger.error(
+        `cti_flow_id | 不一致! fe: ${ctiFlowId}, server: ${extCtiFlowId}, eventName: ${eventName}`
+      )
+      return
+    }
+
+    this.serverEventEmit({ eventName, ext })
+  }
+
+  /** @private serverEventEmit 统一处理服务端推送的事件 */
+  private serverEventEmit({ eventName, ext }: CTIEventParams) {
+    const NO_EMIT_EVENT_LIST = [
+      CTIEvent.OnRingStart,
+      CTIEvent.OnDetectedTone,
+      CTIEvent.OnRingEnd
+    ]
+    switch (eventName) {
+      case CTIEvent.OnRingStart:
+        this.stopLocalAudio()
+        break
+      case CTIEvent.OnDetectedTone:
+        this.stopLocalAudio()
+        break
+      case CTIEvent.OnAgentWorkReport:
+        break
+      case CTIEvent.OnCallEnd:
+        /** 通话结束:重置可拨打状态,清空本轮通话的CallId */
+        this._callStatus = CallStatus.Stopped
+        setBaseOption(BaseOption.TrackParams, {
+          call_id: ''
+        })
+        break
+      case CTIEvent.OnRingEnd:
+        /**  TODO: 后 4 个事件未来服务端不再推送时删掉  */
+        break
+      default:
+      // do nothing
+    }
+
+    if (!NO_EMIT_EVENT_LIST.includes(eventName)) {
+      this.eventEmitAndTrack(eventName, ext)
+    }
+  }
+
+  private initSip(initOptions: InitOptions) {
+    this.setSipStatus({ status: SIPStatus.Initial })
+    const server = initOptions.wssServer
+    const simpleUserPlusOptions: SimpleUserPlusOptions = {
+      aor: initOptions.sipServer,
+      reconnectionAttempts: initOptions.fsRetryCount,
+      reconnectionDelay: initOptions.fsRetryTime,
+      optionsPingInterval: initOptions.fsHeartTime,
+      media: {
+        remote: {
+          audio: this._remoteAudio
+        }
+      },
+      userAgentOptions: {
+        gracefulShutdown: true,
+        authorizationPassword: initOptions.phonePwd,
+        sessionDescriptionHandlerFactoryOptions: {
+          constraints: {
+            audio: true,
+            video: false
+          },
+          peerConnectionConfiguration: {
+            iceServers: []
+          }
+        }
+      }
+    }
+
+    simpleUserPlusOptions.delegate = this.sipDelegate()
+
+    this._sipUserAgent = new SimpleUserPlus(server, simpleUserPlusOptions)
+    this._sipUserAgent.connect().catch((error: Error) => {
+      const err = `SIP UserAgent 启动失败:${JSON.stringify(error)}`
+      this.logger.error(err)
+      this.setSipStatus({
+        status: SIPStatus.Terminated,
+        code: HskTerminatedCode.SIPInitUserAgent,
+        error: err,
+        method: 'initSIPJS'
+      })
+    })
+  }
+
+  private sipDelegate(): SimpleUserPlusDelegate {
+    return {
+      onServerConnect: () => {
+        this._sipUserAgent!.register()
+      },
+      onServerDisconnect: () => {
+        // 正向关闭UA时会进入此回调
+        this.setSipStatus({ status: SIPStatus.Terminated })
+      },
+      onReconnectStart: () => {
+        // 开始重连时将状态流转为ReTry
+        this.setSipStatus({ status: SIPStatus.ReTry })
+      },
+      onReconnectFailed: () => {
+        // 重连失败时会进如此回调
+        this.setSipStatus({
+          status: SIPStatus.Terminated,
+          code: HskTerminatedCode.SIPOnDisconnect,
+          error: `超过重连次数,终止重连`,
+          method: 'onReconnectFailed'
+        })
+        this.setSipStatus({ status: SIPStatus.Terminated })
+      },
+      onRegistered: () => {
+        this.setSipStatus({ status: SIPStatus.Ready })
+      },
+      onUserAgentStateChange: (state: UserAgentState) => {
+        switch (state) {
+          case UserAgentState.Started:
+            this.setSipStatus({ status: SIPStatus.Started })
+            break
+          case UserAgentState.Stopped:
+            this.setSipStatus({
+              status: SIPStatus.Terminated,
+              code: this._sipStatusList.includes(SIPStatus.Started)
+                ? HskTerminatedCode.SIPUserAgentStateStopped
+                : HskTerminatedCode.SIPInitUserAgent,
+              error: 'SIP UserAgentState 状态停止',
+              method: 'onUserAgentStateChange'
+            })
+            break
+        }
+      },
+      onInvite: (invitation: Invitation) => {
+        const callId = invitation.request.getHeader('P-Wdh-Callid') || ''
+        const ctiFlowId = invitation.request.getHeader('P-WDH-CtiFlowId') || ''
+        if (
+          this.scene === Scene.Manual &&
+          ctiFlowId !== this._baseParams.ctiFlowId
+        ) {
+          this.logger.error(
+            `cti_flow_id 不一致! fe: ${this._baseParams.ctiFlowId} | P-WDH-CtiFlowId: ${ctiFlowId}`
+          )
+          return
+        }
+        this._callId = callId
+        setBaseOption(BaseOption.TrackParams, {
+          call_id: callId
+        })
+        this.sessionStateChangeAndTrack(invitation.state)
+        if ([Scene.Robot, Scene.Monitor].includes(this.scene)) {
+          this.playAudio(AudioName.RingAudio)
+        }
+
+        if ([Scene.Manual].includes(this.scene)) {
+          this.answer()
+        }
+
+        invitation.delegate = {
+          onCancel: cancel => {
+            const error = `sip_invitation_on_cancel | ${JSON.stringify(cancel)}`
+            this.logger.error(error)
+            this.eventEmitAndTrack(
+              CTIEvent.OnCtiError,
+              {
+                type: CTIErrorType.SdkError,
+                code: SdkErrorCode.InvitationCancel,
+                msg: '当前通话已结束',
+                method: 'sipDelegate'
+              },
+              error
+            )
+          }
+        }
+
+        invitation.stateChange.addListener((state: SessionState) => {
+          this.sessionStateChangeAndTrack(state)
+
+          switch (state) {
+            case SessionState.Initial:
+            case SessionState.Establishing:
+              break
+            case SessionState.Established:
+              if ([Scene.Robot, Scene.Monitor].includes(this.scene)) {
+                this.stopLocalAudio()
+              }
+              break
+            case SessionState.Terminating:
+              break
+            case SessionState.Terminated:
+              this.playAudio(AudioName.ByeAudio)
+              setTimeout(() => {
+                this.stopAudio(AudioName.ByeAudio, false)
+              }, 1000)
+              this.stopLocalAudio()
+              this._callStatus = CallStatus.Stopped
+              setBaseOption(BaseOption.TrackParams, {
+                call_id: ''
+              })
+              break
+            default:
+              break
+          }
+        })
+      }
+    }
+  }
+
+  private sessionStateChangeAndTrack(status: SessionState) {
+    this.eventEmitAndTrack(CTIEvent.OnSessionStatusChange, {
+      status
+    })
+    const trackName = `sip_session_state_${upperCamelToLowerSnake(status)}`
+    this.logger.log(trackName)
+  }
+
+  /**
+   * @private setCTIStatus CTI 状态流转
+   * @param {CTIStatus} ctiStatus
+   */
+  private setCTIStatus(ctiStatus: CTIStatus) {
+    if (ctiStatus === this.getCTIStatus) return
+
+    this._ctiStatus = ctiStatus
+    this._ctiStatusList.push(ctiStatus)
+    const logInfo = `cti status | ${ctiStatus} | ${this._ctiStatusList}`
+    if (ctiStatus === CTIStatus.Terminated) {
+      this.setSocketStatus({ status: SocketStatus.Terminated })
+      this.setSipStatus({ status: SIPStatus.Terminated })
+      this.stopLocalAudio()
+      this.logger.warn(logInfo)
+    } else {
+      this.logger.debug(logInfo)
+    }
+    if (ctiStatus === CTIStatus.Ready) {
+      this.setSocketStatus({ status: SocketStatus.Ready })
+      this.setSipStatus({ status: SIPStatus.Ready })
+      this._terminatedStatusList = []
+      if (!this._ctiStatusList.includes(CTIStatus.ReTry)) {
+        this.checkIn()
+      }
+    }
+  }
+
+  /**
+   * @private setSocketStatus Socket 状态流转
+   * @param SocketStatusChangeParams Socket 状态流转参数及错误详情等
+   */
+  private setSocketStatus({ status, code, error }: SocketStatusChangeParams) {
+    if (status === this.getSocketStatus) return
+
+    this._socketStatus = status
+    this._socketStatusList.push(status)
+    const logInfo = `socket status | ${status} | ${this._socketStatusList}`
+    status === SocketStatus.Terminated
+      ? this.logger.warn(logInfo)
+      : this.logger.debug(logInfo)
+
+    this.socketOrSipStatusChange(status, this.getSIPStatus)
+    if (error) {
+      this._terminatedStatusList.push(code as string)
+      this.eventEmitAndTrack(
+        CTIEvent.OnCtiError,
+        {
+          type: CTIErrorType.SdkTerminated,
+          code,
+          msg:
+            code === HskTerminatedCode.SocketRepeatLogin
+              ? ExceptMessage.CTIRepeatLoginMsg
+              : ExceptMessage.CommonNetworkErrorMsg,
+          method: 'setSocketStatus'
+        },
+        {
+          error_msg: error,
+          socket_status_list: this._socketStatusList,
+          sip_status_list: this._sipStatusList,
+          cti_status_list: this._ctiStatusList
+        }
+      )
+      this.clearSocketAndSip()
+    }
+  }
+
+  /**
+   * @private setSipStatus SIP 状态流转
+   * @param SIPStatusChangeParams SIP 状态流转参数及错误详情等
+   */
+  private setSipStatus({ status, code, error, method }: SIPStatusChangeParams) {
+    if (status === this.getSIPStatus) return
+
+    this._sipStatus = status
+    this._sipStatusList.push(status)
+    const logInfo = `sip status | ${status} | ${this._sipStatusList}`
+    status === SIPStatus.Terminated
+      ? this.logger.warn(logInfo)
+      : this.logger.debug(logInfo)
+
+    this.socketOrSipStatusChange(this.getSocketStatus, status)
+
+    if (error) {
+      /** 抛出 SIP 类型的错误详情 */
+      this._terminatedStatusList.push(code as string)
+      this.eventEmitAndTrack(
+        CTIEvent.OnCtiError,
+        {
+          type: CTIErrorType.SdkTerminated,
+          code,
+          msg: ExceptMessage.CommonNetworkErrorMsg,
+          method: method || 'setSipStatus'
+        },
+        {
+          error_msg: error,
+          socket_status_list: this._socketStatusList,
+          sip_status_list: this._sipStatusList,
+          cti_status_list: this._ctiStatusList
+        }
+      )
+      this.clearSocketAndSip()
+    }
+  }
+
+  /**
+   * @private socketOrSipStatusChange Socket 或 SIP 状态变化可能引起 CTI 状态变化
+   * @param {SocketStatus} socketStatus
+   * @param {SIPStatus} sipStatus
+   */
+  private socketOrSipStatusChange(
+    socketStatus: SocketStatus,
+    sipStatus: SIPStatus
+  ) {
+    if (socketStatus === SocketStatus.Ready && sipStatus === SIPStatus.Ready) {
+      this.setCTIStatus(CTIStatus.Ready)
+    }
+    if (socketStatus === SocketStatus.ReTry || sipStatus === SIPStatus.ReTry) {
+      this.setCTIStatus(CTIStatus.ReTry)
+    }
+    if (
+      socketStatus === SocketStatus.Terminated ||
+      sipStatus === SIPStatus.Terminated
+    ) {
+      this.setCTIStatus(CTIStatus.Terminated)
+    }
+  }
+
+  /**
+   * @private eventEmitAndTrack SDK 对外抛出事件并统一埋点
+   * @param {CTIEvent} eventName 事件名称
+   * @param {object} ext 事件参数
+   * @param { error } error 错误详情或错误辅助信息
+   */
+  private eventEmitAndTrack(
+    eventName: CTIEvent,
+    ext: object,
+    error?: string | object
+  ) {
+    this.logger.debug(`sdk emit | ${eventName} | ${JSON.stringify(ext)}`)
+    // 如果出现异常,则将通话状态置为结束
+    if (eventName === CTIEvent.OnCtiError) {
+      this._callStatus = CallStatus.Stopped
+    }
+    try {
+      this.emit(eventName, ext)
+      console.log(error)
+    } catch (error) {
+      this.logger.error(`业务监听 ${eventName} 事件,处理回调时报错: ${error}`)
+    }
+  }
+
+  /** @private checkIn 服务端签入,CTIStatus Ready 时自动调用,坐席状态变更 */
+  @checkCTIStatus('签入')
+  @handleApiRes()
+  private async checkIn() {
+    const res = await agentCheckIn(this._baseParams)
+    if (res.code === 0) {
+      this.eventEmitAndTrack(CTIEvent.OnInitalSuccess, {
+        vccId: this.vccId,
+        agentId: this.agentId,
+        scene: this.scene,
+        phoneNum: this._initOptions!.phoneNum,
+        sipServer: this._initOptions!.sipServer
+      })
+    }
+    return res
+  }
+
+  /** @private checkOut 服务端签出, unInit 时自动调用,坐席状态变更 */
+  // @handleApiRes()
+  // private async checkOut() {
+  //   return await agentCheckOut(this._baseParams)
+  // }
+
+  /** @private _getCtiFlowId 获取手动外呼场景需要的 ctiFlowId */
+  @handleApiRes()
+  private async getCtiFlowId() {
+    const res = await getCtiFlowId(this._baseParams)
+    const { code, data } = res
+    if (code === 0) {
+      this._baseParams.ctiFlowId = data
+      this._ctiFlowIdList.push(data)
+      setBaseOption(BaseOption.TrackParams, {
+        cti_flow_id: data,
+        cti_flow_id_list: JSON.stringify(this._ctiFlowIdList)
+      })
+    }
+    return res
+  }
+
+  /** @public setIdle 服务端置闲,坐席状态变更 */
+  @checkCTIStatus('置闲')
+  @handleApiRes()
+  public async setIdle() {
+    return await agentSetIdle(this._baseParams)
+  }
+
+  /** @public setBusy 服务端置忙,坐席状态变更 */
+  @checkCTIStatus('置忙')
+  @handleApiRes()
+  public async setBusy() {
+    return await agentSetBusy(this._baseParams)
+  }
+
+  @checkCTIStatus('人工外呼')
+  public async makeCall(params: CTIManualCallOptions) {
+    // 如果当前通话状态处于外呼开始,则不允许再次调用此方法并上报埋点
+    if (this._callStatus === CallStatus.Started) {
+      return
+    }
+    this._callStatus = CallStatus.Started
+    await this.getCtiFlowId()
+    this.playAudio(AudioName.WaitAudio)
+    return await this.serverCall(params)
+  }
+
+  @handleApiRes()
+  private async serverCall({ called, circuitUid, ext }: CTIManualCallOptions) {
+    const params = { ...this._baseParams, called, circuitUid, ...ext }
+    const res = await manualCall(params)
+    const { code, data } = res
+    if (code === 0) {
+      if (this._callId === '' && data) {
+        this._callId = data
+        setBaseOption(BaseOption.TrackParams, {
+          call_id: data
+        })
+      }
+    } else {
+      // 外呼接口调用失败将通话状态置为结束不阻碍下次外呼
+      this._callStatus = CallStatus.Stopped
+      this.stopLocalAudio()
+    }
+    return res
+  }
+
+  /** @public answer SDK SIP 接起 */
+  @checkCTIStatus('接起')
+  public answer() {
+    return new Promise<CTIRes>((resolve, reject) => {
+      this._sipUserAgent
+        ?.answer({
+          sessionDescriptionHandlerOptions: {
+            constraints: {
+              audio: true,
+              video: false
+            }
+          }
+        })
+        .then(() => {
+          resolve({
+            code: 0,
+            data: 'answer',
+            msg: 'SIP 接起电话成功'
+          })
+          this.logger.debug('sip_accept_success')
+        })
+        .catch(err => {
+          const errorData = {
+            type: CTIErrorType.SdkError,
+            code: SdkErrorCode.Answer,
+            msg:
+              this.scene === Scene.Manual
+                ? ExceptMessage.ManualCallAnswerErrorMsg
+                : ExceptMessage.RobotOrWeChatAnswerErrorMsg,
+            method: 'answer'
+          }
+          reject(errorData)
+          this.eventEmitAndTrack(CTIEvent.OnCtiError, errorData, `${err}`)
+          this.logger.error(`${CTIEvent.OnCtiError} | ${err}`)
+        })
+    })
+  }
+
+  /** @public bye SDK SIP 挂断 */
+  @checkCTIStatus('挂断')
+  public bye() {
+    return new Promise<CTIRes>((resolve, reject) => {
+      this._sipUserAgent
+        ?.hangup()
+        .then(() => {
+          resolve({
+            code: 0,
+            data: 'bye',
+            msg: 'SIP 挂断电话成功'
+          })
+          this.logger.debug('sip_bye_success')
+        })
+        .catch(err => {
+          const errorData = {
+            type: CTIErrorType.SdkError,
+            code: SdkErrorCode.Bye,
+            msg: ExceptMessage.SipByeErrorMsg,
+            method: 'bye'
+          }
+          reject(errorData)
+          if (err.message !== 'Session does not exist.') {
+            this.eventEmitAndTrack(CTIEvent.OnCtiError, errorData, `${err}`)
+          }
+          this.logger.error(`${CTIEvent.OnCtiError} | ${err}`)
+        })
+    })
+  }
+
+  /** @public serverBye 挂断且流转坐席状态 */
+  public async serverBye() {
+    try {
+      this.bye()
+      // 如果人工外呼场景下,没有flowId则不调用并上报埋点
+      if (!this._baseParams.ctiFlowId && this.scene === Scene.Manual) {
+        return
+      }
+      const res = await this.turnHang()
+      return Promise.resolve(res)
+    } catch (error) {
+      return Promise.reject(error)
+    }
+  }
+
+  /** @public turnHang 流转坐席状态-通话结束 */
+  @handleApiRes()
+  private async turnHang() {
+    const res = await manualHang({ ...this._baseParams, callId: this._callId })
+    this._callId = ''
+    return res
+  }
+
+  /** @public getAgentStatus 获取坐席状态 */
+  @handleApiRes()
+  public async getAgentStatus() {
+    return await getAgentStatus(this._baseParams)
+  }
+
+  /**
+   * @public loadAgentGroupData 监听-根据监听组 ID 获取监听组成员
+   * @param {string[]} monitorIds
+   */
+  @checkCTIStatus('获取监听组成员')
+  @handleApiRes()
+  public async loadAgentGroupData(monitorIds: string[]) {
+    return await loadAgentGroupData({ ...this._baseParams, monitorIds })
+  }
+
+  /**
+   * @public listen 监听-服务端发起监听
+   * @param {string} monitoredAgNo
+   */
+  @checkCTIStatus('坐席监听')
+  @handleApiRes()
+  public async listen(monitoredAgNo: string) {
+    const flowIdRes = await this.getCtiFlowId()
+    if (flowIdRes && flowIdRes.code !== 0) {
+      return flowIdRes
+    }
+
+    return await listen({
+      ...this._baseParams,
+      agentId: monitoredAgNo,
+      leaderAgentId: this.agentId
+    })
+  }
+
+  /**
+   * @public setActiveService 机器人外呼-签入人工组
+   * @param {string} serviceId
+   */
+  @checkCTIStatus('签入人工组')
+  @handleApiRes()
+  public async setActiveService(serviceId: string) {
+    return await setActiveServiceTask({ ...this._baseParams, serviceId })
+  }
+
+  /** @public unInit 卸载 SDK,checkOut 成功后断开 socket 和 sip 连接,并销毁 SdCTI 实例 */
+  public async unInit() {
+    // await this.checkOut()
+    this.setCTIStatus(CTIStatus.Terminated)
+    this.initInstanceOptions()
+    this.clearSocketAndSip()
+    this._callId = ''
+  }
+}

+ 218 - 0
src/hs-cti/HsSocket.ts

@@ -0,0 +1,218 @@
+import io from 'socket.io-client'
+import { setBaseOption, BaseOption } from './storage'
+import { type SocketOptions, SocketEvent } from './type'
+import { SocketStatus, HskTerminatedCode } from './outputType'
+import EventEmitter from '../eventemitter'
+import { Logger } from '../Logger'
+
+class HsSocket extends EventEmitter {
+  private logger: Logger
+  public socket?: typeof io.Socket
+  /** 初始化 socket 需要的参数 */
+  public socketOptions: SocketOptions
+  /** 心跳延迟时间 */
+  private heartBeatDelay: number
+  /** 主动关闭链接时间 */
+  private closeHeartBeatDelay: number
+  /** 心跳检测定时器 */
+  private heartBeatTimer: null | ReturnType<typeof setTimeout>
+  /** 清空心跳检测定时器 */
+  private closeHeartBeatTimer: null | ReturnType<typeof setTimeout>
+  /** 超时次数 */
+  // private timeOutCount: number
+  /** 最大超时次数限制 */
+  private imRetryCount: number
+  /** 本次 socket 会话唯一 id */
+  private sessionId: string
+
+  constructor(socketOptions: SocketOptions) {
+    super()
+
+    /** 页面关闭时 关闭 socket */
+    window.addEventListener('onunload', this.closeSocket)
+
+    this.logger = new Logger(socketOptions.loggerLevel, 'HsSocket')
+    this.socket = undefined
+    this.heartBeatTimer = null
+    this.closeHeartBeatTimer = null
+    // this.timeOutCount = 0
+    this.sessionId = ''
+
+    this.socketOptions = socketOptions
+
+    this.heartBeatDelay = socketOptions.imHeartTime * 1000 || 3000
+    this.closeHeartBeatDelay = socketOptions.imHeartTime * 1000 || 3000
+    this.imRetryCount = socketOptions.imRetryCount || 10
+  }
+  /** @public initSocket 初始化 Socket 连接 */
+  public initSocket(): void {
+    /** 如果有未断开的连接先断开 */
+    this.closeSocket()
+
+    // 设置状态为连接中
+    this.emit(SocketEvent.SetSocketStatus, {
+      status: SocketStatus.Connecting
+    })
+
+    /** https://socket.io/docs/v2/client-api/#iourl */
+    this.socket = io(this.socketOptions.imWsServer, {
+      transports: ['websocket'],
+      path: this.socketOptions.imWsPath,
+      reconnectionAttempts: this.imRetryCount,
+      reconnectionDelay: this.heartBeatDelay,
+      reconnectionDelayMax: this.heartBeatDelay + 1000,
+      timeout: this.heartBeatDelay,
+      reconnection: true
+    })
+
+    /** https://socket.io/docs/v2/client-api/#event-error */
+    this.socket.on('error', (error: object) => {
+      this.logger.error(`socket_error | ${JSON.stringify(error)}`)
+    })
+
+    /** https://socket.io/docs/v2/client-api/#event-connect_error-1 */
+    this.socket.on('connect_error', (error: object) => {
+      const errorData = `socket_connect_error | ${JSON.stringify(error)}`
+      this.logger.warn(errorData)
+    })
+
+    this.socket.on('reconnecting', (res: string) => {
+      this.logger.error(`socket_warn | socket_reconnecting | ${res}`)
+      this.emit(SocketEvent.SetSocketStatus, {
+        status: SocketStatus.ReTry
+      })
+    })
+
+    this.socket.on('reconnect', (res: string) => {
+      this.logger.error(`socket_warn | socket_reconnect | ${res}`)
+      this.emit(SocketEvent.SetSocketStatus, {
+        status: SocketStatus.Ready
+      })
+    })
+
+    this.socket.on('reconnect_failed', (error: string) => {
+      this.logger.error(`socket_warn | socket_reconnect_failed | ${error}`)
+      this.emit(SocketEvent.SetSocketStatus, {
+        status: SocketStatus.Terminated,
+        code: HskTerminatedCode.SocketOnReconnectFailed,
+        error: `${error}`
+      })
+    })
+
+    /** https://socket.io/docs/v2/client-api/#event-connect */
+    this.socket.on('connect', () => {
+      this.emit(SocketEvent.SetSocketStatus, {
+        status: SocketStatus.Connected
+      })
+      this.socketLogin()
+    })
+
+    /** https://socket.io/docs/v2/client-api/#event-disconnect */
+    this.socket.on('disconnect', (reason: string) => {
+      const errorMessage = `socket_disconnect | ${reason}`
+      this.logger.warn(errorMessage)
+    })
+
+    /** 服务端下行事件 */
+    this.socket.on('common_down_data', (e: string) => {
+      if (e && JSON.parse(e) && JSON.parse(e).data) {
+        this.emit(SocketEvent.SocketDownEvent, {
+          eventData: JSON.parse(JSON.parse(e).data)
+        })
+      }
+    })
+
+    /** 服务端下行指令 */
+    this.socket.on('common_down_cmd', (e: string) => {
+      const { clientSessionId } = JSON.parse(e)
+      if (clientSessionId === this.sessionId) {
+        this.logger.error(
+          `socket status | ${SocketStatus.Terminated} | 坐席在其他页面重新初始化,本页面被踢出`
+        )
+        this.emit(SocketEvent.SetSocketStatus, {
+          status: SocketStatus.Terminated,
+          code: HskTerminatedCode.SocketRepeatLogin,
+          error: '您已在其他页面签入,当前页面连接已断开'
+        })
+      }
+    })
+  }
+
+  /** @private socketLogin 客户端上行登录事件 */
+  private socketLogin() {
+    const data = {
+      // appCode: this.socketOptions.appCode,
+      token: this.socketOptions.token || '',
+      userId: this.socketOptions.agentId
+    }
+    this.socket &&
+      this.socket.emit('login', data, (sessionId: string) => {
+        this.emit(SocketEvent.SetSocketStatus, {
+          status: SocketStatus.Ready
+        })
+        setBaseOption(BaseOption.TrackParams, { socket_session_id: sessionId })
+        this.sessionId = sessionId
+        this.startHeartbeat()
+      })
+  }
+
+  /** @public closeSocket 关闭 socket 连接 */
+  public closeSocket() {
+    if (this.socket) {
+      this.socket.io.opts.reconnection = false
+      this.socket.close()
+      // 清除之前的监听事件
+      this.socket.removeAllListeners()
+    }
+    this.socket = undefined
+    this.sessionId = ''
+    if (this.heartBeatTimer) {
+      window.clearTimeout(this.heartBeatTimer)
+      this.heartBeatTimer = null
+    }
+    if (this.closeHeartBeatTimer) {
+      window.clearTimeout(this.closeHeartBeatTimer)
+      this.closeHeartBeatTimer = null
+    }
+  }
+
+  /** @private startHeartbeat 开启心跳检测 */
+  private startHeartbeat() {
+    if (this.heartBeatTimer) {
+      window.clearTimeout(this.heartBeatTimer)
+      this.heartBeatTimer = null
+    }
+    if (this.closeHeartBeatTimer) {
+      window.clearTimeout(this.closeHeartBeatTimer)
+      this.closeHeartBeatTimer = null
+    }
+    this.socket && this.heartbeatEvent()
+  }
+
+  /** @private heartbeatEvent websocket心跳检测 */
+  private heartbeatEvent() {
+    this.heartBeatTimer = setTimeout(() => {
+      this.socket && this.sendHeartbeat()
+      /** 如果心跳检测一直没回应,则进行重连 */
+      this.closeHeartBeatTimer = setTimeout(() => {
+        this.logger.warn('socket_heart_beat | 心跳超时,即将重新连接')
+        this.initSocket()
+      }, this.closeHeartBeatDelay)
+    }, this.heartBeatDelay)
+  }
+
+  /** @private sendHeartbeat 客户端上行心跳事件 */
+  private sendHeartbeat() {
+    this.socket &&
+      this.socket.emit(
+        'heartbeat',
+        JSON.stringify({
+          ...this.socketOptions
+        }),
+        () => {
+          this.startHeartbeat()
+        }
+      )
+  }
+}
+export default HsSocket

+ 385 - 0
src/hs-cti/SImpleUserPlus.ts

@@ -0,0 +1,385 @@
+import {
+  type InvitationAcceptOptions,
+  type InviterInviteOptions,
+  type InviterOptions,
+  type RegistererRegisterOptions,
+  type RegistererUnregisterOptions,
+  Message,
+  Core,
+  Session,
+  Invitation,
+  UserAgentState
+} from 'sip.js'
+
+import { SessionManagerPlus } from './SessionManager'
+
+import {
+  SimpleUserPlusOptions,
+  SimpleUserPlusDelegate,
+  SessionManagerPlusOptions
+} from './type'
+
+/**
+ * A simple SIP user class.
+ * @remarks
+ * While this class is completely functional for simple use cases, it is not intended
+ * to provide an interface which is suitable for most (must less all) applications.
+ * While this class has many limitations (for example, it only handles a single concurrent session),
+ * it is, however, intended to serve as a simple example of using the SIP.js API.
+ * @public
+ */
+export class SimpleUserPlus {
+  /** Delegate. */
+  public delegate: SimpleUserPlusDelegate | undefined
+
+  private logger: Core.Logger
+  private options: SimpleUserPlusOptions
+  private session: Session | undefined = undefined
+  private sessionManager: SessionManagerPlus
+
+  /**
+   * Constructs a new instance of the `SimpleUser` class.
+   * @param server - SIP WebSocket Server URL.
+   * @param options - Options bucket. See {@link SimpleUserOptions} for details.
+   */
+  constructor(server: string, options: SimpleUserPlusOptions = {}) {
+    // Delegate
+    this.delegate = options.delegate
+
+    // Copy options
+    this.options = { ...options }
+
+    // Session manager options
+    const sessionManagerOptions: SessionManagerPlusOptions = {
+      aor: this.options.aor,
+      delegate: {
+        onCallAnswered: () => this.delegate?.onCallAnswered?.(),
+        onCallCreated: (session: Session) => {
+          this.session = session
+          this.delegate?.onCallBegin?.(session)
+          this.delegate?.onCallCreated?.()
+        },
+        onCallReceived: (session: Invitation) => {
+          this.delegate?.onCallReceived?.()
+          this.delegate?.onInvite?.(session)
+        },
+        onCallHangup: () => {
+          this.session = undefined
+          this.delegate?.onCallHangup && this.delegate?.onCallHangup()
+        },
+        onCallHold: (_s: Session, held: boolean) =>
+          this.delegate?.onCallHold?.(held),
+        onCallDTMFReceived: (_s: Session, tone: string, dur: number) =>
+          this.delegate?.onCallDTMFReceived?.(tone, dur),
+        onMessageReceived: (message: Message) =>
+          this.delegate?.onMessageReceived?.(message.request.body),
+        onRegistered: () => this.delegate?.onRegistered?.(),
+        onUnregistered: () => this.delegate?.onUnregistered?.(),
+        onServerConnect: () => this.delegate?.onServerConnect?.(),
+        onServerDisconnect: () => this.delegate?.onServerDisconnect?.(),
+        onReconnectFailed: () => this.delegate?.onReconnectFailed?.(),
+        onReconnectStart: () => this.delegate?.onReconnectStart?.()
+      },
+      maxSimultaneousSessions: 1,
+      media: this.options.media,
+      optionsPingInterval: this.options.optionsPingInterval,
+      optionsPingRequestURI: this.options.aor,
+      reconnectionAttempts: this.options.reconnectionAttempts,
+      reconnectionDelay: this.options.reconnectionDelay,
+      registererOptions: this.options.registererOptions,
+      sendDTMFUsingSessionDescriptionHandler:
+        this.options.sendDTMFUsingSessionDescriptionHandler,
+      userAgentOptions: this.options.userAgentOptions
+    }
+
+    this.sessionManager = new SessionManagerPlus(server, sessionManagerOptions)
+    this.sessionManager.userAgent.stateChange.addListener(
+      (state: UserAgentState) => {
+        this.delegate?.onUserAgentStateChange?.(state)
+      }
+    )
+
+    // Use the SIP.js logger
+    this.logger = this.sessionManager.userAgent.getLogger('sip.SimpleUser')
+  }
+
+  /**
+   * Instance identifier.
+   * @internal
+   */
+  get id(): string {
+    return (
+      (this.options.userAgentOptions &&
+        this.options.userAgentOptions.displayName) ||
+      'Anonymous'
+    )
+  }
+
+  /** The local media stream. Undefined if call not answered. */
+  get localMediaStream(): MediaStream | undefined {
+    return this.session && this.sessionManager.getLocalMediaStream(this.session)
+  }
+
+  /** The remote media stream. Undefined if call not answered. */
+  get remoteMediaStream(): MediaStream | undefined {
+    return (
+      this.session && this.sessionManager.getRemoteMediaStream(this.session)
+    )
+  }
+
+  /**
+   * The local audio track, if available.
+   * @deprecated Use localMediaStream and get track from the stream.
+   */
+  get localAudioTrack(): MediaStreamTrack | undefined {
+    return this.session && this.sessionManager.getLocalAudioTrack(this.session)
+  }
+
+  /**
+   * The local video track, if available.
+   * @deprecated Use localMediaStream and get track from the stream.
+   */
+  get localVideoTrack(): MediaStreamTrack | undefined {
+    return this.session && this.sessionManager.getLocalVideoTrack(this.session)
+  }
+
+  /**
+   * The remote audio track, if available.
+   * @deprecated Use remoteMediaStream and get track from the stream.
+   */
+  get remoteAudioTrack(): MediaStreamTrack | undefined {
+    return this.session && this.sessionManager.getRemoteAudioTrack(this.session)
+  }
+
+  /**
+   * The remote video track, if available.
+   * @deprecated Use remoteMediaStream and get track from the stream.
+   */
+  get remoteVideoTrack(): MediaStreamTrack | undefined {
+    return this.session && this.sessionManager.getRemoteVideoTrack(this.session)
+  }
+
+  /**
+   * Connect.
+   * @remarks
+   * Start the UserAgent's WebSocket Transport.
+   */
+  public connect(): Promise<void> {
+    this.logger.log(`[${this.id}] Connecting UserAgent...`)
+    return this.sessionManager.connect()
+  }
+
+  /**
+   * Disconnect.
+   * @remarks
+   * Stop the UserAgent's WebSocket Transport.
+   */
+  public disconnect(): Promise<void> {
+    this.logger.log(`[${this.id}] Disconnecting UserAgent...`)
+    return this.sessionManager.disconnect()
+  }
+
+  /**
+   * Return true if connected.
+   */
+  public isConnected(): boolean {
+    return this.sessionManager.isConnected()
+  }
+
+  /**
+   * Start receiving incoming calls.
+   * @remarks
+   * Send a REGISTER request for the UserAgent's AOR.
+   * Resolves when the REGISTER request is sent, otherwise rejects.
+   */
+  public register(
+    registererRegisterOptions?: RegistererRegisterOptions
+  ): Promise<void> {
+    this.logger.log(`[${this.id}] Registering UserAgent...`)
+    return this.sessionManager.register(registererRegisterOptions)
+  }
+
+  /**
+   * Stop receiving incoming calls.
+   * @remarks
+   * Send an un-REGISTER request for the UserAgent's AOR.
+   * Resolves when the un-REGISTER request is sent, otherwise rejects.
+   */
+  public unregister(
+    registererUnregisterOptions?: RegistererUnregisterOptions
+  ): Promise<void> {
+    this.logger.log(`[${this.id}] Unregistering UserAgent...`)
+    return this.sessionManager.unregister(registererUnregisterOptions)
+  }
+
+  /**
+   * Make an outgoing call.
+   * @remarks
+   * Send an INVITE request to create a new Session.
+   * Resolves when the INVITE request is sent, otherwise rejects.
+   * Use `onCallAnswered` delegate method to determine if Session is established.
+   * @param destination - The target destination to call. A SIP address to send the INVITE to.
+   * @param inviterOptions - Optional options for Inviter constructor.
+   * @param inviterInviteOptions - Optional options for Inviter.invite().
+   */
+  public call(
+    destination: string,
+    inviterOptions?: InviterOptions,
+    inviterInviteOptions?: InviterInviteOptions
+  ): Promise<void> {
+    this.logger.log(`[${this.id}] Beginning Session...`)
+    if (this.session) {
+      return Promise.reject(new Error('Session already exists.'))
+    }
+    return this.sessionManager
+      .call(destination, inviterOptions, inviterInviteOptions)
+      .then(() => {
+        return
+      })
+  }
+
+  /**
+   * Hangup a call.
+   * @remarks
+   * Send a BYE request, CANCEL request or reject response to end the current Session.
+   * Resolves when the request/response is sent, otherwise rejects.
+   * Use `onCallHangup` delegate method to determine if and when call is ended.
+   */
+  public hangup(): Promise<void> {
+    this.logger.log(`[${this.id}] Hangup...`)
+    if (!this.session) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+    return this.sessionManager.hangup(this.session).then(() => {
+      this.session = undefined
+    })
+  }
+
+  /**
+   * Answer an incoming call.
+   * @remarks
+   * Accept an incoming INVITE request creating a new Session.
+   * Resolves with the response is sent, otherwise rejects.
+   * Use `onCallAnswered` delegate method to determine if and when call is established.
+   * @param invitationAcceptOptions - Optional options for Inviter.accept().
+   */
+  public answer(
+    invitationAcceptOptions?: InvitationAcceptOptions
+  ): Promise<void> {
+    this.logger.log(`[${this.id}] Accepting Invitation...`)
+    if (!this.session) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+    return this.sessionManager.answer(this.session, invitationAcceptOptions)
+  }
+
+  /**
+   * Decline an incoming call.
+   * @remarks
+   * Reject an incoming INVITE request.
+   * Resolves with the response is sent, otherwise rejects.
+   * Use `onCallHangup` delegate method to determine if and when call is ended.
+   */
+  public decline(): Promise<void> {
+    this.logger.log(`[${this.id}] rejecting Invitation...`)
+    if (!this.session) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+    return this.sessionManager.decline(this.session)
+  }
+
+  /**
+   * Hold call
+   * @remarks
+   * Send a re-INVITE with new offer indicating "hold".
+   * Resolves when the re-INVITE request is sent, otherwise rejects.
+   * Use `onCallHold` delegate method to determine if request is accepted or rejected.
+   * See: https://tools.ietf.org/html/rfc6337
+   */
+  public hold(): Promise<void> {
+    this.logger.log(`[${this.id}] holding session...`)
+    if (!this.session) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+    return this.sessionManager.hold(this.session)
+  }
+
+  /**
+   * Unhold call.
+   * @remarks
+   * Send a re-INVITE with new offer indicating "unhold".
+   * Resolves when the re-INVITE request is sent, otherwise rejects.
+   * Use `onCallHold` delegate method to determine if request is accepted or rejected.
+   * See: https://tools.ietf.org/html/rfc6337
+   */
+  public unhold(): Promise<void> {
+    this.logger.log(`[${this.id}] unholding session...`)
+    if (!this.session) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+    return this.sessionManager.unhold(this.session)
+  }
+
+  /**
+   * Hold state.
+   * @remarks
+   * True if session is on hold.
+   */
+  public isHeld(): boolean {
+    return this.session ? this.sessionManager.isHeld(this.session) : false
+  }
+
+  /**
+   * Mute call.
+   * @remarks
+   * Disable sender's media tracks.
+   */
+  public mute(): void {
+    this.logger.log(`[${this.id}] disabling media tracks...`)
+    return this.session && this.sessionManager.mute(this.session)
+  }
+
+  /**
+   * Unmute call.
+   * @remarks
+   * Enable sender's media tracks.
+   */
+  public unmute(): void {
+    this.logger.log(`[${this.id}] enabling media tracks...`)
+    return this.session && this.sessionManager.unmute(this.session)
+  }
+
+  /**
+   * Mute state.
+   * @remarks
+   * True if sender's media track is disabled.
+   */
+  public isMuted(): boolean {
+    return this.session ? this.sessionManager.isMuted(this.session) : false
+  }
+
+  /**
+   * Send DTMF.
+   * @remarks
+   * Send an INFO request with content type application/dtmf-relay.
+   * @param tone - Tone to send.
+   */
+  public sendDTMF(tone: string): Promise<void> {
+    this.logger.log(`[${this.id}] sending DTMF...`)
+    if (!this.session) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+    return this.sessionManager.sendDTMF(this.session, tone)
+  }
+
+  /**
+   * Send a message.
+   * @remarks
+   * Send a MESSAGE request.
+   * @param destination - The target destination for the message. A SIP address to send the MESSAGE to.
+   */
+  public message(destination: string, message: string): Promise<void> {
+    this.logger.log(`[${this.id}] sending message...`)
+    return this.sessionManager.message(destination, message)
+  }
+}

+ 1734 - 0
src/hs-cti/SessionManager.ts

@@ -0,0 +1,1734 @@
+import {
+  Info,
+  Invitation,
+  InvitationAcceptOptions,
+  Inviter,
+  InviterInviteOptions,
+  InviterOptions,
+  Message,
+  Messager,
+  Notification,
+  Referral,
+  Registerer,
+  RegistererOptions,
+  RegistererRegisterOptions,
+  RegistererState,
+  RegistererUnregisterOptions,
+  RequestPendingError,
+  Session,
+  SessionInviteOptions,
+  SessionReferOptions,
+  SessionState,
+  UserAgent,
+  UserAgentOptions,
+  UserAgentState,
+  URI,
+  Web,
+  Core
+} from 'sip.js'
+import { SessionManagerPlusDelegate, SessionManagerPlusOptions } from './type'
+
+/**
+ * A session manager for SIP.js sessions.
+ * @public
+ */
+export class SessionManagerPlus {
+  /** Delegate. */
+  public delegate: SessionManagerPlusDelegate | undefined
+
+  /** Sessions being managed. */
+  public managedSessions: Array<Web.ManagedSession> = []
+
+  /** User agent which created sessions being managed. */
+  public userAgent: UserAgent
+
+  private logger: Core.Logger
+  private options: Required<Web.SessionManagerOptions>
+  private optionsPingFailure = false
+  private optionsPingRequest?: Core.OutgoingRequest
+  private optionsPingRunning = false
+  private optionsPingTimeout?: ReturnType<typeof setTimeout>
+  private registrationAttemptTimeout?: ReturnType<typeof setTimeout>
+  private registerer?: Registerer
+  private registererOptions?: RegistererOptions
+  private registererRegisterOptions: RegistererRegisterOptions
+  private shouldBeConnected = false
+  private shouldBeRegistered = false
+  private attemptingReconnection = false
+
+  /**
+   * Constructs a new instance of the `SessionManager` class.
+   * @param server - SIP WebSocket Server URL.
+   * @param options - Options bucket. See {@link Web.SessionManagerOptions} for details.
+   */
+  constructor(server: string, options: SessionManagerPlusOptions = {}) {
+    // Delegate
+    this.delegate = options.delegate
+
+    // Copy options
+    this.options = {
+      // Defaults
+      ...{
+        aor: '',
+        autoStop: true,
+        delegate: {},
+        iceStopWaitingOnServerReflexive: false,
+        managedSessionFactory: this.managedSessionFactory,
+        maxSimultaneousSessions: 2,
+        media: {},
+        optionsPingInterval: -1,
+        optionsPingRequestURI: '',
+        reconnectionAttempts: 3,
+        reconnectionDelay: 3,
+        registrationRetry: false,
+        registrationRetryInterval: 3,
+        registerGuard: null,
+        registererOptions: {},
+        registererRegisterOptions: {},
+        sendDTMFUsingSessionDescriptionHandler: false,
+        userAgentOptions: {}
+      },
+      ...SessionManagerPlus.stripUndefinedProperties(options)
+    }
+
+    // UserAgentOptions
+    const userAgentOptions: UserAgentOptions = {
+      ...options.userAgentOptions
+    }
+
+    // Transport
+    if (!userAgentOptions.transportConstructor) {
+      userAgentOptions.transportConstructor = Web.Transport
+    }
+
+    // TransportOptions
+    if (!userAgentOptions.transportOptions) {
+      userAgentOptions.transportOptions = {
+        server
+      }
+    }
+
+    // URI
+    if (!userAgentOptions.uri) {
+      // If an AOR was provided, convert it to a URI
+      if (options.aor) {
+        const uri = UserAgent.makeURI(options.aor)
+        if (!uri) {
+          throw new Error(`Failed to create valid URI from ${options.aor}`)
+        }
+        userAgentOptions.uri = uri
+      }
+    }
+
+    // UserAgent
+    this.userAgent = new UserAgent(userAgentOptions)
+
+    // UserAgent's delegate
+    this.userAgent.delegate = {
+      // Handle connection with server established
+      onConnect: (): void => {
+        this.logger.log(`Connected`)
+        if (this.delegate && this.delegate.onServerConnect) {
+          this.delegate.onServerConnect()
+        }
+        // Attempt to register if we are supposed to be registered
+        if (this.shouldBeRegistered) {
+          this.register()
+        }
+        // Start OPTIONS pings if we are to be pinging
+        if (this.options.optionsPingInterval > 0) {
+          this.optionsPingStart()
+        }
+      },
+      // Handle connection with server lost
+      onDisconnect: async (error?: Error): Promise<void> => {
+        this.logger.log(`Disconnected`)
+
+        // Stop OPTIONS ping if need be.
+        let optionsPingFailure = false
+        if (this.options.optionsPingInterval > 0) {
+          optionsPingFailure = this.optionsPingFailure
+          this.optionsPingFailure = false
+          this.optionsPingStop()
+        }
+
+        // If the user called `disconnect` a graceful cleanup will be done therein.
+        // Only cleanup if network/server dropped the connection.
+        // Only reconnect if network/server dropped the connection
+        if (error || optionsPingFailure) {
+          // There is no transport at this point, so we are not expecting to be able to
+          // send messages much less get responses. So just dispose of everything without
+          // waiting for anything to succeed.
+          if (this.registerer) {
+            this.logger.log(`Disposing of registerer...`)
+            this.registerer.dispose().catch((e: Error) => {
+              this.logger.debug(
+                `Error occurred disposing of registerer after connection with server was lost.`
+              )
+              this.logger.debug(e.toString())
+            })
+            this.registerer = undefined
+          }
+          this.managedSessions
+            .slice()
+            .map(el => el.session)
+            .forEach(async session => {
+              this.logger.log(`Disposing of session...`)
+              session.dispose().catch((e: Error) => {
+                this.logger.debug(
+                  `Error occurred disposing of a session after connection with server was lost.`
+                )
+                this.logger.debug(e.toString())
+              })
+            })
+          // Attempt to reconnect if we are supposed to be connected.
+          if (this.shouldBeConnected) {
+            this.delegate?.onReconnectStart?.()
+            this.attemptReconnection()
+          }
+        } else {
+          // Let delgate know we have disconnected
+          this.delegate?.onServerDisconnect?.(error)
+        }
+      },
+      // Handle incoming invitations
+      onInvite: (invitation: Invitation): void => {
+        this.logger.log(`[${invitation.id}] Received INVITE`)
+
+        // Guard against a maximum number of pre-existing sessions.
+        // An incoming INVITE request may be received at any time and/or while in the process
+        // of sending an outgoing INVITE request. So we reject any incoming INVITE in those cases.
+        const maxSessions = this.options.maxSimultaneousSessions
+        if (maxSessions !== 0 && this.managedSessions.length > maxSessions) {
+          this.logger.warn(
+            `[${invitation.id}] Session already in progress, rejecting INVITE...`
+          )
+          invitation
+            .reject()
+            .then(() => {
+              this.logger.log(`[${invitation.id}] Rejected INVITE`)
+            })
+            .catch((error: Error) => {
+              this.logger.error(`[${invitation.id}] Failed to reject INVITE`)
+              this.logger.error(error.toString())
+            })
+          return
+        }
+
+        // Use our configured constraints as options for any Inviter created as result of a REFER
+        const referralInviterOptions: InviterOptions = {
+          sessionDescriptionHandlerOptions: { constraints: this.constraints }
+        }
+
+        // Initialize our session
+        this.initSession(invitation, referralInviterOptions)
+
+        // Delegate
+        if (this.delegate && this.delegate.onCallReceived) {
+          this.delegate.onCallReceived(invitation)
+        } else {
+          this.logger.warn(
+            `[${invitation.id}] No handler available, rejecting INVITE...`
+          )
+          invitation
+            .reject()
+            .then(() => {
+              this.logger.log(`[${invitation.id}] Rejected INVITE`)
+            })
+            .catch((error: Error) => {
+              this.logger.error(`[${invitation.id}] Failed to reject INVITE`)
+              this.logger.error(error.toString())
+            })
+        }
+      },
+      // Handle incoming messages
+      onMessage: (message: Message): void => {
+        message.accept().then(() => {
+          if (this.delegate && this.delegate.onMessageReceived) {
+            this.delegate.onMessageReceived(message)
+          }
+        })
+      },
+      // Handle incoming notifications
+      onNotify: (notification: Notification): void => {
+        notification.accept().then(() => {
+          if (this.delegate && this.delegate.onNotificationReceived) {
+            this.delegate.onNotificationReceived(notification)
+          }
+        })
+      }
+    }
+
+    // RegistererOptions
+    this.registererOptions = {
+      ...options.registererOptions
+    }
+
+    // RegistererRegisterOptions
+    this.registererRegisterOptions = {
+      ...options.registererRegisterOptions
+    }
+
+    // Retry registration on failure or rejection.
+    if (this.options.registrationRetry) {
+      // If the register request is rejected, try again...
+      this.registererRegisterOptions.requestDelegate =
+        this.registererRegisterOptions.requestDelegate || {}
+      const existingOnReject =
+        this.registererRegisterOptions.requestDelegate.onReject
+      this.registererRegisterOptions.requestDelegate.onReject = response => {
+        existingOnReject && existingOnReject(response)
+        // If at first we don't succeed, try try again...
+        this.attemptRegistration()
+      }
+    }
+
+    // Use the SIP.js logger
+    this.logger = this.userAgent.getLogger('sip.SessionManager')
+
+    // Monitor network connectivity and attempt reconnection and reregistration when we come online
+    window.addEventListener('online', () => {
+      this.logger.log(`Online`)
+      if (this.shouldBeConnected) {
+        this.connect()
+      }
+    })
+
+    // NOTE: The autoStop option does not currently work as one likley expects.
+    //       This code is here because the "autoStop behavior" and this assoicated
+    //       implemenation has been a recurring request. So instead of removing
+    //       the implementation again (because it doesn't work) and then having
+    //       to explain agian the issue over and over again to those who want it,
+    //       we have included it here to break that cycle. The implementation is
+    //       harmless and serves to provide an explaination for those interested.
+    if (this.options.autoStop) {
+      // Standard operation workflow will resume after this callback exits, meaning
+      // that any asynchronous operations are likely not going to be finished, especially
+      // if they are guaranteed to not be executed in the current tick (promises fall
+      // under this category, they will never be resolved synchronously by design).
+      window.addEventListener('beforeunload', async () => {
+        this.shouldBeConnected = false
+        this.shouldBeRegistered = false
+        if (this.userAgent.state !== UserAgentState.Stopped) {
+          // The stop() method returns a promise which will not resolve before the page unloads.
+          await this.userAgent.stop()
+        }
+      })
+    }
+  }
+
+  /**
+   * Strip properties with undefined values from options.
+   * This is a work around while waiting for missing vs undefined to be addressed (or not)...
+   * https://github.com/Microsoft/TypeScript/issues/13195
+   * @param options - Options to reduce
+   */
+  private static stripUndefinedProperties(
+    options: Partial<Web.SessionManagerOptions>
+  ): Partial<Web.SessionManagerOptions> {
+    return Object.keys(options).reduce((object, key) => {
+      // eslint-disable-next-line @typescript-eslint/no-explicit-any
+      if ((options as any)[key] !== undefined) {
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        ;(object as any)[key] = (options as any)[key]
+      }
+      return object
+    }, {})
+  }
+
+  /**
+   * The local media stream. Undefined if call not answered.
+   * @param session - Session to get the media stream from.
+   */
+  public getLocalMediaStream(session: Session): MediaStream | undefined {
+    const sdh = session.sessionDescriptionHandler
+    if (!sdh) {
+      return undefined
+    }
+    if (!(sdh instanceof Web.SessionDescriptionHandler)) {
+      throw new Error(
+        'Session description handler not instance of web SessionDescriptionHandler'
+      )
+    }
+    return sdh.localMediaStream
+  }
+
+  /**
+   * The remote media stream. Undefined if call not answered.
+   * @param session - Session to get the media stream from.
+   */
+  public getRemoteMediaStream(session: Session): MediaStream | undefined {
+    const sdh = session.sessionDescriptionHandler
+    if (!sdh) {
+      return undefined
+    }
+    if (!(sdh instanceof Web.SessionDescriptionHandler)) {
+      throw new Error(
+        'Session description handler not instance of web SessionDescriptionHandler'
+      )
+    }
+    return sdh.remoteMediaStream
+  }
+
+  /**
+   * The local audio track, if available.
+   * @param session - Session to get track from.
+   * @deprecated Use localMediaStream and get track from the stream.
+   */
+  public getLocalAudioTrack(session: Session): MediaStreamTrack | undefined {
+    return this.getLocalMediaStream(session)
+      ?.getTracks()
+      .find(track => track.kind === 'audio')
+  }
+
+  /**
+   * The local video track, if available.
+   * @param session - Session to get track from.
+   * @deprecated Use localMediaStream and get track from the stream.
+   */
+  public getLocalVideoTrack(session: Session): MediaStreamTrack | undefined {
+    return this.getLocalMediaStream(session)
+      ?.getTracks()
+      .find(track => track.kind === 'video')
+  }
+
+  /**
+   * The remote audio track, if available.
+   * @param session - Session to get track from.
+   * @deprecated Use remoteMediaStream and get track from the stream.
+   */
+  public getRemoteAudioTrack(session: Session): MediaStreamTrack | undefined {
+    return this.getRemoteMediaStream(session)
+      ?.getTracks()
+      .find(track => track.kind === 'audio')
+  }
+
+  /**
+   * The remote video track, if available.
+   * @param session - Session to get track from.
+   * @deprecated Use remoteMediaStream and get track from the stream.
+   */
+  public getRemoteVideoTrack(session: Session): MediaStreamTrack | undefined {
+    return this.getRemoteMediaStream(session)
+      ?.getTracks()
+      .find(track => track.kind === 'video')
+  }
+
+  /**
+   * Connect.
+   * @remarks
+   * If not started, starts the UserAgent connecting the WebSocket Transport.
+   * Otherwise reconnects the UserAgent's WebSocket Transport.
+   * Attempts will be made to reconnect as needed.
+   */
+  public async connect(): Promise<void> {
+    this.logger.log(`Connecting UserAgent...`)
+    this.shouldBeConnected = true
+    if (this.userAgent.state !== UserAgentState.Started) {
+      return this.userAgent.start()
+    }
+    return this.userAgent.reconnect()
+  }
+
+  /**
+   * Disconnect.
+   * @remarks
+   * If not stopped, stops the UserAgent disconnecting the WebSocket Transport.
+   */
+  public async disconnect(): Promise<void> {
+    this.logger.log(`Disconnecting UserAgent...`)
+    if (this.userAgent.state === UserAgentState.Stopped) {
+      return Promise.resolve()
+    }
+    this.shouldBeConnected = false
+    this.shouldBeRegistered = false
+    this.registerer = undefined
+    return this.userAgent.stop()
+  }
+
+  /**
+   * Return true if transport is connected.
+   */
+  public isConnected(): boolean {
+    return this.userAgent.isConnected()
+  }
+
+  /**
+   * Start receiving incoming calls.
+   * @remarks
+   * Send a REGISTER request for the UserAgent's AOR.
+   * Resolves when the REGISTER request is sent, otherwise rejects.
+   * Attempts will be made to re-register as needed.
+   */
+  public async register(
+    registererRegisterOptions?: RegistererRegisterOptions
+  ): Promise<void> {
+    this.logger.log(`Registering UserAgent...`)
+    this.shouldBeRegistered = true
+    if (registererRegisterOptions !== undefined) {
+      this.registererRegisterOptions = {
+        ...registererRegisterOptions
+      }
+    }
+    if (!this.registerer) {
+      this.registerer = new Registerer(this.userAgent, this.registererOptions)
+      this.registerer.stateChange.addListener((state: RegistererState) => {
+        switch (state) {
+          case RegistererState.Initial:
+            break
+          case RegistererState.Registered:
+            if (this.delegate && this.delegate.onRegistered) {
+              this.delegate.onRegistered()
+            }
+            break
+          case RegistererState.Unregistered:
+            if (this.delegate && this.delegate.onUnregistered) {
+              this.delegate.onUnregistered()
+            }
+            // If we transition to an unregister state, attempt to get back to a registered state.
+            if (this.shouldBeRegistered) {
+              this.attemptRegistration()
+            }
+            break
+          case RegistererState.Terminated:
+            break
+          default:
+            throw new Error('Unknown registerer state.')
+        }
+      })
+    }
+    return this.attemptRegistration(true)
+  }
+
+  /**
+   * Stop receiving incoming calls.
+   * @remarks
+   * Send an un-REGISTER request for the UserAgent's AOR.
+   * Resolves when the un-REGISTER request is sent, otherwise rejects.
+   */
+  public async unregister(
+    registererUnregisterOptions?: RegistererUnregisterOptions
+  ): Promise<void> {
+    this.logger.log(`Unregistering UserAgent...`)
+    this.shouldBeRegistered = false
+    if (!this.registerer) {
+      this.logger.warn(`No registerer to unregister.`)
+      return Promise.resolve()
+    }
+    return this.registerer.unregister(registererUnregisterOptions).then(() => {
+      return
+    })
+  }
+
+  /**
+   * Make an outgoing call.
+   * @remarks
+   * Send an INVITE request to create a new Session.
+   * Resolves when the INVITE request is sent, otherwise rejects.
+   * Use `onCallAnswered` delegate method to determine if Session is established.
+   * @param destination - The target destination to call. A SIP address to send the INVITE to.
+   * @param inviterOptions - Optional options for Inviter constructor.
+   * @param inviterInviteOptions - Optional options for Inviter.invite().
+   */
+  public async call(
+    destination: string,
+    inviterOptions?: InviterOptions,
+    inviterInviteOptions?: InviterInviteOptions
+  ): Promise<Inviter> {
+    this.logger.log(`Beginning Session...`)
+
+    // Guard against a maximum number of pre-existing sessions.
+    // An incoming INVITE request may be received at any time and/or while in the process
+    // of sending an outgoing INVITE request. So we reject any incoming INVITE in those cases.
+    const maxSessions = this.options.maxSimultaneousSessions
+    if (maxSessions !== 0 && this.managedSessions.length > maxSessions) {
+      return Promise.reject(
+        new Error('Maximum number of sessions already exists.')
+      )
+    }
+
+    const target = UserAgent.makeURI(destination)
+    if (!target) {
+      return Promise.reject(
+        new Error(`Failed to create a valid URI from "${destination}"`)
+      )
+    }
+
+    // Use our configured constraints as InviterOptions if none provided
+    if (!inviterOptions) {
+      inviterOptions = {}
+    }
+    if (!inviterOptions.sessionDescriptionHandlerOptions) {
+      inviterOptions.sessionDescriptionHandlerOptions = {}
+    }
+    if (!inviterOptions.sessionDescriptionHandlerOptions.constraints) {
+      inviterOptions.sessionDescriptionHandlerOptions.constraints =
+        this.constraints
+    }
+
+    // If utilizing early media, add a handler to catch 183 Session Progress
+    // messages and then to play the associated remote media (the early media).
+    if (inviterOptions.earlyMedia) {
+      inviterInviteOptions = inviterInviteOptions || {}
+      inviterInviteOptions.requestDelegate =
+        inviterInviteOptions.requestDelegate || {}
+      const existingOnProgress = inviterInviteOptions.requestDelegate.onProgress
+      inviterInviteOptions.requestDelegate.onProgress = response => {
+        if (response.message.statusCode === 183) {
+          this.setupRemoteMedia(inviter)
+        }
+        existingOnProgress && existingOnProgress(response)
+      }
+    }
+
+    // TODO: Any existing onSessionDescriptionHandler is getting clobbered here.
+    // If we get a server reflexive candidate, stop waiting on ICE gathering to complete.
+    // The candidate is a server reflexive candidate; the ip indicates an intermediary
+    // address assigned by the STUN server to represent the candidate's peer anonymously.
+    if (this.options.iceStopWaitingOnServerReflexive) {
+      inviterOptions.delegate = inviterOptions.delegate || {}
+      inviterOptions.delegate.onSessionDescriptionHandler =
+        sessionDescriptionHandler => {
+          if (
+            !(
+              sessionDescriptionHandler instanceof Web.SessionDescriptionHandler
+            )
+          ) {
+            throw new Error(
+              'Session description handler not instance of SessionDescriptionHandler'
+            )
+          }
+          sessionDescriptionHandler.peerConnectionDelegate = {
+            onicecandidate: event => {
+              if (event.candidate?.type === 'srflx') {
+                this.logger.log(
+                  `[${inviter.id}] Found srflx ICE candidate, stop waiting...`
+                )
+                // In sip.js > 0.20.1 this cast should be removed as iceGatheringComplete will be public
+                const sdh =
+                  sessionDescriptionHandler as Web.SessionDescriptionHandler & {
+                    iceGatheringComplete: () => void
+                  }
+                sdh.iceGatheringComplete()
+              }
+            }
+          }
+        }
+    }
+
+    // Create a new Inviter for the outgoing Session
+    const inviter = new Inviter(this.userAgent, target, inviterOptions)
+
+    // Send INVITE
+    return this.sendInvite(inviter, inviterOptions, inviterInviteOptions).then(
+      () => {
+        return inviter
+      }
+    )
+  }
+
+  /**
+   * Hangup a call.
+   * @param session - Session to hangup.
+   * @remarks
+   * Send a BYE request, CANCEL request or reject response to end the current Session.
+   * Resolves when the request/response is sent, otherwise rejects.
+   * Use `onCallHangup` delegate method to determine if and when call is ended.
+   */
+  public async hangup(session: Session): Promise<void> {
+    this.logger.log(`[${session.id}] Hangup...`)
+
+    if (!this.sessionExists(session)) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+
+    return this.terminate(session)
+  }
+
+  /**
+   * Answer an incoming call.
+   * @param session - Session to answer.
+   * @remarks
+   * Accept an incoming INVITE request creating a new Session.
+   * Resolves with the response is sent, otherwise rejects.
+   * Use `onCallAnswered` delegate method to determine if and when call is established.
+   * @param invitationAcceptOptions - Optional options for Inviter.accept().
+   */
+  public async answer(
+    session: Session,
+    invitationAcceptOptions?: InvitationAcceptOptions
+  ): Promise<void> {
+    this.logger.log(`[${session.id}] Accepting Invitation...`)
+
+    if (!this.sessionExists(session)) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+
+    if (!(session instanceof Invitation)) {
+      return Promise.reject(new Error('Session not instance of Invitation.'))
+    }
+
+    // Use our configured constraints as InvitationAcceptOptions if none provided
+    if (!invitationAcceptOptions) {
+      invitationAcceptOptions = {}
+    }
+    if (!invitationAcceptOptions.sessionDescriptionHandlerOptions) {
+      invitationAcceptOptions.sessionDescriptionHandlerOptions = {}
+    }
+    if (!invitationAcceptOptions.sessionDescriptionHandlerOptions.constraints) {
+      invitationAcceptOptions.sessionDescriptionHandlerOptions.constraints =
+        this.constraints
+    }
+
+    return session.accept(invitationAcceptOptions)
+  }
+
+  /**
+   * Decline an incoming call.
+   * @param session - Session to decline.
+   * @remarks
+   * Reject an incoming INVITE request.
+   * Resolves with the response is sent, otherwise rejects.
+   * Use `onCallHangup` delegate method to determine if and when call is ended.
+   */
+  public async decline(session: Session): Promise<void> {
+    this.logger.log(`[${session.id}] Rejecting Invitation...`)
+
+    if (!this.sessionExists(session)) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+
+    if (!(session instanceof Invitation)) {
+      return Promise.reject(new Error('Session not instance of Invitation.'))
+    }
+
+    return session.reject()
+  }
+
+  /**
+   * Hold call
+   * @param session - Session to hold.
+   * @remarks
+   * Send a re-INVITE with new offer indicating "hold".
+   * Resolves when the re-INVITE request is sent, otherwise rejects.
+   * Use `onCallHold` delegate method to determine if request is accepted or rejected.
+   * See: https://tools.ietf.org/html/rfc6337
+   */
+  public async hold(session: Session): Promise<void> {
+    this.logger.log(`[${session.id}] Holding session...`)
+    return this.setHold(session, true)
+  }
+
+  /**
+   * Unhold call.
+   * @param session - Session to unhold.
+   * @remarks
+   * Send a re-INVITE with new offer indicating "unhold".
+   * Resolves when the re-INVITE request is sent, otherwise rejects.
+   * Use `onCallHold` delegate method to determine if request is accepted or rejected.
+   * See: https://tools.ietf.org/html/rfc6337
+   */
+  public async unhold(session: Session): Promise<void> {
+    this.logger.log(`[${session.id}] Unholding session...`)
+    return this.setHold(session, false)
+  }
+
+  /**
+   * Hold state.
+   * @param session - Session to check.
+   * @remarks
+   * True if session is on hold.
+   */
+  public isHeld(session: Session): boolean {
+    const managedSession = this.sessionManaged(session)
+    return managedSession ? managedSession.held : false
+  }
+
+  /**
+   * Mute call.
+   * @param session - Session to mute.
+   * @remarks
+   * Disable sender's media tracks.
+   */
+  public mute(session: Session): void {
+    this.logger.log(`[${session.id}] Disabling media tracks...`)
+    this.setMute(session, true)
+  }
+
+  /**
+   * Unmute call.
+   * @param session - Session to unmute.
+   * @remarks
+   * Enable sender's media tracks.
+   */
+  public unmute(session: Session): void {
+    this.logger.log(`[${session.id}] Enabling media tracks...`)
+    this.setMute(session, false)
+  }
+
+  /**
+   * Mute state.
+   * @param session - Session to check.
+   * @remarks
+   * True if sender's media track is disabled.
+   */
+  public isMuted(session: Session): boolean {
+    const managedSession = this.sessionManaged(session)
+    return managedSession ? managedSession.muted : false
+  }
+
+  /**
+   * Send DTMF.
+   * @param session - Session to send on.
+   * @remarks
+   * Send an INFO request with content type application/dtmf-relay.
+   * @param tone - Tone to send.
+   */
+  public async sendDTMF(session: Session, tone: string): Promise<void> {
+    this.logger.log(`[${session.id}] Sending DTMF...`)
+
+    // Validate tone
+    if (!/^[0-9A-D#*,]$/.exec(tone)) {
+      return Promise.reject(new Error('Invalid DTMF tone.'))
+    }
+
+    if (!this.sessionExists(session)) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+
+    this.logger.log(`[${session.id}] Sending DTMF tone: ${tone}`)
+
+    if (this.options.sendDTMFUsingSessionDescriptionHandler) {
+      if (!session.sessionDescriptionHandler) {
+        return Promise.reject(
+          new Error('Session desciption handler undefined.')
+        )
+      }
+
+      if (!session.sessionDescriptionHandler.sendDtmf(tone)) {
+        return Promise.reject(new Error('Failed to send DTMF'))
+      }
+
+      return Promise.resolve()
+    } else {
+      // As RFC 6086 states, sending DTMF via INFO is not standardized...
+      //
+      // Companies have been using INFO messages in order to transport
+      // Dual-Tone Multi-Frequency (DTMF) tones.  All mechanisms are
+      // proprietary and have not been standardized.
+      // https://tools.ietf.org/html/rfc6086#section-2
+      //
+      // It is however widely supported based on this draft:
+      // https://tools.ietf.org/html/draft-kaplan-dispatch-info-dtmf-package-00
+
+      // The UA MUST populate the "application/dtmf-relay" body, as defined
+      // earlier, with the button pressed and the duration it was pressed
+      // for.  Technically, this actually requires the INFO to be generated
+      // when the user *releases* the button, however if the user has still
+      // not released a button after 5 seconds, which is the maximum duration
+      // supported by this mechanism, the UA should generate the INFO at that
+      // time.
+      // https://tools.ietf.org/html/draft-kaplan-dispatch-info-dtmf-package-00#section-5.3
+      const dtmf = tone
+      const duration = 2000
+      const body = {
+        contentDisposition: 'render',
+        contentType: 'application/dtmf-relay',
+        content: 'Signal=' + dtmf + '\r\nDuration=' + duration
+      }
+      const requestOptions = { body }
+
+      return session.info({ requestOptions }).then(() => {
+        return
+      })
+    }
+  }
+
+  /**
+   * Transfer.
+   * @param session - Session with the transferee to transfer.
+   * @param target - The referral target.
+   * @remarks
+   * If target is a Session this is an attended transfer completion (REFER with Replaces),
+   * otherwise this is a blind transfer (REFER). Attempting an attended transfer
+   * completion on a call that has not been answered will be rejected. To implement
+   * an attended transfer with early completion, hangup the call with the target
+   * and execute a blind transfer to the target.
+   */
+  public async transfer(
+    session: Session,
+    target: Session | string,
+    options?: SessionReferOptions
+  ): Promise<void> {
+    this.logger.log(`[${session.id}] Referring session...`)
+
+    if (target instanceof Session) {
+      return session.refer(target, options).then(() => {
+        return
+      })
+    }
+
+    const uri = UserAgent.makeURI(target)
+    if (!uri) {
+      return Promise.reject(
+        new Error(`Failed to create a valid URI from "${target}"`)
+      )
+    }
+
+    return session.refer(uri, options).then(() => {
+      return
+    })
+  }
+
+  /**
+   * Send a message.
+   * @remarks
+   * Send a MESSAGE request.
+   * @param destination - The target destination for the message. A SIP address to send the MESSAGE to.
+   */
+  public async message(destination: string, message: string): Promise<void> {
+    this.logger.log(`Sending message...`)
+
+    const target = UserAgent.makeURI(destination)
+    if (!target) {
+      return Promise.reject(
+        new Error(`Failed to create a valid URI from "${destination}"`)
+      )
+    }
+    return new Messager(this.userAgent, target, message).message()
+  }
+
+  /** Media constraints. */
+  private get constraints(): { audio: boolean; video: boolean } {
+    let constraints = { audio: true, video: false } // default to audio only calls
+    if (this.options.media.constraints) {
+      constraints = { ...this.options.media.constraints }
+    }
+    return constraints
+  }
+
+  /**
+   * Attempt reconnection up to `reconnectionAttempts` times.
+   * @param reconnectionAttempt - Current attempt number.
+   */
+  private attemptReconnection(reconnectionAttempt = 1): void {
+    const reconnectionAttempts = this.options.reconnectionAttempts
+    const reconnectionDelay = this.options.reconnectionDelay
+
+    if (!this.shouldBeConnected) {
+      this.logger.log(`Should not be connected currently`)
+      return // If intentionally disconnected, don't reconnect.
+    }
+
+    if (this.attemptingReconnection) {
+      this.logger.log(`Reconnection attempt already in progress`)
+    }
+
+    if (reconnectionAttempt > reconnectionAttempts) {
+      this.logger.log(`Reconnection maximum attempts reached`)
+      this.delegate?.onReconnectFailed?.()
+      return
+    }
+
+    if (reconnectionAttempt === 1) {
+      this.logger.log(
+        `Reconnection attempt ${reconnectionAttempt} of ${reconnectionAttempts} - trying`
+      )
+    } else {
+      this.logger.log(
+        `Reconnection attempt ${reconnectionAttempt} of ${reconnectionAttempts} - trying in ${reconnectionDelay} seconds`
+      )
+    }
+
+    this.attemptingReconnection = true
+
+    setTimeout(
+      () => {
+        if (!this.shouldBeConnected) {
+          this.logger.log(
+            `Reconnection attempt ${reconnectionAttempt} of ${reconnectionAttempts} - aborted`
+          )
+          this.attemptingReconnection = false
+          return // If intentionally disconnected, don't reconnect.
+        }
+        this.userAgent
+          .reconnect()
+          .then(() => {
+            this.logger.log(
+              `Reconnection attempt ${reconnectionAttempt} of ${reconnectionAttempts} - succeeded`
+            )
+            this.attemptingReconnection = false
+          })
+          .catch((error: Error) => {
+            this.logger.log(
+              `Reconnection attempt ${reconnectionAttempt} of ${reconnectionAttempts} - failed`
+            )
+            this.logger.error(error.message)
+            this.attemptingReconnection = false
+            this.attemptReconnection(++reconnectionAttempt)
+          })
+      },
+      reconnectionAttempt === 1 ? 0 : reconnectionDelay * 1000
+    )
+  }
+
+  /**
+   * Register to receive calls.
+   * @param withoutDelay - If true attempt immediately, otherwise wait `registrationRetryInterval`.
+   */
+  private attemptRegistration(withoutDelay = false): Promise<void> {
+    this.logger.log(
+      `Registration attempt ${withoutDelay ? 'without delay' : ''}`
+    )
+
+    if (!this.shouldBeRegistered) {
+      this.logger.log(`Should not be registered currently`)
+      return Promise.resolve()
+    }
+
+    // It only makes sense to have one attempt in progress at a time.
+    // Perhaps we shall (or should) try once again.
+    if (this.registrationAttemptTimeout !== undefined) {
+      this.logger.log(`Registration attempt already in progress`)
+      return Promise.resolve()
+    }
+
+    // Helper function to send the register request.
+    const _register = (): Promise<void> => {
+      // If we do not have a registerer, it is not worth trying to register.
+      if (!this.registerer) {
+        this.logger.log(`Registerer undefined`)
+        return Promise.resolve()
+      }
+
+      // If the WebSocket transport is not connected, it is not worth trying to register.
+      // Perhpas we shall (or should) try once we are connected.
+      if (!this.isConnected()) {
+        this.logger.log(`User agent not connected`)
+        return Promise.resolve()
+      }
+
+      // If the UserAgent is stopped, it is not worth trying to register.
+      // Perhaps we shall (or should) try once the UserAgent is running.
+      if (this.userAgent.state === UserAgentState.Stopped) {
+        this.logger.log(`User agent stopped`)
+        return Promise.resolve()
+      }
+
+      // If no guard defined, we are good to proceed without any further ado.
+      if (!this.options.registerGuard) {
+        return this.registerer
+          .register(this.registererRegisterOptions)
+          .then(() => {
+            return
+          })
+      }
+
+      // Otherwise check to make sure the guard does not want us halt.
+      return this.options
+        .registerGuard()
+        .catch(error => {
+          this.logger.log(
+            `Register guard rejected will making registration attempt`
+          )
+          throw error
+        })
+        .then(halt => {
+          if (halt || !this.registerer) {
+            return Promise.resolve()
+          }
+          return this.registerer
+            .register(this.registererRegisterOptions)
+            .then(() => {
+              return
+            })
+        })
+    }
+
+    // Compute an amount of time in seconds to wait before sending another register request.
+    // This is a small attempt to avoid DOS attacking our own backend in the event that a
+    // relatively large number of clients sychonously keep retrying register reqeusts.
+    // This is known to happen when the backend goes down for a period and all clients
+    // are attempting to register again - the backend gets slammed with synced reqeusts.
+    const computeRegistrationTimeout = (lowerBound: number): number => {
+      const upperBound = lowerBound * 2
+      return 1000 * (Math.random() * (upperBound - lowerBound) + lowerBound)
+    }
+
+    // Send register request after a delay
+    return new Promise<void>((resolve, reject) => {
+      this.registrationAttemptTimeout = setTimeout(
+        () => {
+          _register()
+            .then(() => {
+              this.registrationAttemptTimeout = undefined
+              resolve()
+            })
+            .catch(error => {
+              this.registrationAttemptTimeout = undefined
+              if (error instanceof RequestPendingError) {
+                resolve()
+              } else {
+                reject(error)
+              }
+            })
+        },
+        withoutDelay
+          ? 0
+          : computeRegistrationTimeout(this.options.registrationRetryInterval)
+      )
+    })
+  }
+
+  /** Helper function to remove media from html elements. */
+  private cleanupMedia(session: Session): void {
+    const managedSession = this.sessionManaged(session)
+    if (!managedSession) {
+      throw new Error('Managed session does not exist.')
+    }
+    if (managedSession.mediaLocal) {
+      if (managedSession.mediaLocal.video) {
+        managedSession.mediaLocal.video.srcObject = null
+        managedSession.mediaLocal.video.pause()
+      }
+    }
+    if (managedSession.mediaRemote) {
+      if (managedSession.mediaRemote.audio) {
+        managedSession.mediaRemote.audio.srcObject = null
+        managedSession.mediaRemote.audio.pause()
+      }
+      if (managedSession.mediaRemote.video) {
+        managedSession.mediaRemote.video.srcObject = null
+        managedSession.mediaRemote.video.pause()
+      }
+    }
+  }
+
+  /** Helper function to enable/disable media tracks. */
+  private enableReceiverTracks(session: Session, enable: boolean): void {
+    if (!this.sessionExists(session)) {
+      throw new Error('Session does not exist.')
+    }
+
+    const sessionDescriptionHandler = session.sessionDescriptionHandler
+    if (!(sessionDescriptionHandler instanceof Web.SessionDescriptionHandler)) {
+      throw new Error(
+        "Session's session description handler not instance of SessionDescriptionHandler."
+      )
+    }
+
+    sessionDescriptionHandler.enableReceiverTracks(enable)
+  }
+
+  /** Helper function to enable/disable media tracks. */
+  private enableSenderTracks(session: Session, enable: boolean): void {
+    if (!this.sessionExists(session)) {
+      throw new Error('Session does not exist.')
+    }
+
+    const sessionDescriptionHandler = session.sessionDescriptionHandler
+    if (!(sessionDescriptionHandler instanceof Web.SessionDescriptionHandler)) {
+      throw new Error(
+        "Session's session description handler not instance of SessionDescriptionHandler."
+      )
+    }
+
+    sessionDescriptionHandler.enableSenderTracks(enable)
+  }
+
+  /**
+   * Setup session delegate and state change handler.
+   * @param session - Session to setup.
+   * @param referralInviterOptions - Options for any Inviter created as result of a REFER.
+   */
+  private initSession(
+    session: Session,
+    referralInviterOptions?: InviterOptions
+  ): void {
+    // Add the session
+    this.sessionAdd(session)
+
+    // Call session created callback
+    if (this.delegate && this.delegate.onCallCreated) {
+      this.delegate.onCallCreated(session)
+    }
+
+    // Setup session state change handler
+    session.stateChange.addListener((state: SessionState) => {
+      this.logger.log(`[${session.id}] Session state changed to ${state}`)
+      switch (state) {
+        case SessionState.Initial:
+          break
+        case SessionState.Establishing:
+          break
+        case SessionState.Established:
+          this.setupLocalMedia(session)
+          this.setupRemoteMedia(session)
+          if (this.delegate && this.delegate.onCallAnswered) {
+            this.delegate.onCallAnswered(session)
+          }
+          break
+        case SessionState.Terminating:
+        // fall through
+        case SessionState.Terminated:
+          // This will already have executed if/when we fall
+          // through from Terminating and thus the managed
+          // session may already have been cleaned up.
+          if (this.sessionExists(session)) {
+            this.cleanupMedia(session)
+            this.sessionRemove(session)
+            if (this.delegate && this.delegate.onCallHangup) {
+              this.delegate.onCallHangup(session)
+            }
+          }
+          break
+        default:
+          throw new Error('Unknown session state.')
+      }
+    })
+
+    // TODO: Any existing onInfo or onRefer delegate gets clobbered here.
+    // Setup delegate
+    session.delegate = session.delegate || {}
+    session.delegate.onInfo = (info: Info): void => {
+      // As RFC 6086 states, sending DTMF via INFO is not standardized...
+      //
+      // Companies have been using INFO messages in order to transport
+      // Dual-Tone Multi-Frequency (DTMF) tones.  All mechanisms are
+      // proprietary and have not been standardized.
+      // https://tools.ietf.org/html/rfc6086#section-2
+      //
+      // It is however widely supported based on this draft:
+      // https://tools.ietf.org/html/draft-kaplan-dispatch-info-dtmf-package-00
+
+      // FIXME: TODO: We should reject correctly...
+      //
+      // If a UA receives an INFO request associated with an Info Package that
+      // the UA has not indicated willingness to receive, the UA MUST send a
+      // 469 (Bad Info Package) response (see Section 11.6), which contains a
+      // Recv-Info header field with Info Packages for which the UA is willing
+      // to receive INFO requests.
+      // https://tools.ietf.org/html/rfc6086#section-4.2.2
+
+      // No delegate
+      if (this.delegate?.onCallDTMFReceived === undefined) {
+        info.reject()
+        return
+      }
+
+      // Invalid content type
+      const contentType = info.request.getHeader('content-type')
+      if (!contentType || !/^application\/dtmf-relay/i.exec(contentType)) {
+        info.reject()
+        return
+      }
+
+      // Invalid body
+      const body = info.request.body.split('\r\n', 2)
+      if (body.length !== 2) {
+        info.reject()
+        return
+      }
+
+      // Invalid tone
+      let tone: string | undefined
+      const toneRegExp = /^(Signal\s*?=\s*?)([0-9A-D#*]{1})(\s)?.*/
+      if (body[0] !== undefined && toneRegExp.test(body[0])) {
+        tone = body[0].replace(toneRegExp, '$2')
+      }
+      if (!tone) {
+        info.reject()
+        return
+      }
+
+      // Invalid duration
+      let duration: number | undefined
+      const durationRegExp = /^(Duration\s?=\s?)([0-9]{1,4})(\s)?.*/
+      if (body[1] !== undefined && durationRegExp.test(body[1])) {
+        duration = parseInt(body[1].replace(durationRegExp, '$2'), 10)
+      }
+      if (!duration) {
+        info.reject()
+        return
+      }
+
+      info
+        .accept()
+        .then(() => {
+          if (this.delegate && this.delegate.onCallDTMFReceived) {
+            if (!tone || !duration) {
+              throw new Error('Tone or duration undefined.')
+            }
+            this.delegate.onCallDTMFReceived(session, tone, duration)
+          }
+        })
+        .catch((error: Error) => {
+          this.logger.error(error.message)
+        })
+    }
+    session.delegate.onRefer = (referral: Referral): void => {
+      referral
+        .accept()
+        .then(() =>
+          this.sendInvite(
+            referral.makeInviter(referralInviterOptions),
+            referralInviterOptions
+          )
+        )
+        .catch((error: Error) => {
+          this.logger.error(error.message)
+        })
+    }
+  }
+
+  /**
+   * Periodically send OPTIONS pings and disconnect when a ping fails.
+   * @param requestURI - Request URI to target
+   * @param fromURI - From URI
+   * @param toURI - To URI
+   */
+  private optionsPingRun(requestURI: URI, fromURI: URI, toURI: URI): void {
+    // Guard against nvalid interval
+    if (this.options.optionsPingInterval < 1) {
+      throw new Error('Invalid options ping interval.')
+    }
+    // Guard against sending a ping when there is one outstanading
+    if (this.optionsPingRunning) {
+      return
+    }
+    this.optionsPingRunning = true
+
+    // Setup next ping to run in future
+    this.optionsPingTimeout = setTimeout(() => {
+      this.optionsPingTimeout = undefined
+
+      // If ping succeeds...
+      const onPingSuccess = () => {
+        // record success or failure
+        this.optionsPingFailure = false
+        // if we are still running, queue up the next ping
+        if (this.optionsPingRunning) {
+          this.optionsPingRunning = false
+          this.optionsPingRun(requestURI, fromURI, toURI)
+        }
+      }
+
+      // If ping fails...
+      const onPingFailure = () => {
+        this.logger.error('OPTIONS ping failed')
+        // record success or failure
+        this.optionsPingFailure = true
+        // stop running
+        this.optionsPingRunning = false
+        // disconnect the transport
+        this.userAgent.transport
+          .disconnect()
+          .catch(error => this.logger.error(error))
+      }
+
+      // Create an OPTIONS request message
+      const core = this.userAgent.userAgentCore
+      const message = core.makeOutgoingRequestMessage(
+        'OPTIONS',
+        requestURI,
+        fromURI,
+        toURI,
+        {}
+      )
+
+      // Send the request message
+      this.optionsPingRequest = core.request(message, {
+        onAccept: () => {
+          this.optionsPingRequest = undefined
+          onPingSuccess()
+        },
+        onReject: response => {
+          this.optionsPingRequest = undefined
+          // Ping fails on following responses...
+          // - 408 Request Timeout (no response was received)
+          // - 503 Service Unavailable (a transport layer error occured)
+          if (
+            response.message.statusCode === 408 ||
+            response.message.statusCode === 503
+          ) {
+            onPingFailure()
+          } else {
+            onPingSuccess()
+          }
+        }
+      })
+    }, this.options.optionsPingInterval * 1000)
+  }
+
+  /**
+   * Start sending OPTIONS pings.
+   */
+  private optionsPingStart(): void {
+    this.logger.log(`OPTIONS pings started`)
+
+    // Create the URIs needed to send OPTIONS pings
+    let requestURI, fromURI, toURI
+    if (this.options.optionsPingRequestURI) {
+      // Use whatever specific RURI is provided.
+      requestURI = UserAgent.makeURI(this.options.optionsPingRequestURI)
+      if (!requestURI) {
+        throw new Error('Failed to create Request URI.')
+      }
+      // Use the user agent's contact URI for From and To URIs
+      fromURI = this.userAgent.contact.uri.clone()
+      toURI = this.userAgent.contact.uri.clone()
+    } else if (this.options.aor) {
+      // Otherwise use the AOR provided to target the assocated registrar server.
+      const uri = UserAgent.makeURI(this.options.aor)
+      if (!uri) {
+        throw new Error('Failed to create URI.')
+      }
+      requestURI = uri.clone()
+      requestURI.user = undefined // target the registrar server
+      fromURI = uri.clone()
+      toURI = uri.clone()
+    } else {
+      this.logger.error(
+        'You have enabled sending OPTIONS pings and as such you must provide either ' +
+          'a) an AOR to register, or b) an RURI to use for the target of the OPTIONS ping requests. '
+      )
+      return
+    }
+
+    // Send the OPTIONS pings
+    this.optionsPingRun(requestURI, fromURI, toURI)
+  }
+
+  /**
+   * Stop sending OPTIONS pings.
+   */
+  private optionsPingStop(): void {
+    this.logger.log(`OPTIONS pings stopped`)
+    this.optionsPingRunning = false
+    this.optionsPingFailure = false
+    if (this.optionsPingRequest) {
+      this.optionsPingRequest.dispose()
+      this.optionsPingRequest = undefined
+    }
+    if (this.optionsPingTimeout) {
+      clearTimeout(this.optionsPingTimeout)
+      this.optionsPingTimeout = undefined
+    }
+  }
+
+  /** Helper function to init send then send invite. */
+  private async sendInvite(
+    inviter: Inviter,
+    inviterOptions?: InviterOptions,
+    inviterInviteOptions?: InviterInviteOptions
+  ): Promise<void> {
+    // Initialize our session
+    this.initSession(inviter, inviterOptions)
+
+    // Send the INVITE
+    return inviter.invite(inviterInviteOptions).then(() => {
+      this.logger.log(`[${inviter.id}] Sent INVITE`)
+    })
+  }
+
+  private managedSessionFactory(
+    _sessionManagerPlus: SessionManagerPlus | Web.SessionManager,
+    session: Session
+  ): Web.ManagedSession {
+    return {
+      session,
+      held: false,
+      muted: false
+    }
+  }
+
+  /** Helper function to add a session to the ones we are managing. */
+  private sessionAdd(session: Session): void {
+    const managedSession = this.managedSessionFactory(this, session)
+    this.managedSessions.push(managedSession)
+  }
+
+  /** Helper function to check if the session is one we are managing. */
+  private sessionExists(session: Session): boolean {
+    return this.sessionManaged(session) !== undefined
+  }
+
+  /** Helper function to check if the session is one we are managing. */
+  private sessionManaged(session: Session): Web.ManagedSession | undefined {
+    return this.managedSessions.find(el => el.session.id === session.id)
+  }
+
+  /** Helper function to remoce a session from the ones we are managing. */
+  private sessionRemove(session: Session): void {
+    this.managedSessions = this.managedSessions.filter(
+      el => el.session.id !== session.id
+    )
+  }
+
+  /**
+   * Puts Session on hold.
+   * @param session - The session to set.
+   * @param hold - Hold on if true, off if false.
+   */
+  private async setHold(session: Session, hold: boolean): Promise<void> {
+    if (!this.sessionExists(session)) {
+      return Promise.reject(new Error('Session does not exist.'))
+    }
+
+    // Just resolve if we are already in correct state
+    if (this.isHeld(session) === hold) {
+      return Promise.resolve()
+    }
+
+    const sessionDescriptionHandler = session.sessionDescriptionHandler
+    if (!(sessionDescriptionHandler instanceof Web.SessionDescriptionHandler)) {
+      throw new Error(
+        "Session's session description handler not instance of SessionDescriptionHandler."
+      )
+    }
+
+    const options: SessionInviteOptions = {
+      requestDelegate: {
+        onAccept: (): void => {
+          const managedSession = this.sessionManaged(session)
+          if (managedSession !== undefined) {
+            managedSession.held = hold
+            this.enableReceiverTracks(session, !managedSession.held)
+            this.enableSenderTracks(
+              session,
+              !managedSession.held && !managedSession.muted
+            )
+            if (this.delegate && this.delegate.onCallHold) {
+              this.delegate.onCallHold(session, managedSession.held)
+            }
+          }
+        },
+        onReject: (): void => {
+          this.logger.warn(`[${session.id}] Re-invite request was rejected`)
+          const managedSession = this.sessionManaged(session)
+          if (managedSession !== undefined) {
+            managedSession.held = !hold // this was preemptively set so undo on failure
+            this.enableReceiverTracks(session, !managedSession.held)
+            this.enableSenderTracks(
+              session,
+              !managedSession.held && !managedSession.muted
+            )
+            if (this.delegate && this.delegate.onCallHold) {
+              this.delegate.onCallHold(session, managedSession.held)
+            }
+          }
+        }
+      }
+    }
+
+    // Session properties used to pass options to the SessionDescriptionHandler:
+    //
+    // 1) Session.sessionDescriptionHandlerOptions
+    //    SDH options for the initial INVITE transaction.
+    //    - Used in all cases when handling the initial INVITE transaction as either UAC or UAS.
+    //    - May be set directly at anytime.
+    //    - May optionally be set via constructor option.
+    //    - May optionally be set via options passed to Inviter.invite() or Invitation.accept().
+    //
+    // 2) Session.sessionDescriptionHandlerOptionsReInvite
+    //    SDH options for re-INVITE transactions.
+    //    - Used in all cases when handling a re-INVITE transaction as either UAC or UAS.
+    //    - May be set directly at anytime.
+    //    - May optionally be set via constructor option.
+    //    - May optionally be set via options passed to Session.invite().
+
+    const sessionDescriptionHandlerOptions =
+      session.sessionDescriptionHandlerOptionsReInvite as Web.SessionDescriptionHandlerOptions
+    sessionDescriptionHandlerOptions.hold = hold
+    session.sessionDescriptionHandlerOptionsReInvite =
+      sessionDescriptionHandlerOptions
+
+    // Preemptively and optimistically set held state (but do not call delegate).
+    const managedSession = this.sessionManaged(session)
+    if (!managedSession) {
+      throw new Error('Managed session is undefiend.')
+    }
+    managedSession.held = hold
+
+    // Send re-INVITE
+    return session
+      .invite(options)
+      .then(() => {
+        // Preemptively enable/disable tracks
+        const managedSession = this.sessionManaged(session)
+        if (managedSession !== undefined) {
+          this.enableReceiverTracks(session, !managedSession.held)
+          this.enableSenderTracks(
+            session,
+            !managedSession.held && !managedSession.muted
+          )
+        }
+      })
+      .catch((error: Error) => {
+        managedSession.held = !hold // was preemptively set so undo on failure
+        if (error instanceof RequestPendingError) {
+          this.logger.error(
+            `[${session.id}] A hold request is already in progress.`
+          )
+        }
+        throw error
+      })
+  }
+
+  /**
+   * Puts Session on mute.
+   * @param session - The session to mute.
+   * @param mute - Mute on if true, off if false.
+   */
+  private setMute(session: Session, mute: boolean): void {
+    if (!this.sessionExists(session)) {
+      this.logger.warn(
+        `[${session.id}] A session is required to enabled/disable media tracks`
+      )
+      return
+    }
+
+    if (session.state !== SessionState.Established) {
+      this.logger.warn(
+        `[${session.id}] An established session is required to enable/disable media tracks`
+      )
+      return
+    }
+
+    const managedSession = this.sessionManaged(session)
+    if (managedSession !== undefined) {
+      managedSession.muted = mute
+      this.enableSenderTracks(
+        session,
+        !managedSession.held && !managedSession.muted
+      )
+    }
+  }
+
+  /** Helper function to attach local media to html elements. */
+  private setupLocalMedia(session: Session): void {
+    const managedSession = this.sessionManaged(session)
+    if (!managedSession) {
+      throw new Error('Managed session does not exist.')
+    }
+
+    // Get the local media element, if any, from the and configuraiton options
+    // and save the info with the managed session so we can clean it up later.
+    const mediaLocal =
+      typeof this.options.media.local === 'function'
+        ? this.options.media.local(session)
+        : this.options.media.local
+    managedSession.mediaLocal = mediaLocal
+    const mediaElement = mediaLocal?.video
+
+    if (mediaElement) {
+      const localStream = this.getLocalMediaStream(session)
+      if (!localStream) {
+        throw new Error('Local media stream undefiend.')
+      }
+
+      mediaElement.srcObject = localStream
+      mediaElement.volume = 0
+      mediaElement.play().catch((error: Error) => {
+        this.logger.error(`[${session.id}] Failed to play local media`)
+        this.logger.error(error.message)
+      })
+    }
+  }
+
+  /** Helper function to attach remote media to html elements. */
+  private setupRemoteMedia(session: Session): void {
+    const managedSession = this.sessionManaged(session)
+    if (!managedSession) {
+      throw new Error('Managed session does not exist.')
+    }
+
+    // Get the remote media element, if any, from the and configuraiton options
+    // and save the info with the managed session so we can clean it up later.
+    const mediaRemote =
+      typeof this.options.media.remote === 'function'
+        ? this.options.media.remote(session)
+        : this.options.media.remote
+    managedSession.mediaRemote = mediaRemote
+    const mediaElement = mediaRemote?.video || mediaRemote?.audio
+
+    if (mediaElement) {
+      const remoteStream = this.getRemoteMediaStream(session)
+      if (!remoteStream) {
+        throw new Error('Remote media stream undefiend.')
+      }
+
+      mediaElement.autoplay = true // Safari hack, because you cannot call .play() from a non user action
+      mediaElement.srcObject = remoteStream
+      mediaElement.play().catch((error: Error) => {
+        this.logger.error(`[${session.id}] Failed to play remote media`)
+        this.logger.error(error.message)
+      })
+
+      remoteStream.onaddtrack = (): void => {
+        this.logger.log(`Remote media onaddtrack`)
+        mediaElement.load() // Safari hack, as it doesn't work otheriwse
+        mediaElement.play().catch((error: Error) => {
+          this.logger.error(`[${session.id}] Failed to play remote media`)
+          this.logger.error(error.message)
+        })
+      }
+    }
+  }
+
+  /**
+   * End a session.
+   * @param session - The session to terminate.
+   * @remarks
+   * Send a BYE request, CANCEL request or reject response to end the current Session.
+   * Resolves when the request/response is sent, otherwise rejects.
+   * Use `onCallHangup` delegate method to determine if and when Session is terminated.
+   */
+  private async terminate(session: Session): Promise<void> {
+    this.logger.log(`[${session.id}] Terminating...`)
+
+    switch (session.state) {
+      case SessionState.Initial:
+        if (session instanceof Inviter) {
+          return session.cancel().then(() => {
+            this.logger.log(
+              `[${session.id}] Inviter never sent INVITE (canceled)`
+            )
+          })
+        } else if (session instanceof Invitation) {
+          return session.reject().then(() => {
+            this.logger.log(`[${session.id}] Invitation rejected (sent 480)`)
+          })
+        } else {
+          throw new Error('Unknown session type.')
+        }
+      case SessionState.Establishing:
+        if (session instanceof Inviter) {
+          return session.cancel().then(() => {
+            this.logger.log(`[${session.id}] Inviter canceled (sent CANCEL)`)
+          })
+        } else if (session instanceof Invitation) {
+          return session.reject().then(() => {
+            this.logger.log(`[${session.id}] Invitation rejected (sent 480)`)
+          })
+        } else {
+          throw new Error('Unknown session type.')
+        }
+      case SessionState.Established:
+        return session.bye().then(() => {
+          this.logger.log(`[${session.id}] Session ended (sent BYE)`)
+        })
+      case SessionState.Terminating:
+        break
+      case SessionState.Terminated:
+        break
+      default:
+        throw new Error('Unknown state')
+    }
+
+    this.logger.log(
+      `[${session.id}] Terminating in state ${session.state}, no action taken`
+    )
+    return Promise.resolve()
+  }
+}

+ 2 - 0
src/hs-cti/index.dts.ts

@@ -0,0 +1,2 @@
+export * from './index'
+export * from './HsCTI'

+ 13 - 0
src/hs-cti/index.ts

@@ -0,0 +1,13 @@
+import { HsCTI } from './HsCTI'
+import type { HsCTIInitOptions, RequiredHsCTIInitOptions } from './outputType'
+
+export * from './outputType'
+export * from '../Logger'
+
+/**
+ * @function getInstance 获取 HsCTI 的实例
+ * @param HsCTIInitOptions 初始化 HsCTI 的配置
+ */
+export const getInstance = (
+  HsCTIInitOptions: HsCTIInitOptions | RequiredHsCTIInitOptions
+) => HsCTI.getInstance(HsCTIInitOptions)

+ 275 - 0
src/hs-cti/outputType.ts

@@ -0,0 +1,275 @@
+import { LoggerLevels } from '../Logger'
+/**
+ * 默认 'test'
+ * 'development': 本地(不上报服埋点只打印 log)
+ * 'test': 测试
+ * 'production': 生产
+ */
+export type ENV = 'development' | 'test' | 'production'
+/**
+ * @enum {string} CTI 初始化场景
+ * Manual: 手动外呼
+ * Robot: 机器人外呼
+ * Monitor: 监听
+ * Predictive: 预测式外呼
+ */
+export enum Scene {
+  Manual = 'manual',
+  Robot = 'robot',
+  Monitor = 'monitor',
+  Predictive = 'predictive'
+}
+
+// type ExcludeSceneKey = Exclude<keyof typeof Scene, 'Monitor'>
+export type ExcludeScene = Scene.Manual | Scene.Robot | Scene.Predictive
+
+/**
+ * @enum {string} CTI 监听场景
+ * All: 不区分场景
+ * Manual: 手动外呼
+ * Robot: 机器人外呼
+ * Predictive: 预测式外呼
+ */
+export enum MonitorScene {
+  All = 'all',
+  Manual = 'manual',
+  Robot = 'robot',
+  Predictive = 'predictive'
+}
+
+/**
+ * @interface {} 初始化 SD_CTI 的基本参数
+ * agentId: 坐席 ID
+ * vccId: 主体标识,例如 'hongshan'
+ */
+export interface BaseOptions {
+  agentId: string
+  vccId: string
+}
+
+export interface BaseHsCTIInitOptions extends BaseOptions {
+  loggerLevel?: LoggerLevels
+  password: string
+  env: ENV
+}
+
+export interface HsCTIInitOptions extends BaseHsCTIInitOptions {
+  scene: ExcludeScene
+}
+
+export interface RequiredHsCTIInitOptions extends BaseHsCTIInitOptions {
+  scene: Scene.Monitor
+  monitorScene: MonitorScene
+}
+
+/**
+ * @interface {} 初始化 SD_CTI 需要的参数
+ * loggerLevel: 日志等级
+ * password: 临时的鉴权字符串,由业务方写死传进来
+ * scene: CTI 初始化场景
+ * monitorScene: 监听场景
+ * env: 环境变量
+ */
+
+/**
+ * @enum {string} Socket 状态
+ * Initial: 初始状态
+ * Connecting: Socket 开始建立连接
+ * Connected: Socket 建立连接成功
+ * Ready: 向 IM 发送第一个上行 login 消息收到成功回调
+ * Terminated: Socket 连接断开、各种 Socket 错误流转到本状态
+ */
+export enum SocketStatus {
+  Initial = 'Initial',
+  Connecting = 'Connecting',
+  Connected = 'Connected',
+  Ready = 'Ready',
+  ReTry = 'ReTry',
+  Terminated = 'Terminated'
+}
+
+/**
+ * @enum {string} SIP 状态
+ * Initial: 初始状态
+ * Started: SIP 的 User Agent 创建成功
+ * Connecting: SIP 底层 Socket 传输 TransportState.Connecting
+ * Connected: SIP 底层 Socket 传输 TransportState.Connected
+ * Ready: SIP Registerer 监听注册状态 RegistererState.Registered
+ * Terminated: SIP Socket 断开、注册失败等各种错误、主动断开连接流转到本状态
+ */
+export enum SIPStatus {
+  Initial = 'Initial',
+  Started = 'Started',
+  Connecting = 'Connecting',
+  Connected = 'Connected',
+  Ready = 'Ready',
+  ReTry = 'ReTry',
+  Terminated = 'Terminated'
+}
+
+/**
+ * @enum {string} CTI 状态
+ * Initial: 初始状态
+ * Ready: SocketStatus Ready && SIPStatus Ready
+ * Terminated: SocketStatus Terminated || SIPStatus Terminated || 正常调用 unInit 方法卸载
+ */
+export enum CTIStatus {
+  Initial = 'Initial',
+  Ready = 'Ready',
+  ReTry = 'ReTry',
+  Terminated = 'Terminated'
+}
+
+/**
+ * @enum {string} 通话状态
+ * 为了防止人工外呼方法被二次调用引发预期以外的问题,增加此状态的流转
+ * Started: 外呼已开始,此状态下不允许再次发起外呼
+ * Stopped: 外呼已结束,此状态下可以再次发起外呼
+ */
+export enum CallStatus {
+  Started = 'Started',
+  Stopped = 'Stopped'
+}
+
+/**
+ * @enum {string} Session 状态
+ * CTI 目前只有 Invitation(接受会话)的场景,Inviter (主动发起会话)暂时没有
+ */
+export enum SessionStatus {
+  /**
+   * If `Inviter`, INVITE not sent yet.
+   * If `Invitation`, SDK 收到 INVITE 通话请求,但尚未处理.
+   */
+  Initial = 'Initial',
+  /**
+   * If `Inviter`, sent INVITE and waiting for a final response.
+   * If `Invitation`, received INVITE and attempting to send 200 final response (but has not sent it yet).
+   */
+  Establishing = 'Establishing',
+  /**
+   * If `Inviter`, sent INVITE and received 200 final response and sent ACK.
+   * If `Invitation`, SDK 完成接受 INVITE 并发送 200 OK 确认接起,同时接通本地语音流.
+   */
+  Established = 'Established',
+  /**
+   * If `Inviter`, sent INVITE, sent CANCEL and now waiting for 487 final response to ACK (or 200 to ACK & BYE).
+   * If `Invitation`, received INVITE, sent 200 final response and now waiting on ACK and upon receipt will attempt BYE
+   * (as the protocol specification requires, before sending a BYE we must receive the ACK - so we are waiting).
+   */
+  Terminating = 'Terminating',
+  /**
+   * If `Inviter`, sent INVITE and received non-200 final response (or sent/received BYE after receiving 200).
+   * If `Invitation`, SDK 收到 BYE 信令,发送 200 OK 挂断确认,会话结束.
+   */
+  Terminated = 'Terminated'
+}
+
+/**
+ * @enum {string} CTI 所有错误的分类
+ * SdkTerminated: SDK 不可用错误,需要重新初始化
+ * SdkError: SDK 状态可用,其他普通错误
+ * ServerTerminated: 服务端不可用错误,需要重新初始化,透传服务端 code,msg
+ * ServerError: 服务端可用,普通错误,透传服务端 code,msg
+ */
+export enum CTIErrorType {
+  SdkTerminated = 'SdkTerminated',
+  SdkError = 'SdkError',
+  ServerTerminated = 'ServerTerminated',
+  ServerError = 'ServerError'
+}
+
+/**
+ * @enum {string} SdkTerminated 类型错误的 code 枚举
+ * CTITerminated: SDK 状态不可用,CTIStatus 的状态为 Terminated
+ * GetUserMedia: 获取坐席媒体权限失败
+ * GetInitConfig: 调接口获取 CTI 初始化配置失败
+ * SocketOnError: 监听 socket.io 的 error 事件
+ * SocketOnConnectError: 监听 socket.io 的 connect_error 事件
+ * SocketOnDisconnect: 监听 socket.io 的 disconnect 事件
+ * SocketRepeatLogin: 多页面重复登录,IM 互踢事件
+ * SIPInitUserAgent: SIP UserAgent 初始化时启动失败
+ * SIPInitRegister: SIP Register 初始化时注册失败
+ * SIPUserAgentStateStopped: 监听 SIP UserAgent stateChange 事件状态变更为 Stopped
+ * SIPTransportStateDisconnected: 监听 SIP Transport StateChange 事件状态变更为 Disconnect
+ * SIPRegistererStateTerminated: 监听 SIP Registerer StateChange 事件状态变更为 Terminated
+ * SIPOnDisconnect: 监听 SIP OnDisconnect 事件收到异常退出 error
+ * SIPInitTransport: SIP Transport 初始化时连接失败
+ * SipHeartBeatErr: SIP发送心跳OPTIONS事件时收到异常结果
+ * SIPUnRegistered: 注册SIP时失败
+ * SocketOnReconnectFailed: Socket重连超过阈值且依然重连失败
+ */
+export enum HskTerminatedCode {
+  CTITerminated = '100001',
+  GetUserMedia = '100002',
+  GetInitConfig = '100003',
+  SocketOnError = '110001',
+  SocketOnConnectError = '110002',
+  SocketOnDisconnect = '110003',
+  SocketRepeatLogin = '110004',
+  SocketOnReconnectFailed = '110007',
+  SIPInitUserAgent = '120001',
+  SIPInitRegister = '120002',
+  SIPUserAgentStateStopped = '120003',
+  SIPTransportStateDisconnected = '120004',
+  SIPRegistererStateTerminated = '120005',
+  SIPOnDisconnect = '120006',
+  SIPInitTransport = '120007',
+  SipHeartBeatErr = '120008',
+  SIPUnRegistered = '120009'
+}
+
+/**
+ * @enum {string} SdkError 类型错误的 code 枚举
+ * Answer: SIP accept 接起失败
+ * Bye: SIP bye 挂断失败
+ * InvitationCancel: SIP Invitation 会话请求被取消
+ * AssignStream: 播放语音流失败
+ * FetchError: 修饰器handleApiRes当进入到catch时上报此code
+ */
+export enum SdkErrorCode {
+  Answer = '200001',
+  Bye = '200002',
+  InvitationCancel = '200003',
+  AssignStream = '200004',
+  FetchError = '200005'
+}
+
+/**
+ * @enum {string} CTI 事件推送
+ * OnCtiError: CTI 错误事件,含前后端所有错误,SDK 推送
+ * OnSessionStatusChange: 坐席侧 SIP 会话状态变更事件,SDK 推送
+ * OnInitalSuccess: CTI 初始化成功事件,SDK 推送
+ * OnAgentWorkReport: 坐席&用户状态变更事件,Server 推送
+ * OnRingStart: 手动外呼用户未接听时,开始播放回铃音,Server 推送
+ * OnRingEnd: 手动外呼用户未接听时,播放回铃音结束,Server 推送
+ * OnAgentReport: 坐席状态变更事件,Server 推送
+ * OnCallReportInfo: 通话时长及通话次数等信息,Server 推送
+ * OnDetectedTone: 服务端收到音频信号后推送
+ */
+export enum CTIEvent {
+  OnCtiError = 'OnCtiError',
+  OnSessionStatusChange = 'OnSessionStatusChange',
+  OnInitalSuccess = 'OnInitalSuccess',
+  OnAgentWorkReport = 'OnAgentWorkReport',
+  OnRingStart = 'OnRingStart',
+  OnRingEnd = 'OnRingEnd',
+  OnDetectedTone = 'OnDetectedTone',
+  OnAgentReport = 'OnAgentReport',
+  OnCallReportInfo = 'OnCallReportInfo',
+  //  TODO: 后 7 个事件服务端未来不再推送时删掉
+  OnCallRing = 'OnCallRing',
+  OnCallEnd = 'OnCallEnd',
+  OnCallAnswer = 'OnCallAnswer',
+  OnAgentGroupQuery = 'OnAgentGroupQuery',
+  OnMethodResponseEvent = 'OnMethodResponseEvent',
+  OnEventPrompt = 'OnEventPrompt',
+  OnPrompt = 'OnPrompt'
+}
+
+/** @interface CTIRes CTI 对外方法暴露 promise 结果 */
+export interface CTIRes {
+  code: number
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  data: any
+  msg: string
+}

+ 55 - 0
src/hs-cti/storage.ts

@@ -0,0 +1,55 @@
+import { LoggerLevels } from '../Logger'
+import type { ENV } from './outputType'
+
+export enum BaseOption {
+  TrackParams = 'trackParams',
+  ENV = 'env',
+  LoggerLevel = 'loggerLevel'
+}
+
+const items = [
+  [BaseOption.TrackParams, {}],
+  [BaseOption.ENV, 'test'],
+  [BaseOption.LoggerLevel, LoggerLevels.debug]
+]
+
+const baseOption = new Map()
+export const resetBaseOption = () => {
+  items.forEach((i, v) => baseOption.set(i, v))
+}
+resetBaseOption()
+
+export const setBaseOption = (
+  key: BaseOption,
+  value: object | ENV | LoggerLevels,
+  isInit: boolean = false
+) => {
+  if (!isInit && typeof value === 'object') {
+    baseOption.set(key, {
+      ...baseOption.get(key),
+      ...value
+    })
+  } else {
+    baseOption.set(key, value)
+  }
+}
+
+export const getBaseOption = (key: BaseOption) => {
+  return baseOption.get(key)
+}
+export const generateUUID = () => {
+  return 'xxxxxxxxxxxx4xxx-yxxx-xxxxxxxxxxxx'
+    .replace(/[xy]/g, function (c) {
+      const r = (Math.random() * 16) | 0
+      const v = c === 'x' ? r : (r & 0x3) | 0x8
+      return v.toString(16)
+    })
+    .replace(/-/g, '')
+}
+
+export const getClientId = () => {
+  if (!window.HS_CTI_CLIENT_ID) {
+    window.HS_CTI_CLIENT_ID = generateUUID()
+  }
+  return window.HS_CTI_CLIENT_ID
+}

+ 244 - 0
src/hs-cti/tools.ts

@@ -0,0 +1,244 @@
+import type { Logger } from '../Logger'
+import {
+  TrackSource,
+  baseRequireParams,
+  allRequiredParams,
+  methodExceptMsgMap
+} from './type'
+import {
+  CTIStatus,
+  CTIEvent,
+  CTIErrorType,
+  HskTerminatedCode,
+  type CTIRes,
+  type HsCTIInitOptions,
+  type RequiredHsCTIInitOptions,
+  Scene,
+  SdkErrorCode
+} from './outputType'
+
+/**
+ * @function getServerErrorType 根据服务端返回的 code 生成错误类型
+ * @param {number} code
+ * @returns {CTIErrorType}
+ */
+export function getServerErrorType(code: number) {
+  if (code >= 300001 && code <= 399999) return CTIErrorType.ServerTerminated
+  else return CTIErrorType.ServerError
+}
+
+/** @function getUserMedia 获取媒体权限 */
+export function getUserMedia() {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  return function (_target: unknown, _methodName: string, descriptor: any) {
+    const originalMethod = descriptor.value
+
+    descriptor.value = function (...args: unknown[]) {
+      if (
+        navigator &&
+        navigator.mediaDevices &&
+        navigator.mediaDevices.getUserMedia
+      ) {
+        navigator.mediaDevices
+          .getUserMedia({
+            audio: true,
+            video: false
+          })
+          .then(() => {
+            this.logger.debug('media | getUserMedia | 获取浏览器媒体权限成功')
+
+            return originalMethod.apply(this, args)
+          })
+          .catch(error => {
+            this.logger.error(`media | getUserMedia | ${error}`)
+
+            this.eventEmitAndTrack(
+              CTIEvent.OnCtiError,
+              {
+                type: CTIErrorType.SdkTerminated,
+                code: HskTerminatedCode.GetUserMedia,
+                msg:
+                  error.name === 'NotAllowedError'
+                    ? '用户拒绝了获取麦克风权限!'
+                    : '获取麦克风权限失败',
+                method: 'getUserMedia'
+              },
+              {
+                source: TrackSource.FeMedia,
+                event_name: 'get_user_media_error',
+                error: error.name
+              }
+            )
+          })
+      } else {
+        this.logger.error(
+          'media | getUserMedia | 浏览器版本过低,不支持获取媒体权限'
+        )
+        this.eventEmitAndTrack(
+          CTIEvent.OnCtiError,
+          {
+            type: CTIErrorType.SdkTerminated,
+            code: HskTerminatedCode.GetUserMedia,
+            msg: '浏览器版本过低,不支持获取媒体权限',
+            method: 'getUserMedia'
+          },
+          {
+            source: TrackSource.FeMedia,
+            event_name: 'get_user_media_not_support'
+          }
+        )
+      }
+    }
+
+    return descriptor
+  }
+}
+
+/**
+ * @function assignStreamError 语音流播放错误
+ * @param {string} type
+ * @param {Error} error
+ * @param {Logger} logger
+ */
+function assignStreamError(type: string, error: Error, logger: Logger) {
+  const err = `Failed to ${type} media: ${error}`
+  logger.error(err)
+}
+
+/**
+ * @function assignStream Assign a MediaStream to an HTMLMediaElement and update if tracks change
+ * @param {MediaStream} stream
+ * @param {HTMLMediaElement} element
+ * @param {Logger} logger
+ */
+export function assignStream(
+  stream: MediaStream,
+  element: HTMLMediaElement,
+  logger: Logger
+): void {
+  /** Set element source. */
+  element.autoplay = true
+  element.srcObject = stream
+
+  logger.log(`media | 把语音流接入到本地播放`)
+
+  /** Load and start playback of media. */
+  element.play().catch((error: Error) => {
+    assignStreamError('play', error, logger)
+  })
+
+  /** If a track is added, load and restart playback of media. */
+  stream.onaddtrack = (): void => {
+    element.load()
+    element.play().catch((error: Error) => {
+      assignStreamError('onaddtrack', error, logger)
+    })
+  }
+
+  /** If a track is removed, load and restart playback of media. */
+  stream.onremovetrack = (): void => {
+    element.load()
+    element.play().catch((error: Error) => {
+      assignStreamError('onremovetrack', error, logger)
+    })
+  }
+}
+
+/**
+ * @function checkCTIStatus 校验 cti 状态
+ * @param {string} msg
+ */
+export function checkCTIStatus(msg: string) {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  return function (_target: unknown, methodName: string, descriptor: any) {
+    const originalMethod = descriptor.value
+
+    descriptor.value = function (...args: unknown[]) {
+      /** 调用 SDK method */
+      this.logger.log(`sdk method | ${methodName} | ${msg}`)
+
+      /** 校验 CTIStatus, 如果不是 Ready 状态,直接报错 */
+      if (this.getCTIStatus !== CTIStatus.Ready) {
+        const errorData = {
+          type: CTIErrorType.SdkTerminated,
+          code: HskTerminatedCode.CTITerminated,
+          msg: methodExceptMsgMap[methodName],
+          method: methodName,
+          terminated_source: this._terminatedStatusList
+        }
+        this.eventEmitAndTrack(CTIEvent.OnCtiError, errorData)
+        return Promise.reject(errorData)
+      }
+
+      return originalMethod.apply(this, args)
+    }
+
+    return descriptor
+  }
+}
+/** @function handleApiRes 统一处理服务端接口返回值 */
+export function handleApiRes() {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  return function (_target: unknown, methodName: string, descriptor: any) {
+    const originalMethod = descriptor.value
+    descriptor.value = async function (...args: unknown[]) {
+      try {
+        const res: CTIRes = await originalMethod.apply(this, args)
+        const { code, msg } = res
+        if (code !== 0) {
+          const serverErrorType = getServerErrorType(code)
+          const errorData = {
+            type: serverErrorType,
+            code,
+            msg: msg,
+            method: methodName
+          }
+          this.eventEmitAndTrack(CTIEvent.OnCtiError, errorData)
+          if (serverErrorType === CTIErrorType.ServerTerminated) {
+            this.setCTIStatus(CTIStatus.Terminated)
+          }
+          return Promise.reject(errorData)
+        } else {
+          return Promise.resolve(res)
+        }
+      } catch (error) {
+        const errorData = {
+          type: 'fetch_error',
+          code: SdkErrorCode.FetchError,
+          msg: JSON.stringify(error),
+          method: methodName
+        }
+        this.eventEmitAndTrack(CTIEvent.OnCtiError, errorData)
+        return Promise.reject(errorData)
+      }
+    }
+
+    return descriptor
+  }
+}
+
+export function validateParams() {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  return function (_target: unknown, _methodName: string, descriptor: any) {
+    const originalMethod = descriptor.value
+    descriptor.value = function (
+      ...args: HsCTIInitOptions[] | RequiredHsCTIInitOptions[]
+    ) {
+      // eslint-disable-next-line @typescript-eslint/no-explicit-any
+      const currentParams: { [key: string]: any } = args[0]
+      let requireList = []
+      if (currentParams.scene === Scene.Monitor) {
+        requireList = allRequiredParams
+      } else {
+        requireList = baseRequireParams
+      }
+      requireList.forEach((param: string) => {
+        if (!currentParams[param]) {
+          throw `参数[${param}]为必填参数`
+        }
+      })
+      return originalMethod.apply(this, args)
+    }
+    return descriptor
+  }
+}

+ 182 - 0
src/hs-cti/type.ts

@@ -0,0 +1,182 @@
+import { Invitation, Session, UserAgentState } from 'sip.js'
+import { LoggerLevels } from '../Logger'
+import {
+  SocketStatus,
+  SIPStatus,
+  CTIEvent,
+  type BaseOptions,
+  type HsCTIInitOptions,
+  type RequiredHsCTIInitOptions
+} from './outputType'
+import {
+  SimpleUserDelegate,
+  SimpleUserOptions,
+  SessionManagerDelegate,
+  SessionManagerOptions
+} from 'sip.js/lib/platform/web'
+
+/**
+ * @enum {string} 本地提示音枚举
+ * RingAudio: 来电振铃提示音
+ * WaitAudio: 主动外呼等待音
+ * ByeAudio: 挂断提示音
+ */
+export enum AudioName {
+  RingAudio = '_ringAudio',
+  WaitAudio = '_waitAudio',
+  ByeAudio = '_byeAudio'
+}
+
+/**
+ * @interface {} 初始化 Socket 需要的参数
+ * ctiSessionId: CTI 后端生成的单次注册唯一 ID
+ * token: 用户 token(一般来说token是必传,一期没有统一的登陆鉴权流程,先不传,后期再完善)
+ * imHeartTime: 心跳延迟时间 单位秒
+ * imRetryCount: 心跳可以重试的次数
+ * loggerLevel: 日志等级
+ * imWsServer: IM websocket url
+ * imWsPath: IM websocket path
+ */
+export interface SocketOptions extends BaseOptions {
+  ctiSessionId: string | undefined
+  token?: string
+  imHeartTime: number
+  imRetryCount: number
+  loggerLevel: LoggerLevels
+  imWsServer: string
+  imWsPath: string
+}
+
+export interface BaseTrackParams {
+  agent_id: string
+  vcc_id: string
+  scene: string
+  monitor_scene?: string
+}
+export interface CtiFlowIdTrackParams {
+  cti_flow_id: string
+  cti_flow_id_list: string
+}
+
+export interface InitConfigTrackParams {
+  sip_server: string
+  cti_session_id: string
+}
+export interface MakeCallTrackParams {
+  call_id?: string
+  called?: string
+  circuit_uid?: string
+}
+
+export interface SocketBaseTrackParams {
+  socket_session_id?: string
+}
+
+export interface SocketStatusChangeParams {
+  status: SocketStatus
+  code?: string
+  error?: string
+}
+
+export interface SIPStatusChangeParams {
+  status: SIPStatus
+  code?: string
+  error?: string
+  method?: string
+}
+
+export interface SimpleUserPlusOptions extends SimpleUserOptions {
+  delegate?: SimpleUserPlusDelegate
+  optionsPingInterval?: number
+}
+
+export interface SimpleUserPlusDelegate extends SimpleUserDelegate {
+  onCallBegin?(session: Session): void
+  onInvite?(invitation: Invitation): void
+  onUserAgentStateChange?(state: UserAgentState): void
+  onReconnectFailed?(): void
+  onReconnectStart?(): void
+}
+
+export interface SessionManagerPlusOptions extends SessionManagerOptions {
+  delegate?: SessionManagerPlusDelegate
+}
+
+export interface SessionManagerPlusDelegate extends SessionManagerDelegate {
+  onReconnectFailed?(): void
+  onReconnectStart?(): void
+}
+
+/**
+ * @enum {string} 埋点上报的分类
+ * FeSocket: Socket 类暴露的事件
+ * FeSIP: SIP 相关事件
+ * FeMethod: 业务调用 SDK 暴露的方法
+ * FeAPI: SDK 调用后端的接口返回值
+ * FeAPIError: SDK 调用后端的接口错误返回值
+ * FeIMDown: 后端通过 IM 返回的常规事件
+ * FeIMCmd: IM 返回的断开指令
+ * FeMedia: SDK 媒体事件
+ * FeStatu: SDK 是否状态流转事件
+ * FeEmit: SDK 抛出的事件
+ */
+export enum TrackSource {
+  FeSocket = 'fe-socket',
+  FeSIP = 'fe-sip',
+  FeMethod = 'fe-method',
+  FeAPI = 'fe-api',
+  FeAPIError = 'fe-api-error',
+  FeIMDown = 'fe-im-down',
+  FeIMCmd = 'fe-im-cmd',
+  FeMedia = 'fe-media',
+  FeStatus = 'fe-status',
+  FeEmit = 'fe-emit'
+}
+
+export interface CTIEventParams {
+  eventName: CTIEvent
+  ext: { [key: string]: string }
+}
+
+export enum SocketEvent {
+  SetSocketStatus = 'SetSocketStatus',
+  SocketDownEvent = 'SocketDownEvent'
+}
+
+export enum ExceptMessage {
+  CommonNetworkErrorMsg = '对不起,网络状况暂时不佳,请刷新后重试。',
+  CustomNetworkErrorMsg = '对不起,连接失败,请刷新后重试。如果问题持续出现,请联系我们的技术人员。感谢您的理解和支持。',
+  ManualCallAnswerErrorMsg = '当前电话未接通,通话已结束,请重试。',
+  RobotOrWeChatAnswerErrorMsg = '对不起,由于用户挂机等原因,当前电话未接通,请等待下一通电话。',
+  SipByeErrorMsg = '请稍等,正在挂断。',
+  CTIRepeatLoginMsg = '当前坐席已被其他终端替代。'
+}
+
+export const methodExceptMsgMap: { [key: string]: ExceptMessage } = {
+  checkIn: ExceptMessage.CommonNetworkErrorMsg,
+  setIdle: ExceptMessage.CustomNetworkErrorMsg,
+  setBusy: ExceptMessage.CommonNetworkErrorMsg,
+  makeCall: ExceptMessage.CommonNetworkErrorMsg,
+  answer: ExceptMessage.CommonNetworkErrorMsg,
+  bye: ExceptMessage.CommonNetworkErrorMsg,
+  loadAgentGroupData: ExceptMessage.CommonNetworkErrorMsg,
+  listen: ExceptMessage.CommonNetworkErrorMsg,
+  setActiveService: ExceptMessage.CustomNetworkErrorMsg
+}
+
+export const baseRequireParams: (keyof HsCTIInitOptions)[] = [
+  'agentId',
+  'vccId',
+  'password',
+  'env',
+  'scene'
+]
+
+const monitorRequireParams: (keyof RequiredHsCTIInitOptions)[] = [
+  'monitorScene'
+]
+
+export const allRequiredParams: (keyof RequiredHsCTIInitOptions)[] = [
+  ...baseRequireParams,
+  ...monitorRequireParams
+]

+ 87 - 0
src/sdk.html

@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+  <title>JsSIP + WebRTC + freeSWITCH</title>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <meta name="Author" content="foruok" />
+  <meta name="description" content="JsSIP based example web application." />
+
+  <script src="https://static.fuxicarbon.com/hs-cti/socket.io.min.js"></script>
+  <script src="https://static.fuxicarbon.com/hs-cti/SIP.min.js"></script>
+  <script src="hs-cti.es6.umd.js"></script>
+
+  <style type="text/css">
+  </style>
+</head>
+
+
+<body>
+
+  <div id="login-page"
+    style="width: 424px; height: 260px; background-color: #f2f4f4; border: 1px solid grey; padding-top: 4px">
+    <table border="0" frame="void" width="418px">
+      <tr>
+        <td class="td_label" width="160px" align="right"><label for="sip_uri">SIP URI:</label></td>
+        <td width="258px"><input style="width:250px" id="sip_uri" type="text" placeholder="sip:1001@pbx.fuxicarbon.com"
+            value="sip:1001@pbx.fuxicarbon.com" /></td>
+      </tr>
+      <tr>
+        <td class="td_label" align="right"><label for="sip_password">SIP Password:</label></td>
+        <td><input style="width:250px" id="sip_password" type="password" placeholder="SIP password"
+            value="slibra@#123456" /></td>
+      </tr>
+      <tr>
+        <td class="td_label" align="right"><label for="ws_uri">WSS URI:</label></td>
+        <td><input style="width:250px" id="ws_uri" class="last unset" type="text"
+            placeholder="WSS URI (i.e: wss://example.com)" value="wss://pbx.fuxicarbon.com:7443" /></td>
+      </tr>
+      <tr>
+        <td class="td_label" align="right"><label class="input_label" for="sip_phone_number">SIP Phone Info:</label>
+        </td>
+        <td><input style="width:250px" id="sip_phone_number" type="text"
+            placeholder="sip:13269511810@pbx.fuxicarbon.com" value="sip:13269511810@pbx.fuxicarbon.com"></td>
+      </tr>
+      <tr>
+        <td colspan="2" align="center"><button onclick="testStart()"> Initialize </button></td>
+      </tr>
+      <tr>
+        <td colspan="2" align="center"><button onclick="testCall()"> Call1 </button></td>
+      </tr>
+      <tr>
+        <td colspan="2" align="center"><button onclick="captureLocalMedia()"> Capture Local Media</button></td>
+      </tr>
+    </table>
+  </div>
+
+  <div
+    style="width: 424px; height: 324px;background-color: #333333; border: 2px solid blue; padding:0px; margin-top: 4px;">
+    <video id="localVideoView" width="420px" height="320px" autoplay></video>
+  </div>
+  <div
+    style="width: 424px; height: 324px;background-color: #333333; border: 2px solid blue; padding:0px; margin-top: 4px;">
+    <video id="remoteVideoView" width="420px" height="320px" autoplay></video>
+  </div>
+
+</body>
+<script type="text/javascript">
+const { Scene, getInstance, LoggerLevels } = window.HS_CTI
+const HS_CTI = getInstance({
+    // 真实的坐席 ID
+    agentId:'hongshan',
+    // 目前每个业务线写死一个值,用于业务线区分
+    vccId: 'jms',
+    // 目前每个业务线写死,用于后端鉴权,前端仅透传
+    password: '123456',
+    // 业务场景详见 Scene 枚举,
+    scene: Scene.Manual,
+    // SDK 日志等级
+    loggerLevel: LoggerLevels.debug,
+    // 环境变量
+    env: 'development',
+    monitorScene:  Scene.Manual
+})
+// HS_CTI.init()
+</script>
+
+</html>

+ 30 - 0
src/utils.ts

@@ -0,0 +1,30 @@
+/** 将小驼峰式字符串转换为小写下划线格式
+ * @param {string}
+ * @returns {string}
+ */
+export function upperCamelToLowerSnake(s: string): string {
+  s = s.replace(/\s/g, '')
+  s = s.replace(/([A-Z])/g, '_$1')
+  s = s.toLowerCase()
+  s = s.replace(/^_/, '')
+  s = s.replace(/_$/, '')
+  return s
+}
+/**
+ * 获取时间戳字符串
+ * @returns {string} 年-月-日 时:分:秒.毫秒
+ */
+export const getDateTime = () => {
+  const timestamp = Date.now()
+  const date = new Date(timestamp)
+
+  const year = date.getFullYear()
+  const month = String(date.getMonth() + 1).padStart(2, '0')
+  const day = String(date.getDate()).padStart(2, '0')
+  const hours = String(date.getHours()).padStart(2, '0')
+  const minutes = String(date.getMinutes()).padStart(2, '0')
+  const seconds = String(date.getSeconds()).padStart(2, '0')
+  const milliseconds = String(date.getMilliseconds()).padStart(3, '0')
+  const formattedDateTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`
+  return formattedDateTime
+}

+ 7 - 0
tsconfig.es5.json

@@ -0,0 +1,7 @@
+// tsconfig.es5.json
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "target": "es5"
+  }
+}

+ 7 - 0
tsconfig.es6.json

@@ -0,0 +1,7 @@
+// tsconfig.es6.json
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "target": "es6"
+  }
+}

+ 29 - 0
tsconfig.json

@@ -0,0 +1,29 @@
+{
+  "include": ["**/*.ts", "**/*.d.ts", "**/*.tsx", "**/*.vue"],
+  "exclude": ["dist", "cdn", "node_modules"],
+  "allowJS": true,
+  "compilerOptions": {
+    "outDir": "dist",
+    "baseUrl": ".",
+    "target": "es5",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "moduleResolution": "Node",
+    "strict": true,
+    "jsx": "preserve",
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "lib": ["ESNext", "DOM"],
+    "skipLibCheck": true,
+    "noEmit": true,
+    "allowSyntheticDefaultImports": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "paths": {
+      "@/*": ["./src/*"]
+    },
+    "types": ["node"],
+    "experimentalDecorators": true,
+    "allowJs": true
+  }
+}

+ 5020 - 0
yarn.lock

@@ -0,0 +1,5020 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@ampproject/remapping@^2.2.0":
+  "integrity" "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="
+  "resolved" "https://registry.npmmirror.com/@ampproject/remapping/-/remapping-2.3.0.tgz"
+  "version" "2.3.0"
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.5"
+    "@jridgewell/trace-mapping" "^0.3.24"
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.5", "@babel/code-frame@^7.24.7":
+  "integrity" "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA=="
+  "resolved" "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/highlight" "^7.24.7"
+    "picocolors" "^1.0.0"
+
+"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.2", "@babel/compat-data@^7.25.4":
+  "integrity" "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.25.4.tgz"
+  "version" "7.25.4"
+
+"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.12.0", "@babel/core@^7.13.0", "@babel/core@^7.22.11", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0":
+  "integrity" "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA=="
+  "resolved" "https://registry.npmmirror.com/@babel/core/-/core-7.25.2.tgz"
+  "version" "7.25.2"
+  dependencies:
+    "@ampproject/remapping" "^2.2.0"
+    "@babel/code-frame" "^7.24.7"
+    "@babel/generator" "^7.25.0"
+    "@babel/helper-compilation-targets" "^7.25.2"
+    "@babel/helper-module-transforms" "^7.25.2"
+    "@babel/helpers" "^7.25.0"
+    "@babel/parser" "^7.25.0"
+    "@babel/template" "^7.25.0"
+    "@babel/traverse" "^7.25.2"
+    "@babel/types" "^7.25.2"
+    "convert-source-map" "^2.0.0"
+    "debug" "^4.1.0"
+    "gensync" "^1.0.0-beta.2"
+    "json5" "^2.2.3"
+    "semver" "^6.3.1"
+
+"@babel/generator@^7.25.0", "@babel/generator@^7.25.4":
+  "integrity" "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w=="
+  "resolved" "https://registry.npmmirror.com/@babel/generator/-/generator-7.25.5.tgz"
+  "version" "7.25.5"
+  dependencies:
+    "@babel/types" "^7.25.4"
+    "@jridgewell/gen-mapping" "^0.3.5"
+    "@jridgewell/trace-mapping" "^0.3.25"
+    "jsesc" "^2.5.1"
+
+"@babel/helper-annotate-as-pure@^7.24.7":
+  "integrity" "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/types" "^7.24.7"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.24.7":
+  "integrity" "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/traverse" "^7.24.7"
+    "@babel/types" "^7.24.7"
+
+"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.24.7", "@babel/helper-compilation-targets@^7.24.8", "@babel/helper-compilation-targets@^7.25.2":
+  "integrity" "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz"
+  "version" "7.25.2"
+  dependencies:
+    "@babel/compat-data" "^7.25.2"
+    "@babel/helper-validator-option" "^7.24.8"
+    "browserslist" "^4.23.1"
+    "lru-cache" "^5.1.1"
+    "semver" "^6.3.1"
+
+"@babel/helper-create-class-features-plugin@^7.24.7", "@babel/helper-create-class-features-plugin@^7.25.0", "@babel/helper-create-class-features-plugin@^7.25.4":
+  "integrity" "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.24.7"
+    "@babel/helper-member-expression-to-functions" "^7.24.8"
+    "@babel/helper-optimise-call-expression" "^7.24.7"
+    "@babel/helper-replace-supers" "^7.25.0"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7"
+    "@babel/traverse" "^7.25.4"
+    "semver" "^6.3.1"
+
+"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.24.7", "@babel/helper-create-regexp-features-plugin@^7.25.0", "@babel/helper-create-regexp-features-plugin@^7.25.2":
+  "integrity" "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz"
+  "version" "7.25.2"
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.24.7"
+    "regexpu-core" "^5.3.1"
+    "semver" "^6.3.1"
+
+"@babel/helper-define-polyfill-provider@^0.6.2":
+  "integrity" "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz"
+  "version" "0.6.2"
+  dependencies:
+    "@babel/helper-compilation-targets" "^7.22.6"
+    "@babel/helper-plugin-utils" "^7.22.5"
+    "debug" "^4.1.1"
+    "lodash.debounce" "^4.0.8"
+    "resolve" "^1.14.2"
+
+"@babel/helper-member-expression-to-functions@^7.24.8":
+  "integrity" "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz"
+  "version" "7.24.8"
+  dependencies:
+    "@babel/traverse" "^7.24.8"
+    "@babel/types" "^7.24.8"
+
+"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.24.7":
+  "integrity" "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/traverse" "^7.24.7"
+    "@babel/types" "^7.24.7"
+
+"@babel/helper-module-transforms@^7.24.7", "@babel/helper-module-transforms@^7.24.8", "@babel/helper-module-transforms@^7.25.0", "@babel/helper-module-transforms@^7.25.2":
+  "integrity" "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz"
+  "version" "7.25.2"
+  dependencies:
+    "@babel/helper-module-imports" "^7.24.7"
+    "@babel/helper-simple-access" "^7.24.7"
+    "@babel/helper-validator-identifier" "^7.24.7"
+    "@babel/traverse" "^7.25.2"
+
+"@babel/helper-optimise-call-expression@^7.24.7":
+  "integrity" "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/types" "^7.24.7"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.24.8", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+  "integrity" "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz"
+  "version" "7.24.8"
+
+"@babel/helper-remap-async-to-generator@^7.24.7", "@babel/helper-remap-async-to-generator@^7.25.0":
+  "integrity" "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.24.7"
+    "@babel/helper-wrap-function" "^7.25.0"
+    "@babel/traverse" "^7.25.0"
+
+"@babel/helper-replace-supers@^7.24.7", "@babel/helper-replace-supers@^7.25.0":
+  "integrity" "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/helper-member-expression-to-functions" "^7.24.8"
+    "@babel/helper-optimise-call-expression" "^7.24.7"
+    "@babel/traverse" "^7.25.0"
+
+"@babel/helper-simple-access@^7.24.7":
+  "integrity" "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/traverse" "^7.24.7"
+    "@babel/types" "^7.24.7"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.24.7":
+  "integrity" "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/traverse" "^7.24.7"
+    "@babel/types" "^7.24.7"
+
+"@babel/helper-string-parser@^7.24.8":
+  "integrity" "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz"
+  "version" "7.24.8"
+
+"@babel/helper-validator-identifier@^7.24.7":
+  "integrity" "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz"
+  "version" "7.24.7"
+
+"@babel/helper-validator-option@^7.24.7", "@babel/helper-validator-option@^7.24.8":
+  "integrity" "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz"
+  "version" "7.24.8"
+
+"@babel/helper-wrap-function@^7.25.0":
+  "integrity" "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/template" "^7.25.0"
+    "@babel/traverse" "^7.25.0"
+    "@babel/types" "^7.25.0"
+
+"@babel/helpers@^7.25.0":
+  "integrity" "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw=="
+  "resolved" "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/template" "^7.25.0"
+    "@babel/types" "^7.25.0"
+
+"@babel/highlight@^7.24.7":
+  "integrity" "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw=="
+  "resolved" "https://registry.npmmirror.com/@babel/highlight/-/highlight-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.24.7"
+    "chalk" "^2.4.2"
+    "js-tokens" "^4.0.0"
+    "picocolors" "^1.0.0"
+
+"@babel/parser@^7.25.0", "@babel/parser@^7.25.4":
+  "integrity" "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA=="
+  "resolved" "https://registry.npmmirror.com/@babel/parser/-/parser-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/types" "^7.25.4"
+
+"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.3":
+  "integrity" "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz"
+  "version" "7.25.3"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/traverse" "^7.25.3"
+
+"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.0":
+  "integrity" "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.0":
+  "integrity" "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.24.7":
+  "integrity" "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7"
+    "@babel/plugin-transform-optional-chaining" "^7.24.7"
+
+"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.0":
+  "integrity" "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/traverse" "^7.25.0"
+
+"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2":
+  "integrity" "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz"
+  "version" "7.21.0-placeholder-for-preset-env.2"
+
+"@babel/plugin-syntax-async-generators@^7.8.4":
+  "integrity" "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz"
+  "version" "7.8.4"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.12.13":
+  "integrity" "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz"
+  "version" "7.12.13"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.12.13"
+
+"@babel/plugin-syntax-class-static-block@^7.14.5":
+  "integrity" "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz"
+  "version" "7.14.5"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.3":
+  "integrity" "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz"
+  "version" "7.8.3"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-export-namespace-from@^7.8.3":
+  "integrity" "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz"
+  "version" "7.8.3"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-import-assertions@^7.24.7":
+  "integrity" "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-syntax-import-attributes@^7.24.7":
+  "integrity" "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-syntax-import-meta@^7.10.4":
+  "integrity" "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz"
+  "version" "7.10.4"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-json-strings@^7.8.3":
+  "integrity" "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz"
+  "version" "7.8.3"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-jsx@^7.24.7":
+  "integrity" "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
+  "integrity" "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz"
+  "version" "7.10.4"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3":
+  "integrity" "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz"
+  "version" "7.8.3"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.10.4":
+  "integrity" "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz"
+  "version" "7.10.4"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.3":
+  "integrity" "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz"
+  "version" "7.8.3"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.3":
+  "integrity" "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz"
+  "version" "7.8.3"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.3":
+  "integrity" "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz"
+  "version" "7.8.3"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-private-property-in-object@^7.14.5":
+  "integrity" "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz"
+  "version" "7.14.5"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-top-level-await@^7.14.5":
+  "integrity" "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz"
+  "version" "7.14.5"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-typescript@^7.24.7":
+  "integrity" "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-syntax-unicode-sets-regex@^7.18.6":
+  "integrity" "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz"
+  "version" "7.18.6"
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.18.6"
+    "@babel/helper-plugin-utils" "^7.18.6"
+
+"@babel/plugin-transform-arrow-functions@^7.24.7":
+  "integrity" "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-async-generator-functions@^7.25.4":
+  "integrity" "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/helper-remap-async-to-generator" "^7.25.0"
+    "@babel/plugin-syntax-async-generators" "^7.8.4"
+    "@babel/traverse" "^7.25.4"
+
+"@babel/plugin-transform-async-to-generator@^7.24.7":
+  "integrity" "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-module-imports" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/helper-remap-async-to-generator" "^7.24.7"
+
+"@babel/plugin-transform-block-scoped-functions@^7.24.7":
+  "integrity" "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-block-scoping@^7.25.0":
+  "integrity" "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-transform-class-properties@^7.25.4":
+  "integrity" "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.25.4"
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-transform-class-static-block@^7.24.7":
+  "integrity" "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-class-static-block" "^7.14.5"
+
+"@babel/plugin-transform-classes@^7.25.4":
+  "integrity" "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.24.7"
+    "@babel/helper-compilation-targets" "^7.25.2"
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/helper-replace-supers" "^7.25.0"
+    "@babel/traverse" "^7.25.4"
+    "globals" "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.24.7":
+  "integrity" "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/template" "^7.24.7"
+
+"@babel/plugin-transform-destructuring@^7.24.8":
+  "integrity" "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz"
+  "version" "7.24.8"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-transform-dotall-regex@^7.24.7":
+  "integrity" "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-duplicate-keys@^7.24.7":
+  "integrity" "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.0":
+  "integrity" "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.0"
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-transform-dynamic-import@^7.24.7":
+  "integrity" "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+
+"@babel/plugin-transform-exponentiation-operator@^7.24.7":
+  "integrity" "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-export-namespace-from@^7.24.7":
+  "integrity" "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+
+"@babel/plugin-transform-for-of@^7.24.7":
+  "integrity" "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7"
+
+"@babel/plugin-transform-function-name@^7.25.1":
+  "integrity" "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz"
+  "version" "7.25.1"
+  dependencies:
+    "@babel/helper-compilation-targets" "^7.24.8"
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/traverse" "^7.25.1"
+
+"@babel/plugin-transform-json-strings@^7.24.7":
+  "integrity" "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-json-strings" "^7.8.3"
+
+"@babel/plugin-transform-literals@^7.25.2":
+  "integrity" "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz"
+  "version" "7.25.2"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-transform-logical-assignment-operators@^7.24.7":
+  "integrity" "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
+"@babel/plugin-transform-member-expression-literals@^7.24.7":
+  "integrity" "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-modules-amd@^7.24.7":
+  "integrity" "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-module-transforms" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-modules-commonjs@^7.24.7", "@babel/plugin-transform-modules-commonjs@^7.24.8":
+  "integrity" "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz"
+  "version" "7.24.8"
+  dependencies:
+    "@babel/helper-module-transforms" "^7.24.8"
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/helper-simple-access" "^7.24.7"
+
+"@babel/plugin-transform-modules-systemjs@^7.25.0":
+  "integrity" "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/helper-module-transforms" "^7.25.0"
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/helper-validator-identifier" "^7.24.7"
+    "@babel/traverse" "^7.25.0"
+
+"@babel/plugin-transform-modules-umd@^7.24.7":
+  "integrity" "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-module-transforms" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.24.7":
+  "integrity" "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-new-target@^7.24.7":
+  "integrity" "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-nullish-coalescing-operator@^7.24.7":
+  "integrity" "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+
+"@babel/plugin-transform-numeric-separator@^7.24.7":
+  "integrity" "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
+"@babel/plugin-transform-object-rest-spread@^7.24.7":
+  "integrity" "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-compilation-targets" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+    "@babel/plugin-transform-parameters" "^7.24.7"
+
+"@babel/plugin-transform-object-super@^7.24.7":
+  "integrity" "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/helper-replace-supers" "^7.24.7"
+
+"@babel/plugin-transform-optional-catch-binding@^7.24.7":
+  "integrity" "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+
+"@babel/plugin-transform-optional-chaining@^7.24.7", "@babel/plugin-transform-optional-chaining@^7.24.8":
+  "integrity" "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz"
+  "version" "7.24.8"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
+"@babel/plugin-transform-parameters@^7.24.7":
+  "integrity" "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-private-methods@^7.25.4":
+  "integrity" "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.25.4"
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-transform-private-property-in-object@^7.24.7":
+  "integrity" "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.24.7"
+    "@babel/helper-create-class-features-plugin" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+
+"@babel/plugin-transform-property-literals@^7.24.7":
+  "integrity" "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-regenerator@^7.24.7":
+  "integrity" "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "regenerator-transform" "^0.15.2"
+
+"@babel/plugin-transform-reserved-words@^7.24.7":
+  "integrity" "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-shorthand-properties@^7.24.7":
+  "integrity" "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-spread@^7.24.7":
+  "integrity" "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7"
+
+"@babel/plugin-transform-sticky-regex@^7.24.7":
+  "integrity" "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-template-literals@^7.24.7":
+  "integrity" "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-typeof-symbol@^7.24.8":
+  "integrity" "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz"
+  "version" "7.24.8"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/plugin-transform-typescript@^7.24.7":
+  "integrity" "sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.2.tgz"
+  "version" "7.25.2"
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.24.7"
+    "@babel/helper-create-class-features-plugin" "^7.25.0"
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7"
+    "@babel/plugin-syntax-typescript" "^7.24.7"
+
+"@babel/plugin-transform-unicode-escapes@^7.24.7":
+  "integrity" "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-unicode-property-regex@^7.24.7":
+  "integrity" "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-unicode-regex@^7.24.7":
+  "integrity" "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.24.7"
+    "@babel/helper-plugin-utils" "^7.24.7"
+
+"@babel/plugin-transform-unicode-sets-regex@^7.25.4":
+  "integrity" "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA=="
+  "resolved" "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.25.2"
+    "@babel/helper-plugin-utils" "^7.24.8"
+
+"@babel/preset-env@^7.22.10":
+  "integrity" "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw=="
+  "resolved" "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/compat-data" "^7.25.4"
+    "@babel/helper-compilation-targets" "^7.25.2"
+    "@babel/helper-plugin-utils" "^7.24.8"
+    "@babel/helper-validator-option" "^7.24.8"
+    "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.3"
+    "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.0"
+    "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.0"
+    "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.24.7"
+    "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.0"
+    "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2"
+    "@babel/plugin-syntax-async-generators" "^7.8.4"
+    "@babel/plugin-syntax-class-properties" "^7.12.13"
+    "@babel/plugin-syntax-class-static-block" "^7.14.5"
+    "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+    "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+    "@babel/plugin-syntax-import-assertions" "^7.24.7"
+    "@babel/plugin-syntax-import-attributes" "^7.24.7"
+    "@babel/plugin-syntax-import-meta" "^7.10.4"
+    "@babel/plugin-syntax-json-strings" "^7.8.3"
+    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+    "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+    "@babel/plugin-syntax-top-level-await" "^7.14.5"
+    "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6"
+    "@babel/plugin-transform-arrow-functions" "^7.24.7"
+    "@babel/plugin-transform-async-generator-functions" "^7.25.4"
+    "@babel/plugin-transform-async-to-generator" "^7.24.7"
+    "@babel/plugin-transform-block-scoped-functions" "^7.24.7"
+    "@babel/plugin-transform-block-scoping" "^7.25.0"
+    "@babel/plugin-transform-class-properties" "^7.25.4"
+    "@babel/plugin-transform-class-static-block" "^7.24.7"
+    "@babel/plugin-transform-classes" "^7.25.4"
+    "@babel/plugin-transform-computed-properties" "^7.24.7"
+    "@babel/plugin-transform-destructuring" "^7.24.8"
+    "@babel/plugin-transform-dotall-regex" "^7.24.7"
+    "@babel/plugin-transform-duplicate-keys" "^7.24.7"
+    "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.0"
+    "@babel/plugin-transform-dynamic-import" "^7.24.7"
+    "@babel/plugin-transform-exponentiation-operator" "^7.24.7"
+    "@babel/plugin-transform-export-namespace-from" "^7.24.7"
+    "@babel/plugin-transform-for-of" "^7.24.7"
+    "@babel/plugin-transform-function-name" "^7.25.1"
+    "@babel/plugin-transform-json-strings" "^7.24.7"
+    "@babel/plugin-transform-literals" "^7.25.2"
+    "@babel/plugin-transform-logical-assignment-operators" "^7.24.7"
+    "@babel/plugin-transform-member-expression-literals" "^7.24.7"
+    "@babel/plugin-transform-modules-amd" "^7.24.7"
+    "@babel/plugin-transform-modules-commonjs" "^7.24.8"
+    "@babel/plugin-transform-modules-systemjs" "^7.25.0"
+    "@babel/plugin-transform-modules-umd" "^7.24.7"
+    "@babel/plugin-transform-named-capturing-groups-regex" "^7.24.7"
+    "@babel/plugin-transform-new-target" "^7.24.7"
+    "@babel/plugin-transform-nullish-coalescing-operator" "^7.24.7"
+    "@babel/plugin-transform-numeric-separator" "^7.24.7"
+    "@babel/plugin-transform-object-rest-spread" "^7.24.7"
+    "@babel/plugin-transform-object-super" "^7.24.7"
+    "@babel/plugin-transform-optional-catch-binding" "^7.24.7"
+    "@babel/plugin-transform-optional-chaining" "^7.24.8"
+    "@babel/plugin-transform-parameters" "^7.24.7"
+    "@babel/plugin-transform-private-methods" "^7.25.4"
+    "@babel/plugin-transform-private-property-in-object" "^7.24.7"
+    "@babel/plugin-transform-property-literals" "^7.24.7"
+    "@babel/plugin-transform-regenerator" "^7.24.7"
+    "@babel/plugin-transform-reserved-words" "^7.24.7"
+    "@babel/plugin-transform-shorthand-properties" "^7.24.7"
+    "@babel/plugin-transform-spread" "^7.24.7"
+    "@babel/plugin-transform-sticky-regex" "^7.24.7"
+    "@babel/plugin-transform-template-literals" "^7.24.7"
+    "@babel/plugin-transform-typeof-symbol" "^7.24.8"
+    "@babel/plugin-transform-unicode-escapes" "^7.24.7"
+    "@babel/plugin-transform-unicode-property-regex" "^7.24.7"
+    "@babel/plugin-transform-unicode-regex" "^7.24.7"
+    "@babel/plugin-transform-unicode-sets-regex" "^7.25.4"
+    "@babel/preset-modules" "0.1.6-no-external-plugins"
+    "babel-plugin-polyfill-corejs2" "^0.4.10"
+    "babel-plugin-polyfill-corejs3" "^0.10.6"
+    "babel-plugin-polyfill-regenerator" "^0.6.1"
+    "core-js-compat" "^3.37.1"
+    "semver" "^6.3.1"
+
+"@babel/preset-modules@0.1.6-no-external-plugins":
+  "integrity" "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA=="
+  "resolved" "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz"
+  "version" "0.1.6-no-external-plugins"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/types" "^7.4.4"
+    "esutils" "^2.0.2"
+
+"@babel/preset-typescript@^7.22.15":
+  "integrity" "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz"
+  "version" "7.24.7"
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.24.7"
+    "@babel/helper-validator-option" "^7.24.7"
+    "@babel/plugin-syntax-jsx" "^7.24.7"
+    "@babel/plugin-transform-modules-commonjs" "^7.24.7"
+    "@babel/plugin-transform-typescript" "^7.24.7"
+
+"@babel/regjsgen@^0.8.0":
+  "integrity" "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA=="
+  "resolved" "https://registry.npmmirror.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz"
+  "version" "0.8.0"
+
+"@babel/runtime@^7.8.4":
+  "integrity" "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w=="
+  "resolved" "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "regenerator-runtime" "^0.14.0"
+
+"@babel/template@^7.24.7", "@babel/template@^7.25.0":
+  "integrity" "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q=="
+  "resolved" "https://registry.npmmirror.com/@babel/template/-/template-7.25.0.tgz"
+  "version" "7.25.0"
+  dependencies:
+    "@babel/code-frame" "^7.24.7"
+    "@babel/parser" "^7.25.0"
+    "@babel/types" "^7.25.0"
+
+"@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.1", "@babel/traverse@^7.25.2", "@babel/traverse@^7.25.3", "@babel/traverse@^7.25.4":
+  "integrity" "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg=="
+  "resolved" "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/code-frame" "^7.24.7"
+    "@babel/generator" "^7.25.4"
+    "@babel/parser" "^7.25.4"
+    "@babel/template" "^7.25.0"
+    "@babel/types" "^7.25.4"
+    "debug" "^4.3.1"
+    "globals" "^11.1.0"
+
+"@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.4", "@babel/types@^7.4.4":
+  "integrity" "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ=="
+  "resolved" "https://registry.npmmirror.com/@babel/types/-/types-7.25.4.tgz"
+  "version" "7.25.4"
+  dependencies:
+    "@babel/helper-string-parser" "^7.24.8"
+    "@babel/helper-validator-identifier" "^7.24.7"
+    "to-fast-properties" "^2.0.0"
+
+"@commitlint/cli@^17.7.1":
+  "integrity" "sha512-ay+WbzQesE0Rv4EQKfNbSMiJJ12KdKTDzIt0tcK4k11FdsWmtwP0Kp1NWMOUswfIWo6Eb7p7Ln721Nx9FLNBjg=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/cli/-/cli-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/format" "^17.8.1"
+    "@commitlint/lint" "^17.8.1"
+    "@commitlint/load" "^17.8.1"
+    "@commitlint/read" "^17.8.1"
+    "@commitlint/types" "^17.8.1"
+    "execa" "^5.0.0"
+    "lodash.isfunction" "^3.0.9"
+    "resolve-from" "5.0.0"
+    "resolve-global" "1.0.0"
+    "yargs" "^17.0.0"
+
+"@commitlint/config-conventional@^17.7.0":
+  "integrity" "sha512-NxCOHx1kgneig3VLauWJcDWS40DVjg7nKOpBEEK9E5fjJpQqLCilcnKkIIjdBH98kEO1q3NpE5NSrZ2kl/QGJg=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/config-conventional/-/config-conventional-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "conventional-changelog-conventionalcommits" "^6.1.0"
+
+"@commitlint/config-validator@^17.8.1":
+  "integrity" "sha512-UUgUC+sNiiMwkyiuIFR7JG2cfd9t/7MV8VB4TZ+q02ZFkHoduUS4tJGsCBWvBOGD9Btev6IecPMvlWUfJorkEA=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/config-validator/-/config-validator-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/types" "^17.8.1"
+    "ajv" "^8.11.0"
+
+"@commitlint/ensure@^17.8.1":
+  "integrity" "sha512-xjafwKxid8s1K23NFpL8JNo6JnY/ysetKo8kegVM7c8vs+kWLP8VrQq+NbhgVlmCojhEDbzQKp4eRXSjVOGsow=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/ensure/-/ensure-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/types" "^17.8.1"
+    "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"
+
+"@commitlint/execute-rule@^17.8.1":
+  "integrity" "sha512-JHVupQeSdNI6xzA9SqMF+p/JjrHTcrJdI02PwesQIDCIGUrv04hicJgCcws5nzaoZbROapPs0s6zeVHoxpMwFQ=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/execute-rule/-/execute-rule-17.8.1.tgz"
+  "version" "17.8.1"
+
+"@commitlint/format@^17.8.1":
+  "integrity" "sha512-f3oMTyZ84M9ht7fb93wbCKmWxO5/kKSbwuYvS867duVomoOsgrgljkGGIztmT/srZnaiGbaK8+Wf8Ik2tSr5eg=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/format/-/format-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/types" "^17.8.1"
+    "chalk" "^4.1.0"
+
+"@commitlint/is-ignored@^17.8.1":
+  "integrity" "sha512-UshMi4Ltb4ZlNn4F7WtSEugFDZmctzFpmbqvpyxD3la510J+PLcnyhf9chs7EryaRFJMdAKwsEKfNK0jL/QM4g=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/is-ignored/-/is-ignored-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/types" "^17.8.1"
+    "semver" "7.5.4"
+
+"@commitlint/lint@^17.8.1":
+  "integrity" "sha512-aQUlwIR1/VMv2D4GXSk7PfL5hIaFSfy6hSHV94O8Y27T5q+DlDEgd/cZ4KmVI+MWKzFfCTiTuWqjfRSfdRllCA=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/lint/-/lint-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/is-ignored" "^17.8.1"
+    "@commitlint/parse" "^17.8.1"
+    "@commitlint/rules" "^17.8.1"
+    "@commitlint/types" "^17.8.1"
+
+"@commitlint/load@^17.8.1":
+  "integrity" "sha512-iF4CL7KDFstP1kpVUkT8K2Wl17h2yx9VaR1ztTc8vzByWWcbO/WaKwxsnCOqow9tVAlzPfo1ywk9m2oJ9ucMqA=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/load/-/load-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/config-validator" "^17.8.1"
+    "@commitlint/execute-rule" "^17.8.1"
+    "@commitlint/resolve-extends" "^17.8.1"
+    "@commitlint/types" "^17.8.1"
+    "@types/node" "20.5.1"
+    "chalk" "^4.1.0"
+    "cosmiconfig" "^8.0.0"
+    "cosmiconfig-typescript-loader" "^4.0.0"
+    "lodash.isplainobject" "^4.0.6"
+    "lodash.merge" "^4.6.2"
+    "lodash.uniq" "^4.5.0"
+    "resolve-from" "^5.0.0"
+    "ts-node" "^10.8.1"
+    "typescript" "^4.6.4 || ^5.2.2"
+
+"@commitlint/message@^17.8.1":
+  "integrity" "sha512-6bYL1GUQsD6bLhTH3QQty8pVFoETfFQlMn2Nzmz3AOLqRVfNNtXBaSY0dhZ0dM6A2MEq4+2d7L/2LP8TjqGRkA=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/message/-/message-17.8.1.tgz"
+  "version" "17.8.1"
+
+"@commitlint/parse@^17.8.1":
+  "integrity" "sha512-/wLUickTo0rNpQgWwLPavTm7WbwkZoBy3X8PpkUmlSmQJyWQTj0m6bDjiykMaDt41qcUbfeFfaCvXfiR4EGnfw=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/parse/-/parse-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/types" "^17.8.1"
+    "conventional-changelog-angular" "^6.0.0"
+    "conventional-commits-parser" "^4.0.0"
+
+"@commitlint/read@^17.8.1":
+  "integrity" "sha512-Fd55Oaz9irzBESPCdMd8vWWgxsW3OWR99wOntBDHgf9h7Y6OOHjWEdS9Xzen1GFndqgyoaFplQS5y7KZe0kO2w=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/read/-/read-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/top-level" "^17.8.1"
+    "@commitlint/types" "^17.8.1"
+    "fs-extra" "^11.0.0"
+    "git-raw-commits" "^2.0.11"
+    "minimist" "^1.2.6"
+
+"@commitlint/resolve-extends@^17.8.1":
+  "integrity" "sha512-W/ryRoQ0TSVXqJrx5SGkaYuAaE/BUontL1j1HsKckvM6e5ZaG0M9126zcwL6peKSuIetJi7E87PRQF8O86EW0Q=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/resolve-extends/-/resolve-extends-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/config-validator" "^17.8.1"
+    "@commitlint/types" "^17.8.1"
+    "import-fresh" "^3.0.0"
+    "lodash.mergewith" "^4.6.2"
+    "resolve-from" "^5.0.0"
+    "resolve-global" "^1.0.0"
+
+"@commitlint/rules@^17.8.1":
+  "integrity" "sha512-2b7OdVbN7MTAt9U0vKOYKCDsOvESVXxQmrvuVUZ0rGFMCrCPJWWP1GJ7f0lAypbDAhaGb8zqtdOr47192LBrIA=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/rules/-/rules-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "@commitlint/ensure" "^17.8.1"
+    "@commitlint/message" "^17.8.1"
+    "@commitlint/to-lines" "^17.8.1"
+    "@commitlint/types" "^17.8.1"
+    "execa" "^5.0.0"
+
+"@commitlint/to-lines@^17.8.1":
+  "integrity" "sha512-LE0jb8CuR/mj6xJyrIk8VLz03OEzXFgLdivBytoooKO5xLt5yalc8Ma5guTWobw998sbR3ogDd+2jed03CFmJA=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/to-lines/-/to-lines-17.8.1.tgz"
+  "version" "17.8.1"
+
+"@commitlint/top-level@^17.8.1":
+  "integrity" "sha512-l6+Z6rrNf5p333SHfEte6r+WkOxGlWK4bLuZKbtf/2TXRN+qhrvn1XE63VhD8Oe9oIHQ7F7W1nG2k/TJFhx2yA=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/top-level/-/top-level-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "find-up" "^5.0.0"
+
+"@commitlint/types@^17.8.1":
+  "integrity" "sha512-PXDQXkAmiMEG162Bqdh9ChML/GJZo6vU+7F03ALKDK8zYc6SuAr47LjG7hGYRqUOz+WK0dU7bQ0xzuqFMdxzeQ=="
+  "resolved" "https://registry.npmmirror.com/@commitlint/types/-/types-17.8.1.tgz"
+  "version" "17.8.1"
+  dependencies:
+    "chalk" "^4.1.0"
+
+"@cspotcode/source-map-support@^0.8.0":
+  "integrity" "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="
+  "resolved" "https://registry.npmmirror.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz"
+  "version" "0.8.1"
+  dependencies:
+    "@jridgewell/trace-mapping" "0.3.9"
+
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
+  "integrity" "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA=="
+  "resolved" "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
+  "version" "4.4.0"
+  dependencies:
+    "eslint-visitor-keys" "^3.3.0"
+
+"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1":
+  "integrity" "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A=="
+  "resolved" "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz"
+  "version" "4.11.0"
+
+"@eslint/eslintrc@^2.1.4":
+  "integrity" "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ=="
+  "resolved" "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz"
+  "version" "2.1.4"
+  dependencies:
+    "ajv" "^6.12.4"
+    "debug" "^4.3.2"
+    "espree" "^9.6.0"
+    "globals" "^13.19.0"
+    "ignore" "^5.2.0"
+    "import-fresh" "^3.2.1"
+    "js-yaml" "^4.1.0"
+    "minimatch" "^3.1.2"
+    "strip-json-comments" "^3.1.1"
+
+"@eslint/js@8.57.0":
+  "integrity" "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g=="
+  "resolved" "https://registry.npmmirror.com/@eslint/js/-/js-8.57.0.tgz"
+  "version" "8.57.0"
+
+"@humanwhocodes/config-array@^0.11.14":
+  "integrity" "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg=="
+  "resolved" "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz"
+  "version" "0.11.14"
+  dependencies:
+    "@humanwhocodes/object-schema" "^2.0.2"
+    "debug" "^4.3.1"
+    "minimatch" "^3.0.5"
+
+"@humanwhocodes/module-importer@^1.0.1":
+  "integrity" "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="
+  "resolved" "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz"
+  "version" "1.0.1"
+
+"@humanwhocodes/object-schema@^2.0.2":
+  "integrity" "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="
+  "resolved" "https://registry.npmmirror.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz"
+  "version" "2.0.3"
+
+"@hutson/parse-repository-url@^3.0.0":
+  "integrity" "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q=="
+  "resolved" "https://registry.npmmirror.com/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz"
+  "version" "3.0.2"
+
+"@isaacs/cliui@^8.0.2":
+  "integrity" "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="
+  "resolved" "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz"
+  "version" "8.0.2"
+  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"
+
+"@jridgewell/gen-mapping@^0.3.5":
+  "integrity" "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg=="
+  "resolved" "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz"
+  "version" "0.3.5"
+  dependencies:
+    "@jridgewell/set-array" "^1.2.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0":
+  "integrity" "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="
+  "resolved" "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
+  "version" "3.1.2"
+
+"@jridgewell/set-array@^1.2.1":
+  "integrity" "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="
+  "resolved" "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.2.1.tgz"
+  "version" "1.2.1"
+
+"@jridgewell/source-map@^0.3.3":
+  "integrity" "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ=="
+  "resolved" "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.6.tgz"
+  "version" "0.3.6"
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.5"
+    "@jridgewell/trace-mapping" "^0.3.25"
+
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0":
+  "integrity" "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="
+  "resolved" "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
+  "version" "1.5.0"
+
+"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25":
+  "integrity" "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="
+  "resolved" "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz"
+  "version" "0.3.25"
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.1.0"
+    "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@jridgewell/trace-mapping@0.3.9":
+  "integrity" "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="
+  "resolved" "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz"
+  "version" "0.3.9"
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.0.3"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@nodelib/fs.scandir@2.1.5":
+  "integrity" "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="
+  "resolved" "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
+  "version" "2.1.5"
+  dependencies:
+    "@nodelib/fs.stat" "2.0.5"
+    "run-parallel" "^1.1.9"
+
+"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
+  "integrity" "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="
+  "resolved" "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
+  "version" "2.0.5"
+
+"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8":
+  "integrity" "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="
+  "resolved" "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz"
+  "version" "1.2.8"
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.5"
+    "fastq" "^1.6.0"
+
+"@pkgjs/parseargs@^0.11.0":
+  "integrity" "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="
+  "resolved" "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz"
+  "version" "0.11.0"
+
+"@pkgr/core@^0.1.0":
+  "integrity" "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA=="
+  "resolved" "https://registry.npmmirror.com/@pkgr/core/-/core-0.1.1.tgz"
+  "version" "0.1.1"
+
+"@rollup/plugin-babel@^6.0.3":
+  "integrity" "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw=="
+  "resolved" "https://registry.npmmirror.com/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz"
+  "version" "6.0.4"
+  dependencies:
+    "@babel/helper-module-imports" "^7.18.6"
+    "@rollup/pluginutils" "^5.0.1"
+
+"@rollup/plugin-commonjs@^25.0.4":
+  "integrity" "sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A=="
+  "resolved" "https://registry.npmmirror.com/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz"
+  "version" "25.0.8"
+  dependencies:
+    "@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.30.3"
+
+"@rollup/plugin-json@^6.0.0":
+  "integrity" "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA=="
+  "resolved" "https://registry.npmmirror.com/@rollup/plugin-json/-/plugin-json-6.1.0.tgz"
+  "version" "6.1.0"
+  dependencies:
+    "@rollup/pluginutils" "^5.1.0"
+
+"@rollup/plugin-node-resolve@^15.2.1":
+  "integrity" "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ=="
+  "resolved" "https://registry.npmmirror.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz"
+  "version" "15.2.3"
+  dependencies:
+    "@rollup/pluginutils" "^5.0.1"
+    "@types/resolve" "1.20.2"
+    "deepmerge" "^4.2.2"
+    "is-builtin-module" "^3.2.1"
+    "is-module" "^1.0.0"
+    "resolve" "^1.22.1"
+
+"@rollup/plugin-terser@^0.4.3":
+  "integrity" "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A=="
+  "resolved" "https://registry.npmmirror.com/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz"
+  "version" "0.4.4"
+  dependencies:
+    "serialize-javascript" "^6.0.1"
+    "smob" "^1.0.0"
+    "terser" "^5.17.4"
+
+"@rollup/plugin-typescript@^11.1.3":
+  "integrity" "sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA=="
+  "resolved" "https://registry.npmmirror.com/@rollup/plugin-typescript/-/plugin-typescript-11.1.6.tgz"
+  "version" "11.1.6"
+  dependencies:
+    "@rollup/pluginutils" "^5.1.0"
+    "resolve" "^1.22.1"
+
+"@rollup/pluginutils@^5.0.1", "@rollup/pluginutils@^5.1.0":
+  "integrity" "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g=="
+  "resolved" "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.1.0.tgz"
+  "version" "5.1.0"
+  dependencies:
+    "@types/estree" "^1.0.0"
+    "estree-walker" "^2.0.2"
+    "picomatch" "^2.3.1"
+
+"@tootallnate/once@2":
+  "integrity" "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="
+  "resolved" "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz"
+  "version" "2.0.0"
+
+"@tsconfig/node10@^1.0.7":
+  "integrity" "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw=="
+  "resolved" "https://registry.npmmirror.com/@tsconfig/node10/-/node10-1.0.11.tgz"
+  "version" "1.0.11"
+
+"@tsconfig/node12@^1.0.7":
+  "integrity" "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
+  "resolved" "https://registry.npmmirror.com/@tsconfig/node12/-/node12-1.0.11.tgz"
+  "version" "1.0.11"
+
+"@tsconfig/node14@^1.0.0":
+  "integrity" "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
+  "resolved" "https://registry.npmmirror.com/@tsconfig/node14/-/node14-1.0.3.tgz"
+  "version" "1.0.3"
+
+"@tsconfig/node16@^1.0.2":
+  "integrity" "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
+  "resolved" "https://registry.npmmirror.com/@tsconfig/node16/-/node16-1.0.4.tgz"
+  "version" "1.0.4"
+
+"@types/estree@*", "@types/estree@^1.0.0":
+  "integrity" "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
+  "resolved" "https://registry.npmmirror.com/@types/estree/-/estree-1.0.5.tgz"
+  "version" "1.0.5"
+
+"@types/json-schema@^7.0.12", "@types/json-schema@^7.0.9":
+  "integrity" "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
+  "resolved" "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz"
+  "version" "7.0.15"
+
+"@types/minimist@^1.2.0":
+  "integrity" "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag=="
+  "resolved" "https://registry.npmmirror.com/@types/minimist/-/minimist-1.2.5.tgz"
+  "version" "1.2.5"
+
+"@types/node@*", "@types/node@^20.5.7":
+  "integrity" "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ=="
+  "resolved" "https://registry.npmmirror.com/@types/node/-/node-20.16.2.tgz"
+  "version" "20.16.2"
+  dependencies:
+    "undici-types" "~6.19.2"
+
+"@types/node@20.5.1":
+  "integrity" "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg=="
+  "resolved" "https://registry.npmmirror.com/@types/node/-/node-20.5.1.tgz"
+  "version" "20.5.1"
+
+"@types/normalize-package-data@^2.4.0":
+  "integrity" "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="
+  "resolved" "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz"
+  "version" "2.4.4"
+
+"@types/resolve@1.20.2":
+  "integrity" "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
+  "resolved" "https://registry.npmmirror.com/@types/resolve/-/resolve-1.20.2.tgz"
+  "version" "1.20.2"
+
+"@types/semver@^7.3.12", "@types/semver@^7.5.0":
+  "integrity" "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ=="
+  "resolved" "https://registry.npmmirror.com/@types/semver/-/semver-7.5.8.tgz"
+  "version" "7.5.8"
+
+"@types/socket.io-client@1.4.36":
+  "integrity" "sha512-ZJWjtFBeBy1kRSYpVbeGYTElf6BqPQUkXDlHHD4k/42byCN5Rh027f4yARHCink9sKAkbtGZXEAmR0ZCnc2/Ag=="
+  "resolved" "https://registry.npmmirror.com/@types/socket.io-client/-/socket.io-client-1.4.36.tgz"
+  "version" "1.4.36"
+
+"@typescript-eslint/eslint-plugin@^5.59.1":
+  "integrity" "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz"
+  "version" "5.62.0"
+  dependencies:
+    "@eslint-community/regexpp" "^4.4.0"
+    "@typescript-eslint/scope-manager" "5.62.0"
+    "@typescript-eslint/type-utils" "5.62.0"
+    "@typescript-eslint/utils" "5.62.0"
+    "debug" "^4.3.4"
+    "graphemer" "^1.4.0"
+    "ignore" "^5.2.0"
+    "natural-compare-lite" "^1.4.0"
+    "semver" "^7.3.7"
+    "tsutils" "^3.21.0"
+
+"@typescript-eslint/eslint-plugin@^6.4.1":
+  "integrity" "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz"
+  "version" "6.21.0"
+  dependencies:
+    "@eslint-community/regexpp" "^4.5.1"
+    "@typescript-eslint/scope-manager" "6.21.0"
+    "@typescript-eslint/type-utils" "6.21.0"
+    "@typescript-eslint/utils" "6.21.0"
+    "@typescript-eslint/visitor-keys" "6.21.0"
+    "debug" "^4.3.4"
+    "graphemer" "^1.4.0"
+    "ignore" "^5.2.4"
+    "natural-compare" "^1.4.0"
+    "semver" "^7.5.4"
+    "ts-api-utils" "^1.0.1"
+
+"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.59.1":
+  "integrity" "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.62.0.tgz"
+  "version" "5.62.0"
+  dependencies:
+    "@typescript-eslint/scope-manager" "5.62.0"
+    "@typescript-eslint/types" "5.62.0"
+    "@typescript-eslint/typescript-estree" "5.62.0"
+    "debug" "^4.3.4"
+
+"@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha", "@typescript-eslint/parser@^6.4.1":
+  "integrity" "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-6.21.0.tgz"
+  "version" "6.21.0"
+  dependencies:
+    "@typescript-eslint/scope-manager" "6.21.0"
+    "@typescript-eslint/types" "6.21.0"
+    "@typescript-eslint/typescript-estree" "6.21.0"
+    "@typescript-eslint/visitor-keys" "6.21.0"
+    "debug" "^4.3.4"
+
+"@typescript-eslint/scope-manager@5.62.0":
+  "integrity" "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz"
+  "version" "5.62.0"
+  dependencies:
+    "@typescript-eslint/types" "5.62.0"
+    "@typescript-eslint/visitor-keys" "5.62.0"
+
+"@typescript-eslint/scope-manager@6.21.0":
+  "integrity" "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz"
+  "version" "6.21.0"
+  dependencies:
+    "@typescript-eslint/types" "6.21.0"
+    "@typescript-eslint/visitor-keys" "6.21.0"
+
+"@typescript-eslint/type-utils@5.62.0":
+  "integrity" "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz"
+  "version" "5.62.0"
+  dependencies:
+    "@typescript-eslint/typescript-estree" "5.62.0"
+    "@typescript-eslint/utils" "5.62.0"
+    "debug" "^4.3.4"
+    "tsutils" "^3.21.0"
+
+"@typescript-eslint/type-utils@6.21.0":
+  "integrity" "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz"
+  "version" "6.21.0"
+  dependencies:
+    "@typescript-eslint/typescript-estree" "6.21.0"
+    "@typescript-eslint/utils" "6.21.0"
+    "debug" "^4.3.4"
+    "ts-api-utils" "^1.0.1"
+
+"@typescript-eslint/types@5.62.0":
+  "integrity" "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.62.0.tgz"
+  "version" "5.62.0"
+
+"@typescript-eslint/types@6.21.0":
+  "integrity" "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/types/-/types-6.21.0.tgz"
+  "version" "6.21.0"
+
+"@typescript-eslint/typescript-estree@5.62.0":
+  "integrity" "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz"
+  "version" "5.62.0"
+  dependencies:
+    "@typescript-eslint/types" "5.62.0"
+    "@typescript-eslint/visitor-keys" "5.62.0"
+    "debug" "^4.3.4"
+    "globby" "^11.1.0"
+    "is-glob" "^4.0.3"
+    "semver" "^7.3.7"
+    "tsutils" "^3.21.0"
+
+"@typescript-eslint/typescript-estree@6.21.0":
+  "integrity" "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz"
+  "version" "6.21.0"
+  dependencies:
+    "@typescript-eslint/types" "6.21.0"
+    "@typescript-eslint/visitor-keys" "6.21.0"
+    "debug" "^4.3.4"
+    "globby" "^11.1.0"
+    "is-glob" "^4.0.3"
+    "minimatch" "9.0.3"
+    "semver" "^7.5.4"
+    "ts-api-utils" "^1.0.1"
+
+"@typescript-eslint/utils@5.62.0":
+  "integrity" "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-5.62.0.tgz"
+  "version" "5.62.0"
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
+    "@types/json-schema" "^7.0.9"
+    "@types/semver" "^7.3.12"
+    "@typescript-eslint/scope-manager" "5.62.0"
+    "@typescript-eslint/types" "5.62.0"
+    "@typescript-eslint/typescript-estree" "5.62.0"
+    "eslint-scope" "^5.1.1"
+    "semver" "^7.3.7"
+
+"@typescript-eslint/utils@6.21.0":
+  "integrity" "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-6.21.0.tgz"
+  "version" "6.21.0"
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.4.0"
+    "@types/json-schema" "^7.0.12"
+    "@types/semver" "^7.5.0"
+    "@typescript-eslint/scope-manager" "6.21.0"
+    "@typescript-eslint/types" "6.21.0"
+    "@typescript-eslint/typescript-estree" "6.21.0"
+    "semver" "^7.5.4"
+
+"@typescript-eslint/visitor-keys@5.62.0":
+  "integrity" "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz"
+  "version" "5.62.0"
+  dependencies:
+    "@typescript-eslint/types" "5.62.0"
+    "eslint-visitor-keys" "^3.3.0"
+
+"@typescript-eslint/visitor-keys@6.21.0":
+  "integrity" "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A=="
+  "resolved" "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz"
+  "version" "6.21.0"
+  dependencies:
+    "@typescript-eslint/types" "6.21.0"
+    "eslint-visitor-keys" "^3.4.1"
+
+"@ungap/structured-clone@^1.2.0":
+  "integrity" "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
+  "resolved" "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz"
+  "version" "1.2.0"
+
+"@vue/eslint-config-prettier@^8.0.0":
+  "integrity" "sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg=="
+  "resolved" "https://registry.npmmirror.com/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz"
+  "version" "8.0.0"
+  dependencies:
+    "eslint-config-prettier" "^8.8.0"
+    "eslint-plugin-prettier" "^5.0.0"
+
+"@vue/eslint-config-typescript@^11.0.3":
+  "integrity" "sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw=="
+  "resolved" "https://registry.npmmirror.com/@vue/eslint-config-typescript/-/eslint-config-typescript-11.0.3.tgz"
+  "version" "11.0.3"
+  dependencies:
+    "@typescript-eslint/eslint-plugin" "^5.59.1"
+    "@typescript-eslint/parser" "^5.59.1"
+    "vue-eslint-parser" "^9.1.1"
+
+"abab@^2.0.6":
+  "integrity" "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
+  "resolved" "https://registry.npmmirror.com/abab/-/abab-2.0.6.tgz"
+  "version" "2.0.6"
+
+"acorn-jsx@^5.3.2":
+  "integrity" "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="
+  "resolved" "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
+  "version" "5.3.2"
+
+"acorn-walk@^8.1.1":
+  "integrity" "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw=="
+  "resolved" "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.3.3.tgz"
+  "version" "8.3.3"
+  dependencies:
+    "acorn" "^8.11.0"
+
+"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^8.11.0", "acorn@^8.4.1", "acorn@^8.8.2", "acorn@^8.9.0":
+  "integrity" "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg=="
+  "resolved" "https://registry.npmmirror.com/acorn/-/acorn-8.12.1.tgz"
+  "version" "8.12.1"
+
+"add-stream@^1.0.0":
+  "integrity" "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ=="
+  "resolved" "https://registry.npmmirror.com/add-stream/-/add-stream-1.0.0.tgz"
+  "version" "1.0.0"
+
+"after@0.8.2":
+  "integrity" "sha512-QbJ0NTQ/I9DI3uSJA4cbexiwQeRAfjPScqIbSjUDd9TOrcg6pTkdgziesOqxBMBzit8vFCTwrP27t13vFOORRA=="
+  "resolved" "https://registry.npmmirror.com/after/-/after-0.8.2.tgz"
+  "version" "0.8.2"
+
+"agent-base@6":
+  "integrity" "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="
+  "resolved" "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz"
+  "version" "6.0.2"
+  dependencies:
+    "debug" "4"
+
+"ajv@^6.12.4":
+  "integrity" "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="
+  "resolved" "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz"
+  "version" "6.12.6"
+  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"
+
+"ajv@^8.11.0":
+  "integrity" "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="
+  "resolved" "https://registry.npmmirror.com/ajv/-/ajv-8.17.1.tgz"
+  "version" "8.17.1"
+  dependencies:
+    "fast-deep-equal" "^3.1.3"
+    "fast-uri" "^3.0.1"
+    "json-schema-traverse" "^1.0.0"
+    "require-from-string" "^2.0.2"
+
+"ansi-colors@^4.1.1":
+  "integrity" "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="
+  "resolved" "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.3.tgz"
+  "version" "4.1.3"
+
+"ansi-escapes@^5.0.0":
+  "integrity" "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA=="
+  "resolved" "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-5.0.0.tgz"
+  "version" "5.0.0"
+  dependencies:
+    "type-fest" "^1.0.2"
+
+"ansi-regex@^5.0.1":
+  "integrity" "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+  "resolved" "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz"
+  "version" "5.0.1"
+
+"ansi-regex@^6.0.1":
+  "integrity" "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="
+  "resolved" "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz"
+  "version" "6.0.1"
+
+"ansi-styles@^3.2.1":
+  "integrity" "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="
+  "resolved" "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz"
+  "version" "3.2.1"
+  dependencies:
+    "color-convert" "^1.9.0"
+
+"ansi-styles@^4.0.0", "ansi-styles@^4.1.0":
+  "integrity" "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="
+  "resolved" "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz"
+  "version" "4.3.0"
+  dependencies:
+    "color-convert" "^2.0.1"
+
+"ansi-styles@^6.0.0":
+  "integrity" "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
+  "resolved" "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz"
+  "version" "6.2.1"
+
+"ansi-styles@^6.1.0":
+  "integrity" "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="
+  "resolved" "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz"
+  "version" "6.2.1"
+
+"arg@^4.1.0":
+  "integrity" "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
+  "resolved" "https://registry.npmmirror.com/arg/-/arg-4.1.3.tgz"
+  "version" "4.1.3"
+
+"argparse@^2.0.1":
+  "integrity" "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+  "resolved" "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz"
+  "version" "2.0.1"
+
+"array-ify@^1.0.0":
+  "integrity" "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="
+  "resolved" "https://registry.npmmirror.com/array-ify/-/array-ify-1.0.0.tgz"
+  "version" "1.0.0"
+
+"array-union@^2.1.0":
+  "integrity" "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="
+  "resolved" "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz"
+  "version" "2.1.0"
+
+"arraybuffer.slice@~0.0.7":
+  "integrity" "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
+  "resolved" "https://registry.npmmirror.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz"
+  "version" "0.0.7"
+
+"arrify@^1.0.1":
+  "integrity" "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA=="
+  "resolved" "https://registry.npmmirror.com/arrify/-/arrify-1.0.1.tgz"
+  "version" "1.0.1"
+
+"asynckit@^0.4.0":
+  "integrity" "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+  "resolved" "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz"
+  "version" "0.4.0"
+
+"babel-plugin-polyfill-corejs2@^0.4.10":
+  "integrity" "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q=="
+  "resolved" "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz"
+  "version" "0.4.11"
+  dependencies:
+    "@babel/compat-data" "^7.22.6"
+    "@babel/helper-define-polyfill-provider" "^0.6.2"
+    "semver" "^6.3.1"
+
+"babel-plugin-polyfill-corejs3@^0.10.6":
+  "integrity" "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA=="
+  "resolved" "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz"
+  "version" "0.10.6"
+  dependencies:
+    "@babel/helper-define-polyfill-provider" "^0.6.2"
+    "core-js-compat" "^3.38.0"
+
+"babel-plugin-polyfill-regenerator@^0.6.1":
+  "integrity" "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg=="
+  "resolved" "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz"
+  "version" "0.6.2"
+  dependencies:
+    "@babel/helper-define-polyfill-provider" "^0.6.2"
+
+"backo2@1.0.2":
+  "integrity" "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA=="
+  "resolved" "https://registry.npmmirror.com/backo2/-/backo2-1.0.2.tgz"
+  "version" "1.0.2"
+
+"balanced-match@^1.0.0":
+  "integrity" "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
+  "resolved" "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz"
+  "version" "1.0.2"
+
+"base64-arraybuffer@0.1.4":
+  "integrity" "sha512-a1eIFi4R9ySrbiMuyTGx5e92uRH5tQY6kArNcFaKBUleIoLjdjBg7Zxm3Mqm3Kmkf27HLR/1fnxX9q8GQ7Iavg=="
+  "resolved" "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz"
+  "version" "0.1.4"
+
+"blob@0.0.5":
+  "integrity" "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
+  "resolved" "https://registry.npmmirror.com/blob/-/blob-0.0.5.tgz"
+  "version" "0.0.5"
+
+"boolbase@^1.0.0":
+  "integrity" "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+  "resolved" "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz"
+  "version" "1.0.0"
+
+"brace-expansion@^1.1.7":
+  "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="
+  "resolved" "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz"
+  "version" "1.1.11"
+  dependencies:
+    "balanced-match" "^1.0.0"
+    "concat-map" "0.0.1"
+
+"brace-expansion@^2.0.1":
+  "integrity" "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="
+  "resolved" "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz"
+  "version" "2.0.1"
+  dependencies:
+    "balanced-match" "^1.0.0"
+
+"braces@^3.0.2", "braces@^3.0.3":
+  "integrity" "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="
+  "resolved" "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz"
+  "version" "3.0.3"
+  dependencies:
+    "fill-range" "^7.1.1"
+
+"browserslist@^4.23.1", "browserslist@^4.23.3", "browserslist@>= 4.21.0":
+  "integrity" "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA=="
+  "resolved" "https://registry.npmmirror.com/browserslist/-/browserslist-4.23.3.tgz"
+  "version" "4.23.3"
+  dependencies:
+    "caniuse-lite" "^1.0.30001646"
+    "electron-to-chromium" "^1.5.4"
+    "node-releases" "^2.0.18"
+    "update-browserslist-db" "^1.1.0"
+
+"buffer-from@^1.0.0":
+  "integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+  "resolved" "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz"
+  "version" "1.1.2"
+
+"builtin-modules@^3.3.0":
+  "integrity" "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="
+  "resolved" "https://registry.npmmirror.com/builtin-modules/-/builtin-modules-3.3.0.tgz"
+  "version" "3.3.0"
+
+"builtins@^2.0.0":
+  "integrity" "sha512-XkkVe5QAb6guWPXTzpSrYpSlN3nqEmrrE2TkAr/tp7idSF6+MONh9WvKrAuR3HiKLvoSgmbs8l1U9IPmMrIoLw=="
+  "resolved" "https://registry.npmmirror.com/builtins/-/builtins-2.0.1.tgz"
+  "version" "2.0.1"
+  dependencies:
+    "semver" "^6.0.0"
+
+"callsites@^3.0.0":
+  "integrity" "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
+  "resolved" "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz"
+  "version" "3.1.0"
+
+"camelcase-keys@^6.2.2":
+  "integrity" "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg=="
+  "resolved" "https://registry.npmmirror.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz"
+  "version" "6.2.2"
+  dependencies:
+    "camelcase" "^5.3.1"
+    "map-obj" "^4.0.0"
+    "quick-lru" "^4.0.1"
+
+"camelcase@^5.3.1":
+  "integrity" "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+  "resolved" "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz"
+  "version" "5.3.1"
+
+"caniuse-lite@^1.0.30001646":
+  "integrity" "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw=="
+  "resolved" "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz"
+  "version" "1.0.30001653"
+
+"chalk@^2.4.2":
+  "integrity" "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="
+  "resolved" "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz"
+  "version" "2.4.2"
+  dependencies:
+    "ansi-styles" "^3.2.1"
+    "escape-string-regexp" "^1.0.5"
+    "supports-color" "^5.3.0"
+
+"chalk@^4.0.0", "chalk@^4.1.0":
+  "integrity" "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="
+  "resolved" "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz"
+  "version" "4.1.2"
+  dependencies:
+    "ansi-styles" "^4.1.0"
+    "supports-color" "^7.1.0"
+
+"chalk@5.3.0":
+  "integrity" "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w=="
+  "resolved" "https://registry.npmmirror.com/chalk/-/chalk-5.3.0.tgz"
+  "version" "5.3.0"
+
+"cli-cursor@^4.0.0":
+  "integrity" "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="
+  "resolved" "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "restore-cursor" "^4.0.0"
+
+"cli-truncate@^3.1.0":
+  "integrity" "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA=="
+  "resolved" "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-3.1.0.tgz"
+  "version" "3.1.0"
+  dependencies:
+    "slice-ansi" "^5.0.0"
+    "string-width" "^5.0.0"
+
+"cliui@^7.0.2":
+  "integrity" "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="
+  "resolved" "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz"
+  "version" "7.0.4"
+  dependencies:
+    "string-width" "^4.2.0"
+    "strip-ansi" "^6.0.0"
+    "wrap-ansi" "^7.0.0"
+
+"cliui@^8.0.1":
+  "integrity" "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="
+  "resolved" "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz"
+  "version" "8.0.1"
+  dependencies:
+    "string-width" "^4.2.0"
+    "strip-ansi" "^6.0.1"
+    "wrap-ansi" "^7.0.0"
+
+"color-convert@^1.9.0":
+  "integrity" "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="
+  "resolved" "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz"
+  "version" "1.9.3"
+  dependencies:
+    "color-name" "1.1.3"
+
+"color-convert@^2.0.1":
+  "integrity" "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="
+  "resolved" "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz"
+  "version" "2.0.1"
+  dependencies:
+    "color-name" "~1.1.4"
+
+"color-name@~1.1.4":
+  "integrity" "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+  "resolved" "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz"
+  "version" "1.1.4"
+
+"color-name@1.1.3":
+  "integrity" "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
+  "resolved" "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz"
+  "version" "1.1.3"
+
+"colorette@^2.0.20":
+  "integrity" "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="
+  "resolved" "https://registry.npmmirror.com/colorette/-/colorette-2.0.20.tgz"
+  "version" "2.0.20"
+
+"combined-stream@^1.0.8":
+  "integrity" "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="
+  "resolved" "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz"
+  "version" "1.0.8"
+  dependencies:
+    "delayed-stream" "~1.0.0"
+
+"commander@^2.20.0":
+  "integrity" "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+  "resolved" "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz"
+  "version" "2.20.3"
+
+"commander@11.0.0":
+  "integrity" "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ=="
+  "resolved" "https://registry.npmmirror.com/commander/-/commander-11.0.0.tgz"
+  "version" "11.0.0"
+
+"commondir@^1.0.1":
+  "integrity" "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="
+  "resolved" "https://registry.npmmirror.com/commondir/-/commondir-1.0.1.tgz"
+  "version" "1.0.1"
+
+"compare-func@^2.0.0":
+  "integrity" "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA=="
+  "resolved" "https://registry.npmmirror.com/compare-func/-/compare-func-2.0.0.tgz"
+  "version" "2.0.0"
+  dependencies:
+    "array-ify" "^1.0.0"
+    "dot-prop" "^5.1.0"
+
+"component-bind@1.0.0":
+  "integrity" "sha512-WZveuKPeKAG9qY+FkYDeADzdHyTYdIboXS59ixDeRJL5ZhxpqUnxSOwop4FQjMsiYm3/Or8cegVbpAHNA7pHxw=="
+  "resolved" "https://registry.npmmirror.com/component-bind/-/component-bind-1.0.0.tgz"
+  "version" "1.0.0"
+
+"component-emitter@~1.3.0":
+  "integrity" "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ=="
+  "resolved" "https://registry.npmmirror.com/component-emitter/-/component-emitter-1.3.1.tgz"
+  "version" "1.3.1"
+
+"component-inherit@0.0.3":
+  "integrity" "sha512-w+LhYREhatpVqTESyGFg3NlP6Iu0kEKUHETY9GoZP/pQyW4mHFZuFWRUCIqVPZ36ueVLtoOEZaAqbCF2RDndaA=="
+  "resolved" "https://registry.npmmirror.com/component-inherit/-/component-inherit-0.0.3.tgz"
+  "version" "0.0.3"
+
+"concat-map@0.0.1":
+  "integrity" "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
+  "resolved" "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz"
+  "version" "0.0.1"
+
+"conventional-changelog-angular@^5.0.12":
+  "integrity" "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz"
+  "version" "5.0.13"
+  dependencies:
+    "compare-func" "^2.0.0"
+    "q" "^1.5.1"
+
+"conventional-changelog-angular@^6.0.0":
+  "integrity" "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz"
+  "version" "6.0.0"
+  dependencies:
+    "compare-func" "^2.0.0"
+
+"conventional-changelog-atom@^2.0.8":
+  "integrity" "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz"
+  "version" "2.0.8"
+  dependencies:
+    "q" "^1.5.1"
+
+"conventional-changelog-cli@^2.2.2":
+  "integrity" "sha512-8grMV5Jo8S0kP3yoMeJxV2P5R6VJOqK72IiSV9t/4H5r/HiRqEBQ83bYGuz4Yzfdj4bjaAEhZN/FFbsFXr5bOA=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-cli/-/conventional-changelog-cli-2.2.2.tgz"
+  "version" "2.2.2"
+  dependencies:
+    "add-stream" "^1.0.0"
+    "conventional-changelog" "^3.1.24"
+    "lodash" "^4.17.15"
+    "meow" "^8.0.0"
+    "tempfile" "^3.0.0"
+
+"conventional-changelog-codemirror@^2.0.8":
+  "integrity" "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz"
+  "version" "2.0.8"
+  dependencies:
+    "q" "^1.5.1"
+
+"conventional-changelog-conventionalcommits@^4.5.0":
+  "integrity" "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz"
+  "version" "4.6.3"
+  dependencies:
+    "compare-func" "^2.0.0"
+    "lodash" "^4.17.15"
+    "q" "^1.5.1"
+
+"conventional-changelog-conventionalcommits@^6.1.0":
+  "integrity" "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz"
+  "version" "6.1.0"
+  dependencies:
+    "compare-func" "^2.0.0"
+
+"conventional-changelog-core@^4.2.1":
+  "integrity" "sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz"
+  "version" "4.2.4"
+  dependencies:
+    "add-stream" "^1.0.0"
+    "conventional-changelog-writer" "^5.0.0"
+    "conventional-commits-parser" "^3.2.0"
+    "dateformat" "^3.0.0"
+    "get-pkg-repo" "^4.0.0"
+    "git-raw-commits" "^2.0.8"
+    "git-remote-origin-url" "^2.0.0"
+    "git-semver-tags" "^4.1.1"
+    "lodash" "^4.17.15"
+    "normalize-package-data" "^3.0.0"
+    "q" "^1.5.1"
+    "read-pkg" "^3.0.0"
+    "read-pkg-up" "^3.0.0"
+    "through2" "^4.0.0"
+
+"conventional-changelog-ember@^2.0.9":
+  "integrity" "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz"
+  "version" "2.0.9"
+  dependencies:
+    "q" "^1.5.1"
+
+"conventional-changelog-eslint@^3.0.9":
+  "integrity" "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz"
+  "version" "3.0.9"
+  dependencies:
+    "q" "^1.5.1"
+
+"conventional-changelog-express@^2.0.6":
+  "integrity" "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz"
+  "version" "2.0.6"
+  dependencies:
+    "q" "^1.5.1"
+
+"conventional-changelog-jquery@^3.0.11":
+  "integrity" "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz"
+  "version" "3.0.11"
+  dependencies:
+    "q" "^1.5.1"
+
+"conventional-changelog-jshint@^2.0.9":
+  "integrity" "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz"
+  "version" "2.0.9"
+  dependencies:
+    "compare-func" "^2.0.0"
+    "q" "^1.5.1"
+
+"conventional-changelog-preset-loader@^2.3.4":
+  "integrity" "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz"
+  "version" "2.3.4"
+
+"conventional-changelog-writer@^5.0.0":
+  "integrity" "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz"
+  "version" "5.0.1"
+  dependencies:
+    "conventional-commits-filter" "^2.0.7"
+    "dateformat" "^3.0.0"
+    "handlebars" "^4.7.7"
+    "json-stringify-safe" "^5.0.1"
+    "lodash" "^4.17.15"
+    "meow" "^8.0.0"
+    "semver" "^6.0.0"
+    "split" "^1.0.0"
+    "through2" "^4.0.0"
+
+"conventional-changelog@^3.1.24":
+  "integrity" "sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ=="
+  "resolved" "https://registry.npmmirror.com/conventional-changelog/-/conventional-changelog-3.1.25.tgz"
+  "version" "3.1.25"
+  dependencies:
+    "conventional-changelog-angular" "^5.0.12"
+    "conventional-changelog-atom" "^2.0.8"
+    "conventional-changelog-codemirror" "^2.0.8"
+    "conventional-changelog-conventionalcommits" "^4.5.0"
+    "conventional-changelog-core" "^4.2.1"
+    "conventional-changelog-ember" "^2.0.9"
+    "conventional-changelog-eslint" "^3.0.9"
+    "conventional-changelog-express" "^2.0.6"
+    "conventional-changelog-jquery" "^3.0.11"
+    "conventional-changelog-jshint" "^2.0.9"
+    "conventional-changelog-preset-loader" "^2.3.4"
+
+"conventional-commits-filter@^2.0.7":
+  "integrity" "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA=="
+  "resolved" "https://registry.npmmirror.com/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz"
+  "version" "2.0.7"
+  dependencies:
+    "lodash.ismatch" "^4.4.0"
+    "modify-values" "^1.0.0"
+
+"conventional-commits-parser@^3.2.0":
+  "integrity" "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q=="
+  "resolved" "https://registry.npmmirror.com/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz"
+  "version" "3.2.4"
+  dependencies:
+    "is-text-path" "^1.0.1"
+    "JSONStream" "^1.0.4"
+    "lodash" "^4.17.15"
+    "meow" "^8.0.0"
+    "split2" "^3.0.0"
+    "through2" "^4.0.0"
+
+"conventional-commits-parser@^4.0.0":
+  "integrity" "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg=="
+  "resolved" "https://registry.npmmirror.com/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "is-text-path" "^1.0.1"
+    "JSONStream" "^1.3.5"
+    "meow" "^8.1.2"
+    "split2" "^3.2.2"
+
+"convert-source-map@^2.0.0":
+  "integrity" "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
+  "resolved" "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz"
+  "version" "2.0.0"
+
+"core-js-compat@^3.37.1", "core-js-compat@^3.38.0":
+  "integrity" "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw=="
+  "resolved" "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.38.1.tgz"
+  "version" "3.38.1"
+  dependencies:
+    "browserslist" "^4.23.3"
+
+"core-util-is@~1.0.0":
+  "integrity" "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+  "resolved" "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz"
+  "version" "1.0.3"
+
+"cosmiconfig-typescript-loader@^4.0.0":
+  "integrity" "sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw=="
+  "resolved" "https://registry.npmmirror.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz"
+  "version" "4.4.0"
+
+"cosmiconfig@^8.0.0", "cosmiconfig@>=7":
+  "integrity" "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA=="
+  "resolved" "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz"
+  "version" "8.3.6"
+  dependencies:
+    "import-fresh" "^3.3.0"
+    "js-yaml" "^4.1.0"
+    "parse-json" "^5.2.0"
+    "path-type" "^4.0.0"
+
+"create-require@^1.1.0":
+  "integrity" "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
+  "resolved" "https://registry.npmmirror.com/create-require/-/create-require-1.1.1.tgz"
+  "version" "1.1.1"
+
+"cross-spawn@^7.0.0", "cross-spawn@^7.0.2", "cross-spawn@^7.0.3":
+  "integrity" "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w=="
+  "resolved" "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz"
+  "version" "7.0.3"
+  dependencies:
+    "path-key" "^3.1.0"
+    "shebang-command" "^2.0.0"
+    "which" "^2.0.1"
+
+"cssesc@^3.0.0":
+  "integrity" "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
+  "resolved" "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz"
+  "version" "3.0.0"
+
+"cssstyle@^3.0.0":
+  "integrity" "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg=="
+  "resolved" "https://registry.npmmirror.com/cssstyle/-/cssstyle-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "rrweb-cssom" "^0.6.0"
+
+"dargs@^7.0.0":
+  "integrity" "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg=="
+  "resolved" "https://registry.npmmirror.com/dargs/-/dargs-7.0.0.tgz"
+  "version" "7.0.0"
+
+"data-urls@^4.0.0":
+  "integrity" "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g=="
+  "resolved" "https://registry.npmmirror.com/data-urls/-/data-urls-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "abab" "^2.0.6"
+    "whatwg-mimetype" "^3.0.0"
+    "whatwg-url" "^12.0.0"
+
+"dateformat@^3.0.0":
+  "integrity" "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="
+  "resolved" "https://registry.npmmirror.com/dateformat/-/dateformat-3.0.3.tgz"
+  "version" "3.0.3"
+
+"debug@^4.1.0", "debug@^4.1.1", "debug@^4.3.1", "debug@^4.3.2", "debug@^4.3.4", "debug@4":
+  "integrity" "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg=="
+  "resolved" "https://registry.npmmirror.com/debug/-/debug-4.3.6.tgz"
+  "version" "4.3.6"
+  dependencies:
+    "ms" "2.1.2"
+
+"debug@~3.1.0":
+  "integrity" "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g=="
+  "resolved" "https://registry.npmmirror.com/debug/-/debug-3.1.0.tgz"
+  "version" "3.1.0"
+  dependencies:
+    "ms" "2.0.0"
+
+"debug@4.3.4":
+  "integrity" "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ=="
+  "resolved" "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz"
+  "version" "4.3.4"
+  dependencies:
+    "ms" "2.1.2"
+
+"decamelize-keys@^1.1.0":
+  "integrity" "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg=="
+  "resolved" "https://registry.npmmirror.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz"
+  "version" "1.1.1"
+  dependencies:
+    "decamelize" "^1.1.0"
+    "map-obj" "^1.0.0"
+
+"decamelize@^1.1.0":
+  "integrity" "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
+  "resolved" "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz"
+  "version" "1.2.0"
+
+"decimal.js@^10.4.3":
+  "integrity" "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
+  "resolved" "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.4.3.tgz"
+  "version" "10.4.3"
+
+"deep-is@^0.1.3":
+  "integrity" "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="
+  "resolved" "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz"
+  "version" "0.1.4"
+
+"deepmerge@^4.2.2":
+  "integrity" "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="
+  "resolved" "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz"
+  "version" "4.3.1"
+
+"delayed-stream@~1.0.0":
+  "integrity" "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+  "resolved" "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz"
+  "version" "1.0.0"
+
+"diff@^4.0.1":
+  "integrity" "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="
+  "resolved" "https://registry.npmmirror.com/diff/-/diff-4.0.2.tgz"
+  "version" "4.0.2"
+
+"dir-glob@^3.0.1":
+  "integrity" "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="
+  "resolved" "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz"
+  "version" "3.0.1"
+  dependencies:
+    "path-type" "^4.0.0"
+
+"doctrine@^3.0.0":
+  "integrity" "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w=="
+  "resolved" "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "esutils" "^2.0.2"
+
+"domexception@^4.0.0":
+  "integrity" "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw=="
+  "resolved" "https://registry.npmmirror.com/domexception/-/domexception-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "webidl-conversions" "^7.0.0"
+
+"dot-prop@^5.1.0":
+  "integrity" "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q=="
+  "resolved" "https://registry.npmmirror.com/dot-prop/-/dot-prop-5.3.0.tgz"
+  "version" "5.3.0"
+  dependencies:
+    "is-obj" "^2.0.0"
+
+"eastasianwidth@^0.2.0":
+  "integrity" "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
+  "resolved" "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz"
+  "version" "0.2.0"
+
+"electron-to-chromium@^1.5.4":
+  "integrity" "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q=="
+  "resolved" "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz"
+  "version" "1.5.13"
+
+"emoji-regex@^8.0.0":
+  "integrity" "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+  "resolved" "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz"
+  "version" "8.0.0"
+
+"emoji-regex@^9.2.2":
+  "integrity" "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
+  "resolved" "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz"
+  "version" "9.2.2"
+
+"engine.io-client@~3.5.0":
+  "integrity" "sha512-ydc8uuMMDxC5KCKNJN3zZKYJk2sgyTuTZQ7Aj1DJSsLKAcizA/PzWivw8fZMIjJVBo2CJOYzntv4FSjY/Lr//g=="
+  "resolved" "https://registry.npmmirror.com/engine.io-client/-/engine.io-client-3.5.4.tgz"
+  "version" "3.5.4"
+  dependencies:
+    "component-emitter" "~1.3.0"
+    "component-inherit" "0.0.3"
+    "debug" "~3.1.0"
+    "engine.io-parser" "~2.2.0"
+    "has-cors" "1.1.0"
+    "indexof" "0.0.1"
+    "parseqs" "0.0.6"
+    "parseuri" "0.0.6"
+    "ws" "~7.5.10"
+    "xmlhttprequest-ssl" "~1.6.2"
+    "yeast" "0.1.2"
+
+"engine.io-parser@~2.2.0":
+  "integrity" "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg=="
+  "resolved" "https://registry.npmmirror.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz"
+  "version" "2.2.1"
+  dependencies:
+    "after" "0.8.2"
+    "arraybuffer.slice" "~0.0.7"
+    "base64-arraybuffer" "0.1.4"
+    "blob" "0.0.5"
+    "has-binary2" "~1.0.2"
+
+"enquirer@^2.4.1", "enquirer@>= 2.3.0 < 3":
+  "integrity" "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="
+  "resolved" "https://registry.npmmirror.com/enquirer/-/enquirer-2.4.1.tgz"
+  "version" "2.4.1"
+  dependencies:
+    "ansi-colors" "^4.1.1"
+    "strip-ansi" "^6.0.1"
+
+"entities@^4.4.0":
+  "integrity" "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
+  "resolved" "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz"
+  "version" "4.5.0"
+
+"error-ex@^1.3.1":
+  "integrity" "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="
+  "resolved" "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz"
+  "version" "1.3.2"
+  dependencies:
+    "is-arrayish" "^0.2.1"
+
+"escalade@^3.1.1", "escalade@^3.1.2":
+  "integrity" "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA=="
+  "resolved" "https://registry.npmmirror.com/escalade/-/escalade-3.1.2.tgz"
+  "version" "3.1.2"
+
+"escape-string-regexp@^1.0.5":
+  "integrity" "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
+  "resolved" "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz"
+  "version" "1.0.5"
+
+"escape-string-regexp@^4.0.0":
+  "integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
+  "resolved" "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz"
+  "version" "4.0.0"
+
+"eslint-config-prettier@*", "eslint-config-prettier@^9.0.0":
+  "integrity" "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw=="
+  "resolved" "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz"
+  "version" "9.1.0"
+
+"eslint-config-prettier@^8.8.0":
+  "integrity" "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg=="
+  "resolved" "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz"
+  "version" "8.10.0"
+
+"eslint-plugin-prettier@^5.0.0":
+  "integrity" "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw=="
+  "resolved" "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz"
+  "version" "5.2.1"
+  dependencies:
+    "prettier-linter-helpers" "^1.0.0"
+    "synckit" "^0.9.1"
+
+"eslint-plugin-vue@^9.0.0":
+  "integrity" "sha512-ShrihdjIhOTxs+MfWun6oJWuk+g/LAhN+CiuOl/jjkG3l0F2AuK5NMTaWqyvBgkFtpYmyks6P4603mLmhNJW8g=="
+  "resolved" "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.28.0.tgz"
+  "version" "9.28.0"
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.4.0"
+    "globals" "^13.24.0"
+    "natural-compare" "^1.4.0"
+    "nth-check" "^2.1.1"
+    "postcss-selector-parser" "^6.0.15"
+    "semver" "^7.6.3"
+    "vue-eslint-parser" "^9.4.3"
+    "xml-name-validator" "^4.0.0"
+
+"eslint-scope@^5.1.1":
+  "integrity" "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw=="
+  "resolved" "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz"
+  "version" "5.1.1"
+  dependencies:
+    "esrecurse" "^4.3.0"
+    "estraverse" "^4.1.1"
+
+"eslint-scope@^7.1.1", "eslint-scope@^7.2.2":
+  "integrity" "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="
+  "resolved" "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.2.2.tgz"
+  "version" "7.2.2"
+  dependencies:
+    "esrecurse" "^4.3.0"
+    "estraverse" "^5.2.0"
+
+"eslint-visitor-keys@^3.3.0", "eslint-visitor-keys@^3.4.1", "eslint-visitor-keys@^3.4.3":
+  "integrity" "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="
+  "resolved" "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
+  "version" "3.4.3"
+
+"eslint@*", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^6.2.0 || ^7.0.0 || ^8.0.0", "eslint@^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^8.48.0", "eslint@>= 8.0.0", "eslint@>=6.0.0", "eslint@>=7.0.0", "eslint@>=8.0.0":
+  "integrity" "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ=="
+  "resolved" "https://registry.npmmirror.com/eslint/-/eslint-8.57.0.tgz"
+  "version" "8.57.0"
+  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"
+
+"espree@^9.3.1", "espree@^9.6.0", "espree@^9.6.1":
+  "integrity" "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="
+  "resolved" "https://registry.npmmirror.com/espree/-/espree-9.6.1.tgz"
+  "version" "9.6.1"
+  dependencies:
+    "acorn" "^8.9.0"
+    "acorn-jsx" "^5.3.2"
+    "eslint-visitor-keys" "^3.4.1"
+
+"esquery@^1.4.0", "esquery@^1.4.2":
+  "integrity" "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="
+  "resolved" "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz"
+  "version" "1.6.0"
+  dependencies:
+    "estraverse" "^5.1.0"
+
+"esrecurse@^4.3.0":
+  "integrity" "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="
+  "resolved" "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz"
+  "version" "4.3.0"
+  dependencies:
+    "estraverse" "^5.2.0"
+
+"estraverse@^4.1.1":
+  "integrity" "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
+  "resolved" "https://registry.npmmirror.com/estraverse/-/estraverse-4.3.0.tgz"
+  "version" "4.3.0"
+
+"estraverse@^5.1.0", "estraverse@^5.2.0":
+  "integrity" "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="
+  "resolved" "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz"
+  "version" "5.3.0"
+
+"estree-walker@^2.0.2":
+  "integrity" "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+  "resolved" "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz"
+  "version" "2.0.2"
+
+"esutils@^2.0.2":
+  "integrity" "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
+  "resolved" "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz"
+  "version" "2.0.3"
+
+"eventemitter3@^5.0.1":
+  "integrity" "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
+  "resolved" "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz"
+  "version" "5.0.1"
+
+"execa@^5.0.0":
+  "integrity" "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="
+  "resolved" "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz"
+  "version" "5.1.1"
+  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"
+
+"execa@7.2.0":
+  "integrity" "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA=="
+  "resolved" "https://registry.npmmirror.com/execa/-/execa-7.2.0.tgz"
+  "version" "7.2.0"
+  dependencies:
+    "cross-spawn" "^7.0.3"
+    "get-stream" "^6.0.1"
+    "human-signals" "^4.3.0"
+    "is-stream" "^3.0.0"
+    "merge-stream" "^2.0.0"
+    "npm-run-path" "^5.1.0"
+    "onetime" "^6.0.0"
+    "signal-exit" "^3.0.7"
+    "strip-final-newline" "^3.0.0"
+
+"fast-deep-equal@^3.1.1", "fast-deep-equal@^3.1.3":
+  "integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
+  "resolved" "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
+  "version" "3.1.3"
+
+"fast-diff@^1.1.2":
+  "integrity" "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="
+  "resolved" "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz"
+  "version" "1.3.0"
+
+"fast-glob@^3.2.9":
+  "integrity" "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow=="
+  "resolved" "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.2.tgz"
+  "version" "3.3.2"
+  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"
+
+"fast-json-stable-stringify@^2.0.0":
+  "integrity" "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
+  "resolved" "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz"
+  "version" "2.1.0"
+
+"fast-levenshtein@^2.0.6":
+  "integrity" "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="
+  "resolved" "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz"
+  "version" "2.0.6"
+
+"fast-uri@^3.0.1":
+  "integrity" "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw=="
+  "resolved" "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.0.1.tgz"
+  "version" "3.0.1"
+
+"fastq@^1.6.0":
+  "integrity" "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w=="
+  "resolved" "https://registry.npmmirror.com/fastq/-/fastq-1.17.1.tgz"
+  "version" "1.17.1"
+  dependencies:
+    "reusify" "^1.0.4"
+
+"file-entry-cache@^6.0.1":
+  "integrity" "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg=="
+  "resolved" "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz"
+  "version" "6.0.1"
+  dependencies:
+    "flat-cache" "^3.0.4"
+
+"fill-range@^7.1.1":
+  "integrity" "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="
+  "resolved" "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz"
+  "version" "7.1.1"
+  dependencies:
+    "to-regex-range" "^5.0.1"
+
+"find-up@^2.0.0":
+  "integrity" "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ=="
+  "resolved" "https://registry.npmmirror.com/find-up/-/find-up-2.1.0.tgz"
+  "version" "2.1.0"
+  dependencies:
+    "locate-path" "^2.0.0"
+
+"find-up@^4.1.0":
+  "integrity" "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="
+  "resolved" "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz"
+  "version" "4.1.0"
+  dependencies:
+    "locate-path" "^5.0.0"
+    "path-exists" "^4.0.0"
+
+"find-up@^5.0.0":
+  "integrity" "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="
+  "resolved" "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz"
+  "version" "5.0.0"
+  dependencies:
+    "locate-path" "^6.0.0"
+    "path-exists" "^4.0.0"
+
+"flat-cache@^3.0.4":
+  "integrity" "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw=="
+  "resolved" "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.2.0.tgz"
+  "version" "3.2.0"
+  dependencies:
+    "flatted" "^3.2.9"
+    "keyv" "^4.5.3"
+    "rimraf" "^3.0.2"
+
+"flatted@^3.2.9":
+  "integrity" "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="
+  "resolved" "https://registry.npmmirror.com/flatted/-/flatted-3.3.1.tgz"
+  "version" "3.3.1"
+
+"foreground-child@^3.1.0":
+  "integrity" "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg=="
+  "resolved" "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.0.tgz"
+  "version" "3.3.0"
+  dependencies:
+    "cross-spawn" "^7.0.0"
+    "signal-exit" "^4.0.1"
+
+"form-data@^4.0.0":
+  "integrity" "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww=="
+  "resolved" "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "asynckit" "^0.4.0"
+    "combined-stream" "^1.0.8"
+    "mime-types" "^2.1.12"
+
+"fs-extra@^11.0.0":
+  "integrity" "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw=="
+  "resolved" "https://registry.npmmirror.com/fs-extra/-/fs-extra-11.2.0.tgz"
+  "version" "11.2.0"
+  dependencies:
+    "graceful-fs" "^4.2.0"
+    "jsonfile" "^6.0.1"
+    "universalify" "^2.0.0"
+
+"fs.realpath@^1.0.0":
+  "integrity" "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
+  "resolved" "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz"
+  "version" "1.0.0"
+
+"fsevents@~2.3.2":
+  "integrity" "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
+  "resolved" "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz"
+  "version" "2.3.3"
+
+"function-bind@^1.1.2":
+  "integrity" "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
+  "resolved" "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz"
+  "version" "1.1.2"
+
+"gensync@^1.0.0-beta.2":
+  "integrity" "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="
+  "resolved" "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz"
+  "version" "1.0.0-beta.2"
+
+"get-caller-file@^2.0.5":
+  "integrity" "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+  "resolved" "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz"
+  "version" "2.0.5"
+
+"get-pkg-repo@^4.0.0":
+  "integrity" "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA=="
+  "resolved" "https://registry.npmmirror.com/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz"
+  "version" "4.2.1"
+  dependencies:
+    "@hutson/parse-repository-url" "^3.0.0"
+    "hosted-git-info" "^4.0.0"
+    "through2" "^2.0.0"
+    "yargs" "^16.2.0"
+
+"get-stream@^6.0.0", "get-stream@^6.0.1":
+  "integrity" "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="
+  "resolved" "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz"
+  "version" "6.0.1"
+
+"git-raw-commits@^2.0.11", "git-raw-commits@^2.0.8":
+  "integrity" "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A=="
+  "resolved" "https://registry.npmmirror.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz"
+  "version" "2.0.11"
+  dependencies:
+    "dargs" "^7.0.0"
+    "lodash" "^4.17.15"
+    "meow" "^8.0.0"
+    "split2" "^3.0.0"
+    "through2" "^4.0.0"
+
+"git-remote-origin-url@^2.0.0":
+  "integrity" "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw=="
+  "resolved" "https://registry.npmmirror.com/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz"
+  "version" "2.0.0"
+  dependencies:
+    "gitconfiglocal" "^1.0.0"
+    "pify" "^2.3.0"
+
+"git-semver-tags@^4.1.1":
+  "integrity" "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA=="
+  "resolved" "https://registry.npmmirror.com/git-semver-tags/-/git-semver-tags-4.1.1.tgz"
+  "version" "4.1.1"
+  dependencies:
+    "meow" "^8.0.0"
+    "semver" "^6.0.0"
+
+"gitconfiglocal@^1.0.0":
+  "integrity" "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ=="
+  "resolved" "https://registry.npmmirror.com/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz"
+  "version" "1.0.0"
+  dependencies:
+    "ini" "^1.3.2"
+
+"glob-parent@^5.1.2":
+  "integrity" "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="
+  "resolved" "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz"
+  "version" "5.1.2"
+  dependencies:
+    "is-glob" "^4.0.1"
+
+"glob-parent@^6.0.2":
+  "integrity" "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="
+  "resolved" "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz"
+  "version" "6.0.2"
+  dependencies:
+    "is-glob" "^4.0.3"
+
+"glob@^10.3.7":
+  "integrity" "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="
+  "resolved" "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz"
+  "version" "10.4.5"
+  dependencies:
+    "foreground-child" "^3.1.0"
+    "jackspeak" "^3.1.2"
+    "minimatch" "^9.0.4"
+    "minipass" "^7.1.2"
+    "package-json-from-dist" "^1.0.0"
+    "path-scurry" "^1.11.1"
+
+"glob@^7.1.3":
+  "integrity" "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="
+  "resolved" "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz"
+  "version" "7.2.3"
+  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"
+
+"glob@^8.0.3":
+  "integrity" "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="
+  "resolved" "https://registry.npmmirror.com/glob/-/glob-8.1.0.tgz"
+  "version" "8.1.0"
+  dependencies:
+    "fs.realpath" "^1.0.0"
+    "inflight" "^1.0.4"
+    "inherits" "2"
+    "minimatch" "^5.0.1"
+    "once" "^1.3.0"
+
+"global-dirs@^0.1.1":
+  "integrity" "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg=="
+  "resolved" "https://registry.npmmirror.com/global-dirs/-/global-dirs-0.1.1.tgz"
+  "version" "0.1.1"
+  dependencies:
+    "ini" "^1.3.4"
+
+"globals@^11.1.0":
+  "integrity" "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
+  "resolved" "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz"
+  "version" "11.12.0"
+
+"globals@^13.19.0":
+  "integrity" "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="
+  "resolved" "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz"
+  "version" "13.24.0"
+  dependencies:
+    "type-fest" "^0.20.2"
+
+"globals@^13.24.0":
+  "integrity" "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ=="
+  "resolved" "https://registry.npmmirror.com/globals/-/globals-13.24.0.tgz"
+  "version" "13.24.0"
+  dependencies:
+    "type-fest" "^0.20.2"
+
+"globby@^11.1.0":
+  "integrity" "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="
+  "resolved" "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz"
+  "version" "11.1.0"
+  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"
+
+"graceful-fs@^4.1.2", "graceful-fs@^4.1.6", "graceful-fs@^4.2.0":
+  "integrity" "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
+  "resolved" "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz"
+  "version" "4.2.11"
+
+"graphemer@^1.4.0":
+  "integrity" "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="
+  "resolved" "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz"
+  "version" "1.4.0"
+
+"handlebars@^4.7.7":
+  "integrity" "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="
+  "resolved" "https://registry.npmmirror.com/handlebars/-/handlebars-4.7.8.tgz"
+  "version" "4.7.8"
+  dependencies:
+    "minimist" "^1.2.5"
+    "neo-async" "^2.6.2"
+    "source-map" "^0.6.1"
+    "wordwrap" "^1.0.0"
+  optionalDependencies:
+    "uglify-js" "^3.1.4"
+
+"hard-rejection@^2.1.0":
+  "integrity" "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA=="
+  "resolved" "https://registry.npmmirror.com/hard-rejection/-/hard-rejection-2.1.0.tgz"
+  "version" "2.1.0"
+
+"has-binary2@~1.0.2":
+  "integrity" "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw=="
+  "resolved" "https://registry.npmmirror.com/has-binary2/-/has-binary2-1.0.3.tgz"
+  "version" "1.0.3"
+  dependencies:
+    "isarray" "2.0.1"
+
+"has-cors@1.1.0":
+  "integrity" "sha512-g5VNKdkFuUuVCP9gYfDJHjK2nqdQJ7aDLTnycnc2+RvsOQbuLdF5pm7vuE5J76SEBIQjs4kQY/BWq74JUmjbXA=="
+  "resolved" "https://registry.npmmirror.com/has-cors/-/has-cors-1.1.0.tgz"
+  "version" "1.1.0"
+
+"has-flag@^3.0.0":
+  "integrity" "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
+  "resolved" "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz"
+  "version" "3.0.0"
+
+"has-flag@^4.0.0":
+  "integrity" "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
+  "resolved" "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz"
+  "version" "4.0.0"
+
+"hasown@^2.0.2":
+  "integrity" "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="
+  "resolved" "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz"
+  "version" "2.0.2"
+  dependencies:
+    "function-bind" "^1.1.2"
+
+"hosted-git-info@^2.1.4":
+  "integrity" "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="
+  "resolved" "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz"
+  "version" "2.8.9"
+
+"hosted-git-info@^4.0.0", "hosted-git-info@^4.0.1":
+  "integrity" "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="
+  "resolved" "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz"
+  "version" "4.1.0"
+  dependencies:
+    "lru-cache" "^6.0.0"
+
+"html-encoding-sniffer@^3.0.0":
+  "integrity" "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA=="
+  "resolved" "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "whatwg-encoding" "^2.0.0"
+
+"http-proxy-agent@^5.0.0":
+  "integrity" "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="
+  "resolved" "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz"
+  "version" "5.0.0"
+  dependencies:
+    "@tootallnate/once" "2"
+    "agent-base" "6"
+    "debug" "4"
+
+"https-proxy-agent@^5.0.1":
+  "integrity" "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="
+  "resolved" "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz"
+  "version" "5.0.1"
+  dependencies:
+    "agent-base" "6"
+    "debug" "4"
+
+"human-signals@^2.1.0":
+  "integrity" "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
+  "resolved" "https://registry.npmmirror.com/human-signals/-/human-signals-2.1.0.tgz"
+  "version" "2.1.0"
+
+"human-signals@^4.3.0":
+  "integrity" "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ=="
+  "resolved" "https://registry.npmmirror.com/human-signals/-/human-signals-4.3.1.tgz"
+  "version" "4.3.1"
+
+"husky@^8.0.3":
+  "integrity" "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg=="
+  "resolved" "https://registry.npmmirror.com/husky/-/husky-8.0.3.tgz"
+  "version" "8.0.3"
+
+"iconv-lite@0.6.3":
+  "integrity" "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="
+  "resolved" "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz"
+  "version" "0.6.3"
+  dependencies:
+    "safer-buffer" ">= 2.1.2 < 3.0.0"
+
+"ignore@^5.2.0", "ignore@^5.2.4":
+  "integrity" "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="
+  "resolved" "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz"
+  "version" "5.3.2"
+
+"import-fresh@^3.0.0", "import-fresh@^3.2.1", "import-fresh@^3.3.0":
+  "integrity" "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw=="
+  "resolved" "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz"
+  "version" "3.3.0"
+  dependencies:
+    "parent-module" "^1.0.0"
+    "resolve-from" "^4.0.0"
+
+"imurmurhash@^0.1.4":
+  "integrity" "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="
+  "resolved" "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz"
+  "version" "0.1.4"
+
+"indent-string@^4.0.0":
+  "integrity" "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
+  "resolved" "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz"
+  "version" "4.0.0"
+
+"indexof@0.0.1":
+  "integrity" "sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg=="
+  "resolved" "https://registry.npmmirror.com/indexof/-/indexof-0.0.1.tgz"
+  "version" "0.0.1"
+
+"inflight@^1.0.4":
+  "integrity" "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="
+  "resolved" "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz"
+  "version" "1.0.6"
+  dependencies:
+    "once" "^1.3.0"
+    "wrappy" "1"
+
+"inherits@^2.0.3", "inherits@~2.0.3", "inherits@2":
+  "integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+  "resolved" "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz"
+  "version" "2.0.4"
+
+"ini@^1.3.2", "ini@^1.3.4":
+  "integrity" "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
+  "resolved" "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz"
+  "version" "1.3.8"
+
+"is-arrayish@^0.2.1":
+  "integrity" "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="
+  "resolved" "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.2.1.tgz"
+  "version" "0.2.1"
+
+"is-builtin-module@^3.2.1":
+  "integrity" "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A=="
+  "resolved" "https://registry.npmmirror.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz"
+  "version" "3.2.1"
+  dependencies:
+    "builtin-modules" "^3.3.0"
+
+"is-core-module@^2.13.0", "is-core-module@^2.5.0":
+  "integrity" "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ=="
+  "resolved" "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.15.1.tgz"
+  "version" "2.15.1"
+  dependencies:
+    "hasown" "^2.0.2"
+
+"is-extglob@^2.1.1":
+  "integrity" "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
+  "resolved" "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz"
+  "version" "2.1.1"
+
+"is-fullwidth-code-point@^3.0.0":
+  "integrity" "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+  "resolved" "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz"
+  "version" "3.0.0"
+
+"is-fullwidth-code-point@^4.0.0":
+  "integrity" "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="
+  "resolved" "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz"
+  "version" "4.0.0"
+
+"is-glob@^4.0.0", "is-glob@^4.0.1", "is-glob@^4.0.3":
+  "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="
+  "resolved" "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz"
+  "version" "4.0.3"
+  dependencies:
+    "is-extglob" "^2.1.1"
+
+"is-module@^1.0.0":
+  "integrity" "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="
+  "resolved" "https://registry.npmmirror.com/is-module/-/is-module-1.0.0.tgz"
+  "version" "1.0.0"
+
+"is-number@^7.0.0":
+  "integrity" "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+  "resolved" "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz"
+  "version" "7.0.0"
+
+"is-obj@^2.0.0":
+  "integrity" "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
+  "resolved" "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz"
+  "version" "2.0.0"
+
+"is-path-inside@^3.0.3":
+  "integrity" "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="
+  "resolved" "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-3.0.3.tgz"
+  "version" "3.0.3"
+
+"is-plain-obj@^1.1.0":
+  "integrity" "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="
+  "resolved" "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz"
+  "version" "1.1.0"
+
+"is-potential-custom-element-name@^1.0.1":
+  "integrity" "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
+  "resolved" "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz"
+  "version" "1.0.1"
+
+"is-reference@1.2.1":
+  "integrity" "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="
+  "resolved" "https://registry.npmmirror.com/is-reference/-/is-reference-1.2.1.tgz"
+  "version" "1.2.1"
+  dependencies:
+    "@types/estree" "*"
+
+"is-stream@^2.0.0":
+  "integrity" "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
+  "resolved" "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz"
+  "version" "2.0.1"
+
+"is-stream@^3.0.0":
+  "integrity" "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="
+  "resolved" "https://registry.npmmirror.com/is-stream/-/is-stream-3.0.0.tgz"
+  "version" "3.0.0"
+
+"is-text-path@^1.0.1":
+  "integrity" "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w=="
+  "resolved" "https://registry.npmmirror.com/is-text-path/-/is-text-path-1.0.1.tgz"
+  "version" "1.0.1"
+  dependencies:
+    "text-extensions" "^1.0.0"
+
+"isarray@~1.0.0":
+  "integrity" "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+  "resolved" "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz"
+  "version" "1.0.0"
+
+"isarray@2.0.1":
+  "integrity" "sha512-c2cu3UxbI+b6kR3fy0nRnAhodsvR9dx7U5+znCOzdj6IfP3upFURTr0Xl5BlQZNKZjEtxrmVyfSdeE3O57smoQ=="
+  "resolved" "https://registry.npmmirror.com/isarray/-/isarray-2.0.1.tgz"
+  "version" "2.0.1"
+
+"isexe@^2.0.0":
+  "integrity" "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+  "resolved" "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz"
+  "version" "2.0.0"
+
+"jackspeak@^3.1.2":
+  "integrity" "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="
+  "resolved" "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz"
+  "version" "3.4.3"
+  dependencies:
+    "@isaacs/cliui" "^8.0.2"
+  optionalDependencies:
+    "@pkgjs/parseargs" "^0.11.0"
+
+"js-tokens@^4.0.0":
+  "integrity" "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+  "resolved" "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz"
+  "version" "4.0.0"
+
+"js-yaml@^4.1.0":
+  "integrity" "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="
+  "resolved" "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz"
+  "version" "4.1.0"
+  dependencies:
+    "argparse" "^2.0.1"
+
+"jsdom@^22.1.0":
+  "integrity" "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw=="
+  "resolved" "https://registry.npmmirror.com/jsdom/-/jsdom-22.1.0.tgz"
+  "version" "22.1.0"
+  dependencies:
+    "abab" "^2.0.6"
+    "cssstyle" "^3.0.0"
+    "data-urls" "^4.0.0"
+    "decimal.js" "^10.4.3"
+    "domexception" "^4.0.0"
+    "form-data" "^4.0.0"
+    "html-encoding-sniffer" "^3.0.0"
+    "http-proxy-agent" "^5.0.0"
+    "https-proxy-agent" "^5.0.1"
+    "is-potential-custom-element-name" "^1.0.1"
+    "nwsapi" "^2.2.4"
+    "parse5" "^7.1.2"
+    "rrweb-cssom" "^0.6.0"
+    "saxes" "^6.0.0"
+    "symbol-tree" "^3.2.4"
+    "tough-cookie" "^4.1.2"
+    "w3c-xmlserializer" "^4.0.0"
+    "webidl-conversions" "^7.0.0"
+    "whatwg-encoding" "^2.0.0"
+    "whatwg-mimetype" "^3.0.0"
+    "whatwg-url" "^12.0.1"
+    "ws" "^8.13.0"
+    "xml-name-validator" "^4.0.0"
+
+"jsesc@^2.5.1":
+  "integrity" "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
+  "resolved" "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz"
+  "version" "2.5.2"
+
+"jsesc@~0.5.0":
+  "integrity" "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA=="
+  "resolved" "https://registry.npmmirror.com/jsesc/-/jsesc-0.5.0.tgz"
+  "version" "0.5.0"
+
+"json-buffer@3.0.1":
+  "integrity" "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
+  "resolved" "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz"
+  "version" "3.0.1"
+
+"json-parse-better-errors@^1.0.1":
+  "integrity" "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
+  "resolved" "https://registry.npmmirror.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz"
+  "version" "1.0.2"
+
+"json-parse-even-better-errors@^2.3.0":
+  "integrity" "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
+  "resolved" "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz"
+  "version" "2.3.1"
+
+"json-schema-traverse@^0.4.1":
+  "integrity" "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
+  "resolved" "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz"
+  "version" "0.4.1"
+
+"json-schema-traverse@^1.0.0":
+  "integrity" "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
+  "resolved" "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz"
+  "version" "1.0.0"
+
+"json-stable-stringify-without-jsonify@^1.0.1":
+  "integrity" "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="
+  "resolved" "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
+  "version" "1.0.1"
+
+"json-stringify-safe@^5.0.1":
+  "integrity" "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
+  "resolved" "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
+  "version" "5.0.1"
+
+"json5@^2.2.3":
+  "integrity" "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
+  "resolved" "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz"
+  "version" "2.2.3"
+
+"jsonfile@^6.0.1":
+  "integrity" "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="
+  "resolved" "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz"
+  "version" "6.1.0"
+  dependencies:
+    "universalify" "^2.0.0"
+  optionalDependencies:
+    "graceful-fs" "^4.1.6"
+
+"jsonparse@^1.2.0":
+  "integrity" "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg=="
+  "resolved" "https://registry.npmmirror.com/jsonparse/-/jsonparse-1.3.1.tgz"
+  "version" "1.3.1"
+
+"JSONStream@^1.0.4", "JSONStream@^1.3.5":
+  "integrity" "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ=="
+  "resolved" "https://registry.npmmirror.com/JSONStream/-/JSONStream-1.3.5.tgz"
+  "version" "1.3.5"
+  dependencies:
+    "jsonparse" "^1.2.0"
+    "through" ">=2.2.7 <3"
+
+"keyv@^4.5.3":
+  "integrity" "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="
+  "resolved" "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz"
+  "version" "4.5.4"
+  dependencies:
+    "json-buffer" "3.0.1"
+
+"kind-of@^6.0.3":
+  "integrity" "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
+  "resolved" "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz"
+  "version" "6.0.3"
+
+"levn@^0.4.1":
+  "integrity" "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="
+  "resolved" "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz"
+  "version" "0.4.1"
+  dependencies:
+    "prelude-ls" "^1.2.1"
+    "type-check" "~0.4.0"
+
+"lilconfig@2.1.0":
+  "integrity" "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="
+  "resolved" "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.1.0.tgz"
+  "version" "2.1.0"
+
+"lines-and-columns@^1.1.6":
+  "integrity" "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
+  "resolved" "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
+  "version" "1.2.4"
+
+"lint-staged@^14.0.1":
+  "integrity" "sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw=="
+  "resolved" "https://registry.npmmirror.com/lint-staged/-/lint-staged-14.0.1.tgz"
+  "version" "14.0.1"
+  dependencies:
+    "chalk" "5.3.0"
+    "commander" "11.0.0"
+    "debug" "4.3.4"
+    "execa" "7.2.0"
+    "lilconfig" "2.1.0"
+    "listr2" "6.6.1"
+    "micromatch" "4.0.5"
+    "pidtree" "0.6.0"
+    "string-argv" "0.3.2"
+    "yaml" "2.3.1"
+
+"listr2@6.6.1":
+  "integrity" "sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg=="
+  "resolved" "https://registry.npmmirror.com/listr2/-/listr2-6.6.1.tgz"
+  "version" "6.6.1"
+  dependencies:
+    "cli-truncate" "^3.1.0"
+    "colorette" "^2.0.20"
+    "eventemitter3" "^5.0.1"
+    "log-update" "^5.0.1"
+    "rfdc" "^1.3.0"
+    "wrap-ansi" "^8.1.0"
+
+"load-json-file@^4.0.0":
+  "integrity" "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw=="
+  "resolved" "https://registry.npmmirror.com/load-json-file/-/load-json-file-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "graceful-fs" "^4.1.2"
+    "parse-json" "^4.0.0"
+    "pify" "^3.0.0"
+    "strip-bom" "^3.0.0"
+
+"locate-path@^2.0.0":
+  "integrity" "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA=="
+  "resolved" "https://registry.npmmirror.com/locate-path/-/locate-path-2.0.0.tgz"
+  "version" "2.0.0"
+  dependencies:
+    "p-locate" "^2.0.0"
+    "path-exists" "^3.0.0"
+
+"locate-path@^5.0.0":
+  "integrity" "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="
+  "resolved" "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz"
+  "version" "5.0.0"
+  dependencies:
+    "p-locate" "^4.1.0"
+
+"locate-path@^6.0.0":
+  "integrity" "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="
+  "resolved" "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz"
+  "version" "6.0.0"
+  dependencies:
+    "p-locate" "^5.0.0"
+
+"lodash.camelcase@^4.3.0":
+  "integrity" "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
+  "resolved" "https://registry.npmmirror.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz"
+  "version" "4.3.0"
+
+"lodash.debounce@^4.0.8":
+  "integrity" "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
+  "resolved" "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
+  "version" "4.0.8"
+
+"lodash.isfunction@^3.0.9":
+  "integrity" "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
+  "resolved" "https://registry.npmmirror.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz"
+  "version" "3.0.9"
+
+"lodash.ismatch@^4.4.0":
+  "integrity" "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g=="
+  "resolved" "https://registry.npmmirror.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz"
+  "version" "4.4.0"
+
+"lodash.isplainobject@^4.0.6":
+  "integrity" "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+  "resolved" "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz"
+  "version" "4.0.6"
+
+"lodash.kebabcase@^4.1.1":
+  "integrity" "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g=="
+  "resolved" "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz"
+  "version" "4.1.1"
+
+"lodash.merge@^4.6.2":
+  "integrity" "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
+  "resolved" "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz"
+  "version" "4.6.2"
+
+"lodash.mergewith@^4.6.2":
+  "integrity" "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ=="
+  "resolved" "https://registry.npmmirror.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz"
+  "version" "4.6.2"
+
+"lodash.snakecase@^4.1.1":
+  "integrity" "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
+  "resolved" "https://registry.npmmirror.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz"
+  "version" "4.1.1"
+
+"lodash.startcase@^4.4.0":
+  "integrity" "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg=="
+  "resolved" "https://registry.npmmirror.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz"
+  "version" "4.4.0"
+
+"lodash.uniq@^4.5.0":
+  "integrity" "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ=="
+  "resolved" "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz"
+  "version" "4.5.0"
+
+"lodash.upperfirst@^4.3.1":
+  "integrity" "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg=="
+  "resolved" "https://registry.npmmirror.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz"
+  "version" "4.3.1"
+
+"lodash@^4.17.15", "lodash@^4.17.21":
+  "integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+  "resolved" "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz"
+  "version" "4.17.21"
+
+"log-update@^5.0.1":
+  "integrity" "sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw=="
+  "resolved" "https://registry.npmmirror.com/log-update/-/log-update-5.0.1.tgz"
+  "version" "5.0.1"
+  dependencies:
+    "ansi-escapes" "^5.0.0"
+    "cli-cursor" "^4.0.0"
+    "slice-ansi" "^5.0.0"
+    "strip-ansi" "^7.0.1"
+    "wrap-ansi" "^8.0.1"
+
+"lru-cache@^10.2.0":
+  "integrity" "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="
+  "resolved" "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz"
+  "version" "10.4.3"
+
+"lru-cache@^5.1.1":
+  "integrity" "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="
+  "resolved" "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz"
+  "version" "5.1.1"
+  dependencies:
+    "yallist" "^3.0.2"
+
+"lru-cache@^6.0.0":
+  "integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="
+  "resolved" "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz"
+  "version" "6.0.0"
+  dependencies:
+    "yallist" "^4.0.0"
+
+"magic-string@^0.30.2", "magic-string@^0.30.3":
+  "integrity" "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A=="
+  "resolved" "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.11.tgz"
+  "version" "0.30.11"
+  dependencies:
+    "@jridgewell/sourcemap-codec" "^1.5.0"
+
+"make-error@^1.1.1":
+  "integrity" "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
+  "resolved" "https://registry.npmmirror.com/make-error/-/make-error-1.3.6.tgz"
+  "version" "1.3.6"
+
+"map-obj@^1.0.0":
+  "integrity" "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="
+  "resolved" "https://registry.npmmirror.com/map-obj/-/map-obj-1.0.1.tgz"
+  "version" "1.0.1"
+
+"map-obj@^4.0.0":
+  "integrity" "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ=="
+  "resolved" "https://registry.npmmirror.com/map-obj/-/map-obj-4.3.0.tgz"
+  "version" "4.3.0"
+
+"meow@^8.0.0", "meow@^8.1.2":
+  "integrity" "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q=="
+  "resolved" "https://registry.npmmirror.com/meow/-/meow-8.1.2.tgz"
+  "version" "8.1.2"
+  dependencies:
+    "@types/minimist" "^1.2.0"
+    "camelcase-keys" "^6.2.2"
+    "decamelize-keys" "^1.1.0"
+    "hard-rejection" "^2.1.0"
+    "minimist-options" "4.1.0"
+    "normalize-package-data" "^3.0.0"
+    "read-pkg-up" "^7.0.1"
+    "redent" "^3.0.0"
+    "trim-newlines" "^3.0.0"
+    "type-fest" "^0.18.0"
+    "yargs-parser" "^20.2.3"
+
+"merge-stream@^2.0.0":
+  "integrity" "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
+  "resolved" "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz"
+  "version" "2.0.0"
+
+"merge2@^1.3.0", "merge2@^1.4.1":
+  "integrity" "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
+  "resolved" "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz"
+  "version" "1.4.1"
+
+"micromatch@^4.0.4":
+  "integrity" "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="
+  "resolved" "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz"
+  "version" "4.0.8"
+  dependencies:
+    "braces" "^3.0.3"
+    "picomatch" "^2.3.1"
+
+"micromatch@4.0.5":
+  "integrity" "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA=="
+  "resolved" "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz"
+  "version" "4.0.5"
+  dependencies:
+    "braces" "^3.0.2"
+    "picomatch" "^2.3.1"
+
+"mime-db@1.52.0":
+  "integrity" "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+  "resolved" "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz"
+  "version" "1.52.0"
+
+"mime-types@^2.1.12":
+  "integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="
+  "resolved" "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz"
+  "version" "2.1.35"
+  dependencies:
+    "mime-db" "1.52.0"
+
+"mime@^2":
+  "integrity" "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
+  "resolved" "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz"
+  "version" "2.6.0"
+
+"mimic-fn@^2.1.0":
+  "integrity" "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
+  "resolved" "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz"
+  "version" "2.1.0"
+
+"mimic-fn@^4.0.0":
+  "integrity" "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="
+  "resolved" "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz"
+  "version" "4.0.0"
+
+"min-indent@^1.0.0":
+  "integrity" "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="
+  "resolved" "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz"
+  "version" "1.0.1"
+
+"minimatch@^3.0.5", "minimatch@^3.1.1", "minimatch@^3.1.2":
+  "integrity" "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="
+  "resolved" "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz"
+  "version" "3.1.2"
+  dependencies:
+    "brace-expansion" "^1.1.7"
+
+"minimatch@^5.0.1":
+  "integrity" "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="
+  "resolved" "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz"
+  "version" "5.1.6"
+  dependencies:
+    "brace-expansion" "^2.0.1"
+
+"minimatch@^9.0.4":
+  "integrity" "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="
+  "resolved" "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz"
+  "version" "9.0.5"
+  dependencies:
+    "brace-expansion" "^2.0.1"
+
+"minimatch@9.0.3":
+  "integrity" "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="
+  "resolved" "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz"
+  "version" "9.0.3"
+  dependencies:
+    "brace-expansion" "^2.0.1"
+
+"minimist-options@4.1.0":
+  "integrity" "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A=="
+  "resolved" "https://registry.npmmirror.com/minimist-options/-/minimist-options-4.1.0.tgz"
+  "version" "4.1.0"
+  dependencies:
+    "arrify" "^1.0.1"
+    "is-plain-obj" "^1.1.0"
+    "kind-of" "^6.0.3"
+
+"minimist@^1.2.5", "minimist@^1.2.6":
+  "integrity" "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
+  "resolved" "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz"
+  "version" "1.2.8"
+
+"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", "minipass@^7.1.2":
+  "integrity" "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="
+  "resolved" "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz"
+  "version" "7.1.2"
+
+"modify-values@^1.0.0":
+  "integrity" "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="
+  "resolved" "https://registry.npmmirror.com/modify-values/-/modify-values-1.0.1.tgz"
+  "version" "1.0.1"
+
+"ms@2.0.0":
+  "integrity" "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+  "resolved" "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz"
+  "version" "2.0.0"
+
+"ms@2.1.2":
+  "integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+  "resolved" "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz"
+  "version" "2.1.2"
+
+"natural-compare-lite@^1.4.0":
+  "integrity" "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g=="
+  "resolved" "https://registry.npmmirror.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz"
+  "version" "1.4.0"
+
+"natural-compare@^1.4.0":
+  "integrity" "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
+  "resolved" "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz"
+  "version" "1.4.0"
+
+"neo-async@^2.6.2":
+  "integrity" "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+  "resolved" "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz"
+  "version" "2.6.2"
+
+"node-releases@^2.0.18":
+  "integrity" "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g=="
+  "resolved" "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz"
+  "version" "2.0.18"
+
+"normalize-package-data@^2.3.2", "normalize-package-data@^2.5.0":
+  "integrity" "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="
+  "resolved" "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz"
+  "version" "2.5.0"
+  dependencies:
+    "hosted-git-info" "^2.1.4"
+    "resolve" "^1.10.0"
+    "semver" "2 || 3 || 4 || 5"
+    "validate-npm-package-license" "^3.0.1"
+
+"normalize-package-data@^3.0.0":
+  "integrity" "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA=="
+  "resolved" "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz"
+  "version" "3.0.3"
+  dependencies:
+    "hosted-git-info" "^4.0.1"
+    "is-core-module" "^2.5.0"
+    "semver" "^7.3.4"
+    "validate-npm-package-license" "^3.0.1"
+
+"npm-run-path@^4.0.1":
+  "integrity" "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="
+  "resolved" "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz"
+  "version" "4.0.1"
+  dependencies:
+    "path-key" "^3.0.0"
+
+"npm-run-path@^5.1.0":
+  "integrity" "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="
+  "resolved" "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-5.3.0.tgz"
+  "version" "5.3.0"
+  dependencies:
+    "path-key" "^4.0.0"
+
+"nth-check@^2.1.1":
+  "integrity" "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="
+  "resolved" "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz"
+  "version" "2.1.1"
+  dependencies:
+    "boolbase" "^1.0.0"
+
+"nwsapi@^2.2.4":
+  "integrity" "sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w=="
+  "resolved" "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.12.tgz"
+  "version" "2.2.12"
+
+"once@^1.3.0":
+  "integrity" "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="
+  "resolved" "https://registry.npmmirror.com/once/-/once-1.4.0.tgz"
+  "version" "1.4.0"
+  dependencies:
+    "wrappy" "1"
+
+"onetime@^5.1.0", "onetime@^5.1.2":
+  "integrity" "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="
+  "resolved" "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz"
+  "version" "5.1.2"
+  dependencies:
+    "mimic-fn" "^2.1.0"
+
+"onetime@^6.0.0":
+  "integrity" "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="
+  "resolved" "https://registry.npmmirror.com/onetime/-/onetime-6.0.0.tgz"
+  "version" "6.0.0"
+  dependencies:
+    "mimic-fn" "^4.0.0"
+
+"opener@1":
+  "integrity" "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="
+  "resolved" "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz"
+  "version" "1.5.2"
+
+"optionator@^0.9.3":
+  "integrity" "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="
+  "resolved" "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz"
+  "version" "0.9.4"
+  dependencies:
+    "deep-is" "^0.1.3"
+    "fast-levenshtein" "^2.0.6"
+    "levn" "^0.4.1"
+    "prelude-ls" "^1.2.1"
+    "type-check" "^0.4.0"
+    "word-wrap" "^1.2.5"
+
+"p-limit@^1.1.0":
+  "integrity" "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q=="
+  "resolved" "https://registry.npmmirror.com/p-limit/-/p-limit-1.3.0.tgz"
+  "version" "1.3.0"
+  dependencies:
+    "p-try" "^1.0.0"
+
+"p-limit@^2.2.0":
+  "integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="
+  "resolved" "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz"
+  "version" "2.3.0"
+  dependencies:
+    "p-try" "^2.0.0"
+
+"p-limit@^3.0.2":
+  "integrity" "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="
+  "resolved" "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz"
+  "version" "3.1.0"
+  dependencies:
+    "yocto-queue" "^0.1.0"
+
+"p-locate@^2.0.0":
+  "integrity" "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg=="
+  "resolved" "https://registry.npmmirror.com/p-locate/-/p-locate-2.0.0.tgz"
+  "version" "2.0.0"
+  dependencies:
+    "p-limit" "^1.1.0"
+
+"p-locate@^4.1.0":
+  "integrity" "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="
+  "resolved" "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz"
+  "version" "4.1.0"
+  dependencies:
+    "p-limit" "^2.2.0"
+
+"p-locate@^5.0.0":
+  "integrity" "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="
+  "resolved" "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz"
+  "version" "5.0.0"
+  dependencies:
+    "p-limit" "^3.0.2"
+
+"p-try@^1.0.0":
+  "integrity" "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww=="
+  "resolved" "https://registry.npmmirror.com/p-try/-/p-try-1.0.0.tgz"
+  "version" "1.0.0"
+
+"p-try@^2.0.0":
+  "integrity" "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+  "resolved" "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz"
+  "version" "2.2.0"
+
+"package-json-from-dist@^1.0.0":
+  "integrity" "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw=="
+  "resolved" "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz"
+  "version" "1.0.0"
+
+"parent-module@^1.0.0":
+  "integrity" "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="
+  "resolved" "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz"
+  "version" "1.0.1"
+  dependencies:
+    "callsites" "^3.0.0"
+
+"parse-json@^4.0.0":
+  "integrity" "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="
+  "resolved" "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "error-ex" "^1.3.1"
+    "json-parse-better-errors" "^1.0.1"
+
+"parse-json@^5.0.0", "parse-json@^5.2.0":
+  "integrity" "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="
+  "resolved" "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz"
+  "version" "5.2.0"
+  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"
+
+"parse5@^7.1.2":
+  "integrity" "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw=="
+  "resolved" "https://registry.npmmirror.com/parse5/-/parse5-7.1.2.tgz"
+  "version" "7.1.2"
+  dependencies:
+    "entities" "^4.4.0"
+
+"parseqs@0.0.6":
+  "integrity" "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
+  "resolved" "https://registry.npmmirror.com/parseqs/-/parseqs-0.0.6.tgz"
+  "version" "0.0.6"
+
+"parseuri@0.0.6":
+  "integrity" "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
+  "resolved" "https://registry.npmmirror.com/parseuri/-/parseuri-0.0.6.tgz"
+  "version" "0.0.6"
+
+"path-exists@^3.0.0":
+  "integrity" "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="
+  "resolved" "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz"
+  "version" "3.0.0"
+
+"path-exists@^4.0.0":
+  "integrity" "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+  "resolved" "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz"
+  "version" "4.0.0"
+
+"path-is-absolute@^1.0.0":
+  "integrity" "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
+  "resolved" "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz"
+  "version" "1.0.1"
+
+"path-key@^3.0.0", "path-key@^3.1.0":
+  "integrity" "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
+  "resolved" "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz"
+  "version" "3.1.1"
+
+"path-key@^4.0.0":
+  "integrity" "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="
+  "resolved" "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz"
+  "version" "4.0.0"
+
+"path-parse@^1.0.7":
+  "integrity" "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
+  "resolved" "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz"
+  "version" "1.0.7"
+
+"path-scurry@^1.11.1":
+  "integrity" "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="
+  "resolved" "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz"
+  "version" "1.11.1"
+  dependencies:
+    "lru-cache" "^10.2.0"
+    "minipass" "^5.0.0 || ^6.0.2 || ^7.0.0"
+
+"path-type@^3.0.0":
+  "integrity" "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg=="
+  "resolved" "https://registry.npmmirror.com/path-type/-/path-type-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "pify" "^3.0.0"
+
+"path-type@^4.0.0":
+  "integrity" "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
+  "resolved" "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz"
+  "version" "4.0.0"
+
+"picocolors@^1.0.0", "picocolors@^1.0.1":
+  "integrity" "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew=="
+  "resolved" "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.1.tgz"
+  "version" "1.0.1"
+
+"picomatch@^2.3.1":
+  "integrity" "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
+  "resolved" "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz"
+  "version" "2.3.1"
+
+"pidtree@0.6.0":
+  "integrity" "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="
+  "resolved" "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz"
+  "version" "0.6.0"
+
+"pify@^2.3.0":
+  "integrity" "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
+  "resolved" "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz"
+  "version" "2.3.0"
+
+"pify@^3.0.0":
+  "integrity" "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="
+  "resolved" "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz"
+  "version" "3.0.0"
+
+"postcss-selector-parser@^6.0.15":
+  "integrity" "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="
+  "resolved" "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz"
+  "version" "6.1.2"
+  dependencies:
+    "cssesc" "^3.0.0"
+    "util-deprecate" "^1.0.2"
+
+"prelude-ls@^1.2.1":
+  "integrity" "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
+  "resolved" "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz"
+  "version" "1.2.1"
+
+"prettier-linter-helpers@^1.0.0":
+  "integrity" "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w=="
+  "resolved" "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz"
+  "version" "1.0.0"
+  dependencies:
+    "fast-diff" "^1.1.2"
+
+"prettier@^3.0.2", "prettier@>= 3.0.0", "prettier@>=3.0.0":
+  "integrity" "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew=="
+  "resolved" "https://registry.npmmirror.com/prettier/-/prettier-3.3.3.tgz"
+  "version" "3.3.3"
+
+"process-nextick-args@~2.0.0":
+  "integrity" "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+  "resolved" "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
+  "version" "2.0.1"
+
+"psl@^1.1.33":
+  "integrity" "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
+  "resolved" "https://registry.npmmirror.com/psl/-/psl-1.9.0.tgz"
+  "version" "1.9.0"
+
+"punycode@^2.1.0", "punycode@^2.1.1", "punycode@^2.3.0":
+  "integrity" "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
+  "resolved" "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz"
+  "version" "2.3.1"
+
+"q@^1.5.1":
+  "integrity" "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw=="
+  "resolved" "https://registry.npmmirror.com/q/-/q-1.5.1.tgz"
+  "version" "1.5.1"
+
+"querystringify@^2.1.1":
+  "integrity" "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
+  "resolved" "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz"
+  "version" "2.2.0"
+
+"queue-microtask@^1.2.2":
+  "integrity" "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
+  "resolved" "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz"
+  "version" "1.2.3"
+
+"quick-lru@^4.0.1":
+  "integrity" "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="
+  "resolved" "https://registry.npmmirror.com/quick-lru/-/quick-lru-4.0.1.tgz"
+  "version" "4.0.1"
+
+"randombytes@^2.1.0":
+  "integrity" "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="
+  "resolved" "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz"
+  "version" "2.1.0"
+  dependencies:
+    "safe-buffer" "^5.1.0"
+
+"read-pkg-up@^3.0.0":
+  "integrity" "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw=="
+  "resolved" "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "find-up" "^2.0.0"
+    "read-pkg" "^3.0.0"
+
+"read-pkg-up@^7.0.1":
+  "integrity" "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="
+  "resolved" "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz"
+  "version" "7.0.1"
+  dependencies:
+    "find-up" "^4.1.0"
+    "read-pkg" "^5.2.0"
+    "type-fest" "^0.8.1"
+
+"read-pkg@^3.0.0":
+  "integrity" "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA=="
+  "resolved" "https://registry.npmmirror.com/read-pkg/-/read-pkg-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "load-json-file" "^4.0.0"
+    "normalize-package-data" "^2.3.2"
+    "path-type" "^3.0.0"
+
+"read-pkg@^5.2.0":
+  "integrity" "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg=="
+  "resolved" "https://registry.npmmirror.com/read-pkg/-/read-pkg-5.2.0.tgz"
+  "version" "5.2.0"
+  dependencies:
+    "@types/normalize-package-data" "^2.4.0"
+    "normalize-package-data" "^2.5.0"
+    "parse-json" "^5.0.0"
+    "type-fest" "^0.6.0"
+
+"readable-stream@^3.0.0", "readable-stream@3":
+  "integrity" "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="
+  "resolved" "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz"
+  "version" "3.6.2"
+  dependencies:
+    "inherits" "^2.0.3"
+    "string_decoder" "^1.1.1"
+    "util-deprecate" "^1.0.1"
+
+"readable-stream@~2.3.6":
+  "integrity" "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="
+  "resolved" "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz"
+  "version" "2.3.8"
+  dependencies:
+    "core-util-is" "~1.0.0"
+    "inherits" "~2.0.3"
+    "isarray" "~1.0.0"
+    "process-nextick-args" "~2.0.0"
+    "safe-buffer" "~5.1.1"
+    "string_decoder" "~1.1.1"
+    "util-deprecate" "~1.0.1"
+
+"redent@^3.0.0":
+  "integrity" "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg=="
+  "resolved" "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "indent-string" "^4.0.0"
+    "strip-indent" "^3.0.0"
+
+"regenerate-unicode-properties@^10.1.0":
+  "integrity" "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q=="
+  "resolved" "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz"
+  "version" "10.1.1"
+  dependencies:
+    "regenerate" "^1.4.2"
+
+"regenerate@^1.4.2":
+  "integrity" "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="
+  "resolved" "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz"
+  "version" "1.4.2"
+
+"regenerator-runtime@^0.14.0":
+  "integrity" "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
+  "resolved" "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz"
+  "version" "0.14.1"
+
+"regenerator-transform@^0.15.2":
+  "integrity" "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg=="
+  "resolved" "https://registry.npmmirror.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz"
+  "version" "0.15.2"
+  dependencies:
+    "@babel/runtime" "^7.8.4"
+
+"regexpu-core@^5.3.1":
+  "integrity" "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ=="
+  "resolved" "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-5.3.2.tgz"
+  "version" "5.3.2"
+  dependencies:
+    "@babel/regjsgen" "^0.8.0"
+    "regenerate" "^1.4.2"
+    "regenerate-unicode-properties" "^10.1.0"
+    "regjsparser" "^0.9.1"
+    "unicode-match-property-ecmascript" "^2.0.0"
+    "unicode-match-property-value-ecmascript" "^2.1.0"
+
+"regjsparser@^0.9.1":
+  "integrity" "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ=="
+  "resolved" "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.9.1.tgz"
+  "version" "0.9.1"
+  dependencies:
+    "jsesc" "~0.5.0"
+
+"require-directory@^2.1.1":
+  "integrity" "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
+  "resolved" "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz"
+  "version" "2.1.1"
+
+"require-from-string@^2.0.2":
+  "integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
+  "resolved" "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz"
+  "version" "2.0.2"
+
+"requires-port@^1.0.0":
+  "integrity" "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
+  "resolved" "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz"
+  "version" "1.0.0"
+
+"resolve-from@^4.0.0":
+  "integrity" "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
+  "resolved" "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz"
+  "version" "4.0.0"
+
+"resolve-from@^5.0.0", "resolve-from@5.0.0":
+  "integrity" "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="
+  "resolved" "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz"
+  "version" "5.0.0"
+
+"resolve-global@^1.0.0", "resolve-global@1.0.0":
+  "integrity" "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw=="
+  "resolved" "https://registry.npmmirror.com/resolve-global/-/resolve-global-1.0.0.tgz"
+  "version" "1.0.0"
+  dependencies:
+    "global-dirs" "^0.1.1"
+
+"resolve@^1.10.0", "resolve@^1.14.2", "resolve@^1.22.1":
+  "integrity" "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw=="
+  "resolved" "https://registry.npmmirror.com/resolve/-/resolve-1.22.8.tgz"
+  "version" "1.22.8"
+  dependencies:
+    "is-core-module" "^2.13.0"
+    "path-parse" "^1.0.7"
+    "supports-preserve-symlinks-flag" "^1.0.0"
+
+"restore-cursor@^4.0.0":
+  "integrity" "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="
+  "resolved" "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "onetime" "^5.1.0"
+    "signal-exit" "^3.0.2"
+
+"reusify@^1.0.4":
+  "integrity" "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="
+  "resolved" "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz"
+  "version" "1.0.4"
+
+"rfdc@^1.3.0":
+  "integrity" "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="
+  "resolved" "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz"
+  "version" "1.4.1"
+
+"rimraf@^3.0.2":
+  "integrity" "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="
+  "resolved" "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz"
+  "version" "3.0.2"
+  dependencies:
+    "glob" "^7.1.3"
+
+"rimraf@^5.0.1":
+  "integrity" "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="
+  "resolved" "https://registry.npmmirror.com/rimraf/-/rimraf-5.0.10.tgz"
+  "version" "5.0.10"
+  dependencies:
+    "glob" "^10.3.7"
+
+"rollup-plugin-auto-external@^2.0.0":
+  "integrity" "sha512-HQM3ZkZYfSam1uoZtAB9sK26EiAsfs1phrkf91c/YX+S07wugyRXSigBxrIwiLr5EPPilKYmoMxsrnlGBsXnuQ=="
+  "resolved" "https://registry.npmmirror.com/rollup-plugin-auto-external/-/rollup-plugin-auto-external-2.0.0.tgz"
+  "version" "2.0.0"
+  dependencies:
+    "builtins" "^2.0.0"
+    "read-pkg" "^3.0.0"
+    "safe-resolve" "^1.0.0"
+    "semver" "^5.5.0"
+
+"rollup-plugin-dts@^5.3.1":
+  "integrity" "sha512-gusMi+Z4gY/JaEQeXnB0RUdU82h1kF0WYzCWgVmV4p3hWXqelaKuCvcJawfeg+EKn2T1Ie+YWF2OiN1/L8bTVg=="
+  "resolved" "https://registry.npmmirror.com/rollup-plugin-dts/-/rollup-plugin-dts-5.3.1.tgz"
+  "version" "5.3.1"
+  dependencies:
+    "magic-string" "^0.30.2"
+  optionalDependencies:
+    "@babel/code-frame" "^7.22.5"
+
+"rollup-plugin-serve@^1.1.1":
+  "integrity" "sha512-H0VarZRtFR0lfiiC9/P8jzCDvtFf1liOX4oSdIeeYqUCKrmFA7vNiQ0rg2D+TuoP7leaa/LBR8XBts5viF6lnw=="
+  "resolved" "https://registry.npmmirror.com/rollup-plugin-serve/-/rollup-plugin-serve-1.1.1.tgz"
+  "version" "1.1.1"
+  dependencies:
+    "mime" "^2"
+    "opener" "1"
+
+"rollup@^1.20.0||^2.0.0||^3.0.0||^4.0.0", "rollup@^2.0.0||^3.0.0||^4.0.0", "rollup@^2.14.0||^3.0.0||^4.0.0", "rollup@^2.68.0||^3.0.0||^4.0.0", "rollup@^2.78.0||^3.0.0||^4.0.0", "rollup@^3.0", "rollup@^3.28.1", "rollup@>=0.45.2":
+  "integrity" "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw=="
+  "resolved" "https://registry.npmmirror.com/rollup/-/rollup-3.29.4.tgz"
+  "version" "3.29.4"
+  optionalDependencies:
+    "fsevents" "~2.3.2"
+
+"rrweb-cssom@^0.6.0":
+  "integrity" "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw=="
+  "resolved" "https://registry.npmmirror.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz"
+  "version" "0.6.0"
+
+"run-parallel@^1.1.9":
+  "integrity" "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="
+  "resolved" "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz"
+  "version" "1.2.0"
+  dependencies:
+    "queue-microtask" "^1.2.2"
+
+"safe-buffer@^5.1.0":
+  "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+  "resolved" "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz"
+  "version" "5.2.1"
+
+"safe-buffer@~5.1.0", "safe-buffer@~5.1.1":
+  "integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+  "resolved" "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz"
+  "version" "5.1.2"
+
+"safe-buffer@~5.2.0":
+  "integrity" "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
+  "resolved" "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz"
+  "version" "5.2.1"
+
+"safe-resolve@^1.0.0":
+  "integrity" "sha512-aQpRvfxoi1y0UxKEU0tNO327kb0/LMo8Xrk64M2u172UqOOLCCM0khxN2OTClDiTqTJz5864GMD1X92j4YiHTg=="
+  "resolved" "https://registry.npmmirror.com/safe-resolve/-/safe-resolve-1.0.0.tgz"
+  "version" "1.0.0"
+
+"safer-buffer@>= 2.1.2 < 3.0.0":
+  "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+  "resolved" "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz"
+  "version" "2.1.2"
+
+"saxes@^6.0.0":
+  "integrity" "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA=="
+  "resolved" "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz"
+  "version" "6.0.0"
+  dependencies:
+    "xmlchars" "^2.2.0"
+
+"semver@^5.5.0":
+  "integrity" "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz"
+  "version" "5.7.2"
+
+"semver@^6.0.0", "semver@^6.3.1":
+  "integrity" "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz"
+  "version" "6.3.1"
+
+"semver@^7.3.4":
+  "integrity" "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz"
+  "version" "7.6.3"
+
+"semver@^7.3.6":
+  "integrity" "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz"
+  "version" "7.6.3"
+
+"semver@^7.3.7":
+  "integrity" "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz"
+  "version" "7.6.3"
+
+"semver@^7.5.4":
+  "integrity" "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz"
+  "version" "7.6.3"
+
+"semver@^7.6.3":
+  "integrity" "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-7.6.3.tgz"
+  "version" "7.6.3"
+
+"semver@2 || 3 || 4 || 5":
+  "integrity" "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz"
+  "version" "5.7.2"
+
+"semver@7.5.4":
+  "integrity" "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA=="
+  "resolved" "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz"
+  "version" "7.5.4"
+  dependencies:
+    "lru-cache" "^6.0.0"
+
+"serialize-javascript@^6.0.1":
+  "integrity" "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="
+  "resolved" "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz"
+  "version" "6.0.2"
+  dependencies:
+    "randombytes" "^2.1.0"
+
+"shebang-command@^2.0.0":
+  "integrity" "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="
+  "resolved" "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz"
+  "version" "2.0.0"
+  dependencies:
+    "shebang-regex" "^3.0.0"
+
+"shebang-regex@^3.0.0":
+  "integrity" "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="
+  "resolved" "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz"
+  "version" "3.0.0"
+
+"signal-exit@^3.0.2", "signal-exit@^3.0.3", "signal-exit@^3.0.7":
+  "integrity" "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
+  "resolved" "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz"
+  "version" "3.0.7"
+
+"signal-exit@^4.0.1":
+  "integrity" "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="
+  "resolved" "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz"
+  "version" "4.1.0"
+
+"sip.js@^0.21.2":
+  "integrity" "sha512-tSqTcIgrOd2IhP/rd70JablvAp+fSfLSxO4hGNY6LkWRY1SKygTO7OtJEV/BQb8oIxtMRx0LE7nUF2MaqGbFzA=="
+  "resolved" "https://registry.npmmirror.com/sip.js/-/sip.js-0.21.2.tgz"
+  "version" "0.21.2"
+
+"slash@^3.0.0":
+  "integrity" "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
+  "resolved" "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz"
+  "version" "3.0.0"
+
+"slice-ansi@^5.0.0":
+  "integrity" "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="
+  "resolved" "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-5.0.0.tgz"
+  "version" "5.0.0"
+  dependencies:
+    "ansi-styles" "^6.0.0"
+    "is-fullwidth-code-point" "^4.0.0"
+
+"smob@^1.0.0":
+  "integrity" "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig=="
+  "resolved" "https://registry.npmmirror.com/smob/-/smob-1.5.0.tgz"
+  "version" "1.5.0"
+
+"socket.io-client@2":
+  "integrity" "sha512-lOO9clmdgssDykiOmVQQitwBAF3I6mYcQAo7hQ7AM6Ny5X7fp8hIJ3HcQs3Rjz4SoggoxA1OgrQyY8EgTbcPYw=="
+  "resolved" "https://registry.npmmirror.com/socket.io-client/-/socket.io-client-2.5.0.tgz"
+  "version" "2.5.0"
+  dependencies:
+    "backo2" "1.0.2"
+    "component-bind" "1.0.0"
+    "component-emitter" "~1.3.0"
+    "debug" "~3.1.0"
+    "engine.io-client" "~3.5.0"
+    "has-binary2" "~1.0.2"
+    "indexof" "0.0.1"
+    "parseqs" "0.0.6"
+    "parseuri" "0.0.6"
+    "socket.io-parser" "~3.3.0"
+    "to-array" "0.1.4"
+
+"socket.io-parser@~3.3.0":
+  "integrity" "sha512-z/pFQB3x+EZldRRzORYW1vwVO8m/3ILkswtnpoeU6Ve3cbMWkmHEWDAVJn4QJtchiiFTo5j7UG2QvwxvaA9vow=="
+  "resolved" "https://registry.npmmirror.com/socket.io-parser/-/socket.io-parser-3.3.4.tgz"
+  "version" "3.3.4"
+  dependencies:
+    "component-emitter" "~1.3.0"
+    "debug" "~3.1.0"
+    "isarray" "2.0.1"
+
+"source-map-support@~0.5.20":
+  "integrity" "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w=="
+  "resolved" "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz"
+  "version" "0.5.21"
+  dependencies:
+    "buffer-from" "^1.0.0"
+    "source-map" "^0.6.0"
+
+"source-map@^0.6.0", "source-map@^0.6.1":
+  "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+  "resolved" "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz"
+  "version" "0.6.1"
+
+"spdx-correct@^3.0.0":
+  "integrity" "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="
+  "resolved" "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz"
+  "version" "3.2.0"
+  dependencies:
+    "spdx-expression-parse" "^3.0.0"
+    "spdx-license-ids" "^3.0.0"
+
+"spdx-exceptions@^2.1.0":
+  "integrity" "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="
+  "resolved" "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz"
+  "version" "2.5.0"
+
+"spdx-expression-parse@^3.0.0":
+  "integrity" "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="
+  "resolved" "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz"
+  "version" "3.0.1"
+  dependencies:
+    "spdx-exceptions" "^2.1.0"
+    "spdx-license-ids" "^3.0.0"
+
+"spdx-license-ids@^3.0.0":
+  "integrity" "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw=="
+  "resolved" "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz"
+  "version" "3.0.20"
+
+"split@^1.0.0":
+  "integrity" "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg=="
+  "resolved" "https://registry.npmmirror.com/split/-/split-1.0.1.tgz"
+  "version" "1.0.1"
+  dependencies:
+    "through" "2"
+
+"split2@^3.0.0", "split2@^3.2.2":
+  "integrity" "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg=="
+  "resolved" "https://registry.npmmirror.com/split2/-/split2-3.2.2.tgz"
+  "version" "3.2.2"
+  dependencies:
+    "readable-stream" "^3.0.0"
+
+"string_decoder@^1.1.1":
+  "integrity" "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="
+  "resolved" "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz"
+  "version" "1.3.0"
+  dependencies:
+    "safe-buffer" "~5.2.0"
+
+"string_decoder@~1.1.1":
+  "integrity" "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="
+  "resolved" "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz"
+  "version" "1.1.1"
+  dependencies:
+    "safe-buffer" "~5.1.0"
+
+"string-argv@0.3.2":
+  "integrity" "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="
+  "resolved" "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.2.tgz"
+  "version" "0.3.2"
+
+"string-width-cjs@npm:string-width@^4.2.0":
+  "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="
+  "resolved" "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
+  "version" "4.2.3"
+  dependencies:
+    "emoji-regex" "^8.0.0"
+    "is-fullwidth-code-point" "^3.0.0"
+    "strip-ansi" "^6.0.1"
+
+"string-width@^4.1.0", "string-width@^4.2.0", "string-width@^4.2.3":
+  "integrity" "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="
+  "resolved" "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz"
+  "version" "4.2.3"
+  dependencies:
+    "emoji-regex" "^8.0.0"
+    "is-fullwidth-code-point" "^3.0.0"
+    "strip-ansi" "^6.0.1"
+
+"string-width@^5.0.0":
+  "integrity" "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="
+  "resolved" "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz"
+  "version" "5.1.2"
+  dependencies:
+    "eastasianwidth" "^0.2.0"
+    "emoji-regex" "^9.2.2"
+    "strip-ansi" "^7.0.1"
+
+"string-width@^5.0.1":
+  "integrity" "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="
+  "resolved" "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz"
+  "version" "5.1.2"
+  dependencies:
+    "eastasianwidth" "^0.2.0"
+    "emoji-regex" "^9.2.2"
+    "strip-ansi" "^7.0.1"
+
+"string-width@^5.1.2":
+  "integrity" "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="
+  "resolved" "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz"
+  "version" "5.1.2"
+  dependencies:
+    "eastasianwidth" "^0.2.0"
+    "emoji-regex" "^9.2.2"
+    "strip-ansi" "^7.0.1"
+
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+  "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="
+  "resolved" "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
+  "version" "6.0.1"
+  dependencies:
+    "ansi-regex" "^5.0.1"
+
+"strip-ansi@^6.0.0", "strip-ansi@^6.0.1":
+  "integrity" "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="
+  "resolved" "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz"
+  "version" "6.0.1"
+  dependencies:
+    "ansi-regex" "^5.0.1"
+
+"strip-ansi@^7.0.1":
+  "integrity" "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="
+  "resolved" "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.0.tgz"
+  "version" "7.1.0"
+  dependencies:
+    "ansi-regex" "^6.0.1"
+
+"strip-bom@^3.0.0":
+  "integrity" "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="
+  "resolved" "https://registry.npmmirror.com/strip-bom/-/strip-bom-3.0.0.tgz"
+  "version" "3.0.0"
+
+"strip-final-newline@^2.0.0":
+  "integrity" "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
+  "resolved" "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz"
+  "version" "2.0.0"
+
+"strip-final-newline@^3.0.0":
+  "integrity" "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="
+  "resolved" "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz"
+  "version" "3.0.0"
+
+"strip-indent@^3.0.0":
+  "integrity" "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="
+  "resolved" "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "min-indent" "^1.0.0"
+
+"strip-json-comments@^3.1.1":
+  "integrity" "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
+  "resolved" "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
+  "version" "3.1.1"
+
+"supports-color@^5.3.0":
+  "integrity" "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="
+  "resolved" "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz"
+  "version" "5.5.0"
+  dependencies:
+    "has-flag" "^3.0.0"
+
+"supports-color@^7.1.0":
+  "integrity" "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="
+  "resolved" "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz"
+  "version" "7.2.0"
+  dependencies:
+    "has-flag" "^4.0.0"
+
+"supports-preserve-symlinks-flag@^1.0.0":
+  "integrity" "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
+  "resolved" "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz"
+  "version" "1.0.0"
+
+"symbol-tree@^3.2.4":
+  "integrity" "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
+  "resolved" "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz"
+  "version" "3.2.4"
+
+"synckit@^0.9.1":
+  "integrity" "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A=="
+  "resolved" "https://registry.npmmirror.com/synckit/-/synckit-0.9.1.tgz"
+  "version" "0.9.1"
+  dependencies:
+    "@pkgr/core" "^0.1.0"
+    "tslib" "^2.6.2"
+
+"temp-dir@^2.0.0":
+  "integrity" "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg=="
+  "resolved" "https://registry.npmmirror.com/temp-dir/-/temp-dir-2.0.0.tgz"
+  "version" "2.0.0"
+
+"tempfile@^3.0.0":
+  "integrity" "sha512-uNFCg478XovRi85iD42egu+eSFUmmka750Jy7L5tfHI5hQKKtbPnxaSaXAbBqCDYrw3wx4tXjKwci4/QmsZJxw=="
+  "resolved" "https://registry.npmmirror.com/tempfile/-/tempfile-3.0.0.tgz"
+  "version" "3.0.0"
+  dependencies:
+    "temp-dir" "^2.0.0"
+    "uuid" "^3.3.2"
+
+"terser@^5.17.4":
+  "integrity" "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg=="
+  "resolved" "https://registry.npmmirror.com/terser/-/terser-5.31.6.tgz"
+  "version" "5.31.6"
+  dependencies:
+    "@jridgewell/source-map" "^0.3.3"
+    "acorn" "^8.8.2"
+    "commander" "^2.20.0"
+    "source-map-support" "~0.5.20"
+
+"text-extensions@^1.0.0":
+  "integrity" "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="
+  "resolved" "https://registry.npmmirror.com/text-extensions/-/text-extensions-1.9.0.tgz"
+  "version" "1.9.0"
+
+"text-table@^0.2.0":
+  "integrity" "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="
+  "resolved" "https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz"
+  "version" "0.2.0"
+
+"through@>=2.2.7 <3", "through@2":
+  "integrity" "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
+  "resolved" "https://registry.npmmirror.com/through/-/through-2.3.8.tgz"
+  "version" "2.3.8"
+
+"through2@^2.0.0":
+  "integrity" "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ=="
+  "resolved" "https://registry.npmmirror.com/through2/-/through2-2.0.5.tgz"
+  "version" "2.0.5"
+  dependencies:
+    "readable-stream" "~2.3.6"
+    "xtend" "~4.0.1"
+
+"through2@^4.0.0":
+  "integrity" "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw=="
+  "resolved" "https://registry.npmmirror.com/through2/-/through2-4.0.2.tgz"
+  "version" "4.0.2"
+  dependencies:
+    "readable-stream" "3"
+
+"to-array@0.1.4":
+  "integrity" "sha512-LhVdShQD/4Mk4zXNroIQZJC+Ap3zgLcDuwEdcmLv9CCO73NWockQDwyUnW/m8VX/EElfL6FcYx7EeutN4HJA6A=="
+  "resolved" "https://registry.npmmirror.com/to-array/-/to-array-0.1.4.tgz"
+  "version" "0.1.4"
+
+"to-fast-properties@^2.0.0":
+  "integrity" "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog=="
+  "resolved" "https://registry.npmmirror.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz"
+  "version" "2.0.0"
+
+"to-regex-range@^5.0.1":
+  "integrity" "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="
+  "resolved" "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz"
+  "version" "5.0.1"
+  dependencies:
+    "is-number" "^7.0.0"
+
+"tough-cookie@^4.1.2":
+  "integrity" "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="
+  "resolved" "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.4.tgz"
+  "version" "4.1.4"
+  dependencies:
+    "psl" "^1.1.33"
+    "punycode" "^2.1.1"
+    "universalify" "^0.2.0"
+    "url-parse" "^1.5.3"
+
+"tr46@^4.1.1":
+  "integrity" "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw=="
+  "resolved" "https://registry.npmmirror.com/tr46/-/tr46-4.1.1.tgz"
+  "version" "4.1.1"
+  dependencies:
+    "punycode" "^2.3.0"
+
+"trim-newlines@^3.0.0":
+  "integrity" "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw=="
+  "resolved" "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz"
+  "version" "3.0.1"
+
+"ts-api-utils@^1.0.1":
+  "integrity" "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ=="
+  "resolved" "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz"
+  "version" "1.3.0"
+
+"ts-node@^10.8.1", "ts-node@^10.9.1", "ts-node@>=10":
+  "integrity" "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ=="
+  "resolved" "https://registry.npmmirror.com/ts-node/-/ts-node-10.9.2.tgz"
+  "version" "10.9.2"
+  dependencies:
+    "@cspotcode/source-map-support" "^0.8.0"
+    "@tsconfig/node10" "^1.0.7"
+    "@tsconfig/node12" "^1.0.7"
+    "@tsconfig/node14" "^1.0.0"
+    "@tsconfig/node16" "^1.0.2"
+    "acorn" "^8.4.1"
+    "acorn-walk" "^8.1.1"
+    "arg" "^4.1.0"
+    "create-require" "^1.1.0"
+    "diff" "^4.0.1"
+    "make-error" "^1.1.1"
+    "v8-compile-cache-lib" "^3.0.1"
+    "yn" "3.1.1"
+
+"tslib@*", "tslib@^2.6.2":
+  "integrity" "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA=="
+  "resolved" "https://registry.npmmirror.com/tslib/-/tslib-2.7.0.tgz"
+  "version" "2.7.0"
+
+"tslib@^1.8.1":
+  "integrity" "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+  "resolved" "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz"
+  "version" "1.14.1"
+
+"tsutils@^3.21.0":
+  "integrity" "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA=="
+  "resolved" "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz"
+  "version" "3.21.0"
+  dependencies:
+    "tslib" "^1.8.1"
+
+"type-check@^0.4.0", "type-check@~0.4.0":
+  "integrity" "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="
+  "resolved" "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz"
+  "version" "0.4.0"
+  dependencies:
+    "prelude-ls" "^1.2.1"
+
+"type-fest@^0.18.0":
+  "integrity" "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="
+  "resolved" "https://registry.npmmirror.com/type-fest/-/type-fest-0.18.1.tgz"
+  "version" "0.18.1"
+
+"type-fest@^0.20.2":
+  "integrity" "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="
+  "resolved" "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz"
+  "version" "0.20.2"
+
+"type-fest@^0.6.0":
+  "integrity" "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="
+  "resolved" "https://registry.npmmirror.com/type-fest/-/type-fest-0.6.0.tgz"
+  "version" "0.6.0"
+
+"type-fest@^0.8.1":
+  "integrity" "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="
+  "resolved" "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz"
+  "version" "0.8.1"
+
+"type-fest@^1.0.2":
+  "integrity" "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="
+  "resolved" "https://registry.npmmirror.com/type-fest/-/type-fest-1.4.0.tgz"
+  "version" "1.4.0"
+
+"typescript@*", "typescript@^4.1 || ^5.0", "typescript@>=2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", "typescript@>=3.7.0", "typescript@>=4.2.0", "typescript@5.1.6":
+  "integrity" "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA=="
+  "resolved" "https://registry.npmmirror.com/typescript/-/typescript-5.1.6.tgz"
+  "version" "5.1.6"
+
+"typescript@^4.6.4 || ^5.2.2", "typescript@>=4", "typescript@>=4.9.5":
+  "integrity" "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q=="
+  "resolved" "https://registry.npmmirror.com/typescript/-/typescript-5.5.4.tgz"
+  "version" "5.5.4"
+
+"uglify-js@^3.1.4":
+  "integrity" "sha512-S8KA6DDI47nQXJSi2ctQ629YzwOVs+bQML6DAtvy0wgNdpi+0ySpQK0g2pxBq2xfF2z3YCscu7NNA8nXT9PlIQ=="
+  "resolved" "https://registry.npmmirror.com/uglify-js/-/uglify-js-3.19.2.tgz"
+  "version" "3.19.2"
+
+"undici-types@~6.19.2":
+  "integrity" "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
+  "resolved" "https://registry.npmmirror.com/undici-types/-/undici-types-6.19.8.tgz"
+  "version" "6.19.8"
+
+"unicode-canonical-property-names-ecmascript@^2.0.0":
+  "integrity" "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ=="
+  "resolved" "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz"
+  "version" "2.0.0"
+
+"unicode-match-property-ecmascript@^2.0.0":
+  "integrity" "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q=="
+  "resolved" "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz"
+  "version" "2.0.0"
+  dependencies:
+    "unicode-canonical-property-names-ecmascript" "^2.0.0"
+    "unicode-property-aliases-ecmascript" "^2.0.0"
+
+"unicode-match-property-value-ecmascript@^2.1.0":
+  "integrity" "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA=="
+  "resolved" "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz"
+  "version" "2.1.0"
+
+"unicode-property-aliases-ecmascript@^2.0.0":
+  "integrity" "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w=="
+  "resolved" "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz"
+  "version" "2.1.0"
+
+"universalify@^0.2.0":
+  "integrity" "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="
+  "resolved" "https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz"
+  "version" "0.2.0"
+
+"universalify@^2.0.0":
+  "integrity" "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="
+  "resolved" "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz"
+  "version" "2.0.1"
+
+"update-browserslist-db@^1.1.0":
+  "integrity" "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ=="
+  "resolved" "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz"
+  "version" "1.1.0"
+  dependencies:
+    "escalade" "^3.1.2"
+    "picocolors" "^1.0.1"
+
+"uri-js@^4.2.2":
+  "integrity" "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="
+  "resolved" "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz"
+  "version" "4.4.1"
+  dependencies:
+    "punycode" "^2.1.0"
+
+"url-parse@^1.5.3":
+  "integrity" "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="
+  "resolved" "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz"
+  "version" "1.5.10"
+  dependencies:
+    "querystringify" "^2.1.1"
+    "requires-port" "^1.0.0"
+
+"util-deprecate@^1.0.1", "util-deprecate@^1.0.2", "util-deprecate@~1.0.1":
+  "integrity" "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+  "resolved" "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz"
+  "version" "1.0.2"
+
+"uuid@^3.3.2":
+  "integrity" "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
+  "resolved" "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz"
+  "version" "3.4.0"
+
+"v8-compile-cache-lib@^3.0.1":
+  "integrity" "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
+  "resolved" "https://registry.npmmirror.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
+  "version" "3.0.1"
+
+"validate-npm-package-license@^3.0.1":
+  "integrity" "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="
+  "resolved" "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz"
+  "version" "3.0.4"
+  dependencies:
+    "spdx-correct" "^3.0.0"
+    "spdx-expression-parse" "^3.0.0"
+
+"vue-eslint-parser@^9.1.1", "vue-eslint-parser@^9.4.3":
+  "integrity" "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg=="
+  "resolved" "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz"
+  "version" "9.4.3"
+  dependencies:
+    "debug" "^4.3.4"
+    "eslint-scope" "^7.1.1"
+    "eslint-visitor-keys" "^3.3.0"
+    "espree" "^9.3.1"
+    "esquery" "^1.4.0"
+    "lodash" "^4.17.21"
+    "semver" "^7.3.6"
+
+"w3c-xmlserializer@^4.0.0":
+  "integrity" "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw=="
+  "resolved" "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "xml-name-validator" "^4.0.0"
+
+"webidl-conversions@^7.0.0":
+  "integrity" "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
+  "resolved" "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz"
+  "version" "7.0.0"
+
+"whatwg-encoding@^2.0.0":
+  "integrity" "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg=="
+  "resolved" "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz"
+  "version" "2.0.0"
+  dependencies:
+    "iconv-lite" "0.6.3"
+
+"whatwg-mimetype@^3.0.0":
+  "integrity" "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="
+  "resolved" "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz"
+  "version" "3.0.0"
+
+"whatwg-url@^12.0.0", "whatwg-url@^12.0.1":
+  "integrity" "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ=="
+  "resolved" "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-12.0.1.tgz"
+  "version" "12.0.1"
+  dependencies:
+    "tr46" "^4.1.1"
+    "webidl-conversions" "^7.0.0"
+
+"which@^2.0.1":
+  "integrity" "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="
+  "resolved" "https://registry.npmmirror.com/which/-/which-2.0.2.tgz"
+  "version" "2.0.2"
+  dependencies:
+    "isexe" "^2.0.0"
+
+"word-wrap@^1.2.5":
+  "integrity" "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
+  "resolved" "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz"
+  "version" "1.2.5"
+
+"wordwrap@^1.0.0":
+  "integrity" "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="
+  "resolved" "https://registry.npmmirror.com/wordwrap/-/wordwrap-1.0.0.tgz"
+  "version" "1.0.0"
+
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+  "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="
+  "resolved" "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
+  "version" "7.0.0"
+  dependencies:
+    "ansi-styles" "^4.0.0"
+    "string-width" "^4.1.0"
+    "strip-ansi" "^6.0.0"
+
+"wrap-ansi@^7.0.0":
+  "integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="
+  "resolved" "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
+  "version" "7.0.0"
+  dependencies:
+    "ansi-styles" "^4.0.0"
+    "string-width" "^4.1.0"
+    "strip-ansi" "^6.0.0"
+
+"wrap-ansi@^8.0.1", "wrap-ansi@^8.1.0":
+  "integrity" "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="
+  "resolved" "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz"
+  "version" "8.1.0"
+  dependencies:
+    "ansi-styles" "^6.1.0"
+    "string-width" "^5.0.1"
+    "strip-ansi" "^7.0.1"
+
+"wrappy@1":
+  "integrity" "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+  "resolved" "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz"
+  "version" "1.0.2"
+
+"ws@^8.13.0":
+  "integrity" "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="
+  "resolved" "https://registry.npmmirror.com/ws/-/ws-8.18.0.tgz"
+  "version" "8.18.0"
+
+"ws@~7.5.10":
+  "integrity" "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="
+  "resolved" "https://registry.npmmirror.com/ws/-/ws-7.5.10.tgz"
+  "version" "7.5.10"
+
+"xml-name-validator@^4.0.0":
+  "integrity" "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw=="
+  "resolved" "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz"
+  "version" "4.0.0"
+
+"xmlchars@^2.2.0":
+  "integrity" "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
+  "resolved" "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz"
+  "version" "2.2.0"
+
+"xmlhttprequest-ssl@~1.6.2":
+  "integrity" "sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q=="
+  "resolved" "https://registry.npmmirror.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz"
+  "version" "1.6.3"
+
+"xtend@~4.0.1":
+  "integrity" "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+  "resolved" "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz"
+  "version" "4.0.2"
+
+"y18n@^5.0.5":
+  "integrity" "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
+  "resolved" "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz"
+  "version" "5.0.8"
+
+"yallist@^3.0.2":
+  "integrity" "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
+  "resolved" "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz"
+  "version" "3.1.1"
+
+"yallist@^4.0.0":
+  "integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
+  "resolved" "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz"
+  "version" "4.0.0"
+
+"yaml@2.3.1":
+  "integrity" "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ=="
+  "resolved" "https://registry.npmmirror.com/yaml/-/yaml-2.3.1.tgz"
+  "version" "2.3.1"
+
+"yargs-parser@^20.2.2", "yargs-parser@^20.2.3":
+  "integrity" "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="
+  "resolved" "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz"
+  "version" "20.2.9"
+
+"yargs-parser@^21.1.1":
+  "integrity" "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
+  "resolved" "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-21.1.1.tgz"
+  "version" "21.1.1"
+
+"yargs@^16.2.0":
+  "integrity" "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="
+  "resolved" "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz"
+  "version" "16.2.0"
+  dependencies:
+    "cliui" "^7.0.2"
+    "escalade" "^3.1.1"
+    "get-caller-file" "^2.0.5"
+    "require-directory" "^2.1.1"
+    "string-width" "^4.2.0"
+    "y18n" "^5.0.5"
+    "yargs-parser" "^20.2.2"
+
+"yargs@^17.0.0":
+  "integrity" "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="
+  "resolved" "https://registry.npmmirror.com/yargs/-/yargs-17.7.2.tgz"
+  "version" "17.7.2"
+  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"
+
+"yeast@0.1.2":
+  "integrity" "sha512-8HFIh676uyGYP6wP13R/j6OJ/1HwJ46snpvzE7aHAN3Ryqh2yX6Xox2B4CUmTwwOIzlG3Bs7ocsP5dZH/R1Qbg=="
+  "resolved" "https://registry.npmmirror.com/yeast/-/yeast-0.1.2.tgz"
+  "version" "0.1.2"
+
+"yn@3.1.1":
+  "integrity" "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
+  "resolved" "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz"
+  "version" "3.1.1"
+
+"yocto-queue@^0.1.0":
+  "integrity" "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
+  "resolved" "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz"
+  "version" "0.1.0"