diff --git a/.env.sample b/.env.sample index 8c3990fc..c82ec71d 100644 --- a/.env.sample +++ b/.env.sample @@ -1,9 +1,10 @@ # MongoDB MONGODB_URI=${MONGODB_URI:-mongodb://api:password@localhost:27017/live-gui} -# Ateliere Live System Controlleer +# Ateliere Live System Controller LIVE_URL=${LIVE_URL:-https://localhost:8080} LIVE_CREDENTIALS=${LIVE_CREDENTIALS:-admin:admin} + # This ENV variable disables SSL Verification, use if the above LIVE_URL doesn't have a proper certificate NODE_TLS_REJECT_UNAUTHORIZED=${NODE_TLS_REJECT_UNAUTHORIZED:-1} diff --git a/README.md b/README.md index 96d44262..49a5b116 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ https://help.ateliere.com/live/docs/reference/7-0-0/rest_api/ See [CONTRIBUTING](CONTRIBUTING.md) -## Licence +## License Copyright (C) 2024 Ateliere Creative Technologies diff --git a/next.config.js b/next.config.js index cdf8c9c8..24756994 100644 --- a/next.config.js +++ b/next.config.js @@ -9,5 +9,21 @@ module.exports = { locales: ['en', 'sv'], defaultLocale: 'en', localeDetection: false + }, + images: { + minimumCacheTTL: 0 + }, + async headers() { + return [ + { + source: '/(.*)', + headers: [ + { + key: 'Permissions-Policy', + value: 'clipboard-write=(self)' + } + ] + } + ]; } }; diff --git a/package-lock.json b/package-lock.json index dc7f3857..4f7cc178 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,11 +19,13 @@ "@sinclair/typebox": "^0.25.24", "@tabler/icons": "^2.22.0", "@tabler/icons-react": "^2.20.0", + "@types/ws": "^8.5.12", "bcrypt": "^5.1.0", "cron": "^2.3.1", "date-fns": "^2.30.0", "dotenv": "^16.0.3", "fastify": "^4.28.1", + "lodash.clonedeep": "^4.5.0", "lodash.get": "^4.4.2", "mongodb": "^5.8.0", "next": "^13.5.6", @@ -35,7 +37,8 @@ "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", "tailwind-merge": "^1.13.2", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "ws": "^8.18.0" }, "devDependencies": { "@commitlint/cli": "^17.4.2", @@ -43,6 +46,7 @@ "@types/bcrypt": "^5.0.0", "@types/cron": "^2.0.1", "@types/jest": "^29.4.0", + "@types/lodash.clonedeep": "^4.5.9", "@types/node": "^18.16.7", "@types/react": "18.2.6", "@types/react-dom": "^18.2.4", @@ -81,132 +85,61 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.21.7", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.7.tgz", - "integrity": "sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz", - "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-compilation-targets": "^7.21.5", - "@babel/helper-module-transforms": "^7.21.5", - "@babel/helpers": "^7.21.5", - "@babel/parser": "^7.21.8", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5", - "convert-source-map": "^1.7.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.2", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -216,12 +149,6 @@ "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -232,14 +159,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.25.6", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -247,22 +174,19 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz", - "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.21.5", - "@babel/helper-validator-option": "^7.21.0", - "browserslist": "^4.21.3", + "@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.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { @@ -274,154 +198,109 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-module-imports": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz", - "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, "dependencies": { - "@babel/types": "^7.21.4" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz", - "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-module-imports": "^7.21.4", - "@babel/helper-simple-access": "^7.21.5", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.19.1", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@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" }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz", - "integrity": "sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz", - "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.21.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz", - "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz", - "integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.6.tgz", + "integrity": "sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==", "dev": true, "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.21.5", - "@babel/types": "^7.21.5" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -499,10 +378,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.6.tgz", + "integrity": "sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==", "dev": true, + "dependencies": { + "@babel/types": "^7.25.6" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -546,6 +428,36 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz", + "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -571,12 +483,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.21.4.tgz", - "integrity": "sha512-5hewiLct5OKyh6PLKEYaFclcqtIgCb6bmELouxjF6up5q3Sov7rOayW4RwhbaBL0dit8rA80GNfY+UuDp2mBbQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -657,6 +569,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", @@ -673,12 +600,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz", - "integrity": "sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", + "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -688,45 +615,42 @@ } }, "node_modules/@babel/runtime": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz", - "integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", "dependencies": { - "regenerator-runtime": "^0.13.11" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -743,13 +667,13 @@ } }, "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.6.tgz", + "integrity": "sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -763,16 +687,16 @@ "dev": true }, "node_modules/@commitlint/cli": { - "version": "17.6.3", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.6.3.tgz", - "integrity": "sha512-ItSz2fd4F+CujgIbQOfNNerDF1eFlsBGEfp9QcCb1kxTYMuKTYZzA6Nu1YRRrIaaWwe2E7awUGpIMrPoZkOG3A==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz", + "integrity": "sha512-ay+WbzQesE0Rv4EQKfNbSMiJJ12KdKTDzIt0tcK4k11FdsWmtwP0Kp1NWMOUswfIWo6Eb7p7Ln721Nx9FLNBjg==", "dev": true, "dependencies": { - "@commitlint/format": "^17.4.4", - "@commitlint/lint": "^17.6.3", - "@commitlint/load": "^17.5.0", - "@commitlint/read": "^17.5.1", - "@commitlint/types": "^17.4.4", + "@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", @@ -787,24 +711,24 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "17.6.3", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.6.3.tgz", - "integrity": "sha512-bLyHEjjRWqlLQWIgYFHmUPbEFMOOLXeF3QbUinDIJev/u9e769tkoTH9YPknEywiuIrAgZaVo+OfzAIsJP0fsw==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.8.1.tgz", + "integrity": "sha512-NxCOHx1kgneig3VLauWJcDWS40DVjg7nKOpBEEK9E5fjJpQqLCilcnKkIIjdBH98kEO1q3NpE5NSrZ2kl/QGJg==", "dev": true, "dependencies": { - "conventional-changelog-conventionalcommits": "^5.0.0" + "conventional-changelog-conventionalcommits": "^6.1.0" }, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/config-validator": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.4.4.tgz", - "integrity": "sha512-bi0+TstqMiqoBAQDvdEP4AFh0GaKyLFlPPEObgI29utoKEYoPQTvF0EYqIwYYLEoJYhj5GfMIhPHJkTJhagfeg==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.8.1.tgz", + "integrity": "sha512-UUgUC+sNiiMwkyiuIFR7JG2cfd9t/7MV8VB4TZ+q02ZFkHoduUS4tJGsCBWvBOGD9Btev6IecPMvlWUfJorkEA==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^17.8.1", "ajv": "^8.11.0" }, "engines": { @@ -812,12 +736,12 @@ } }, "node_modules/@commitlint/ensure": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.4.4.tgz", - "integrity": "sha512-AHsFCNh8hbhJiuZ2qHv/m59W/GRE9UeOXbkOqxYMNNg9pJ7qELnFcwj5oYpa6vzTSHtPGKf3C2yUFNy1GGHq6g==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.8.1.tgz", + "integrity": "sha512-xjafwKxid8s1K23NFpL8JNo6JnY/ysetKo8kegVM7c8vs+kWLP8VrQq+NbhgVlmCojhEDbzQKp4eRXSjVOGsow==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^17.8.1", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", @@ -829,21 +753,21 @@ } }, "node_modules/@commitlint/execute-rule": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz", - "integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.8.1.tgz", + "integrity": "sha512-JHVupQeSdNI6xzA9SqMF+p/JjrHTcrJdI02PwesQIDCIGUrv04hicJgCcws5nzaoZbROapPs0s6zeVHoxpMwFQ==", "dev": true, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/format": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.4.4.tgz", - "integrity": "sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.8.1.tgz", + "integrity": "sha512-f3oMTyZ84M9ht7fb93wbCKmWxO5/kKSbwuYvS867duVomoOsgrgljkGGIztmT/srZnaiGbaK8+Wf8Ik2tSr5eg==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", + "@commitlint/types": "^17.8.1", "chalk": "^4.1.0" }, "engines": { @@ -864,31 +788,31 @@ } }, "node_modules/@commitlint/lint": { - "version": "17.6.3", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.6.3.tgz", - "integrity": "sha512-fBlXwt6SHJFgm3Tz+luuo3DkydAx9HNC5y4eBqcKuDuMVqHd2ugMNr+bQtx6riv9mXFiPoKp7nE4Xn/ls3iVDA==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.8.1.tgz", + "integrity": "sha512-aQUlwIR1/VMv2D4GXSk7PfL5hIaFSfy6hSHV94O8Y27T5q+DlDEgd/cZ4KmVI+MWKzFfCTiTuWqjfRSfdRllCA==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^17.6.3", - "@commitlint/parse": "^17.4.4", - "@commitlint/rules": "^17.6.1", - "@commitlint/types": "^17.4.4" + "@commitlint/is-ignored": "^17.8.1", + "@commitlint/parse": "^17.8.1", + "@commitlint/rules": "^17.8.1", + "@commitlint/types": "^17.8.1" }, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/load": { - "version": "17.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.5.0.tgz", - "integrity": "sha512-l+4W8Sx4CD5rYFsrhHH8HP01/8jEP7kKf33Xlx2Uk2out/UKoKPYMOIRcDH5ppT8UXLMV+x6Wm5osdRKKgaD1Q==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.8.1.tgz", + "integrity": "sha512-iF4CL7KDFstP1kpVUkT8K2Wl17h2yx9VaR1ztTc8vzByWWcbO/WaKwxsnCOqow9tVAlzPfo1ywk9m2oJ9ucMqA==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^17.4.4", - "@commitlint/execute-rule": "^17.4.0", - "@commitlint/resolve-extends": "^17.4.4", - "@commitlint/types": "^17.4.4", - "@types/node": "*", + "@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", @@ -897,43 +821,49 @@ "lodash.uniq": "^4.5.0", "resolve-from": "^5.0.0", "ts-node": "^10.8.1", - "typescript": "^4.6.4 || ^5.0.0" + "typescript": "^4.6.4 || ^5.2.2" }, "engines": { "node": ">=v14" } }, + "node_modules/@commitlint/load/node_modules/@types/node": { + "version": "20.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", + "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", + "dev": true + }, "node_modules/@commitlint/message": { - "version": "17.4.2", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz", - "integrity": "sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz", + "integrity": "sha512-6bYL1GUQsD6bLhTH3QQty8pVFoETfFQlMn2Nzmz3AOLqRVfNNtXBaSY0dhZ0dM6A2MEq4+2d7L/2LP8TjqGRkA==", "dev": true, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/parse": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.4.4.tgz", - "integrity": "sha512-EKzz4f49d3/OU0Fplog7nwz/lAfXMaDxtriidyGF9PtR+SRbgv4FhsfF310tKxs6EPj8Y+aWWuX3beN5s+yqGg==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.8.1.tgz", + "integrity": "sha512-/wLUickTo0rNpQgWwLPavTm7WbwkZoBy3X8PpkUmlSmQJyWQTj0m6bDjiykMaDt41qcUbfeFfaCvXfiR4EGnfw==", "dev": true, "dependencies": { - "@commitlint/types": "^17.4.4", - "conventional-changelog-angular": "^5.0.11", - "conventional-commits-parser": "^3.2.2" + "@commitlint/types": "^17.8.1", + "conventional-changelog-angular": "^6.0.0", + "conventional-commits-parser": "^4.0.0" }, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/read": { - "version": "17.5.1", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.5.1.tgz", - "integrity": "sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.8.1.tgz", + "integrity": "sha512-Fd55Oaz9irzBESPCdMd8vWWgxsW3OWR99wOntBDHgf9h7Y6OOHjWEdS9Xzen1GFndqgyoaFplQS5y7KZe0kO2w==", "dev": true, "dependencies": { - "@commitlint/top-level": "^17.4.0", - "@commitlint/types": "^17.4.4", + "@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" @@ -943,13 +873,13 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "17.4.4", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.4.4.tgz", - "integrity": "sha512-znXr1S0Rr8adInptHw0JeLgumS11lWbk5xAWFVno+HUFVN45875kUtqjrI6AppmD3JI+4s0uZlqqlkepjJd99A==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.8.1.tgz", + "integrity": "sha512-W/ryRoQ0TSVXqJrx5SGkaYuAaE/BUontL1j1HsKckvM6e5ZaG0M9126zcwL6peKSuIetJi7E87PRQF8O86EW0Q==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^17.4.4", - "@commitlint/types": "^17.4.4", + "@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", @@ -960,15 +890,15 @@ } }, "node_modules/@commitlint/rules": { - "version": "17.6.1", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.6.1.tgz", - "integrity": "sha512-lUdHw6lYQ1RywExXDdLOKxhpp6857/4c95Dc/1BikrHgdysVUXz26yV0vp1GL7Gv+avx9WqZWTIVB7pNouxlfw==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.8.1.tgz", + "integrity": "sha512-2b7OdVbN7MTAt9U0vKOYKCDsOvESVXxQmrvuVUZ0rGFMCrCPJWWP1GJ7f0lAypbDAhaGb8zqtdOr47192LBrIA==", "dev": true, "dependencies": { - "@commitlint/ensure": "^17.4.4", - "@commitlint/message": "^17.4.2", - "@commitlint/to-lines": "^17.4.0", - "@commitlint/types": "^17.4.4", + "@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" }, "engines": { @@ -976,18 +906,18 @@ } }, "node_modules/@commitlint/to-lines": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz", - "integrity": "sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.8.1.tgz", + "integrity": "sha512-LE0jb8CuR/mj6xJyrIk8VLz03OEzXFgLdivBytoooKO5xLt5yalc8Ma5guTWobw998sbR3ogDd+2jed03CFmJA==", "dev": true, "engines": { "node": ">=v14" } }, "node_modules/@commitlint/top-level": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz", - "integrity": "sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==", + "version": "17.8.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.8.1.tgz", + "integrity": "sha512-l6+Z6rrNf5p333SHfEte6r+WkOxGlWK4bLuZKbtf/2TXRN+qhrvn1XE63VhD8Oe9oIHQ7F7W1nG2k/TJFhx2yA==", "dev": true, "dependencies": { "find-up": "^5.0.0" @@ -1049,19 +979,6 @@ "node": ">=12.22.1" } }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1078,23 +995,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -1132,18 +1049,18 @@ "dev": true }, "node_modules/@eslint/js": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.40.0.tgz", - "integrity": "sha512-ElyB54bJIhXQYVKjDSvCkPO1iU1tSAeVQJbllWJq1XQSmmA4dgFk8CbiBGpiOPxleE48vDogxCtmMYku4HSVLA==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@exodus/schemasafe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.0.1.tgz", - "integrity": "sha512-PQdbF8dGd4LnbwBlcc4ML8RKYdplm+e9sUeWBTr4zgF13/Shiuov9XznvM4T8cb1CfyKK21yTUkuAIIh/DAH/g==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", + "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", "dev": true }, "node_modules/@eyevinn/csai-manager": { @@ -1161,6 +1078,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@eyevinn/video-event-filter/-/video-event-filter-2.0.0.tgz", "integrity": "sha512-S5pECmymqg7WqlAeerhtPVEFxWhLY3cz3DTuhuljZreVDLnEBV6/DlWI6SWDnKdpDJMgD6VjPfaRJssmGatNBg==", + "deprecated": "Package no longer maintained, we recommend migrating to @eyevinn/media-event-filter", "dependencies": { "mitt": "^2.1.0" } @@ -1197,22 +1115,27 @@ } }, "node_modules/@fastify/ajv-compiler": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz", - "integrity": "sha512-ebbEtlI7dxXF5ziNdr05mOY8NnDiPB1XvAlLHctRt/Rc+C3LCOVW5imUVX+mhvUhnNzmPBHewUkOFgGlCxgdAA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz", + "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==", "dependencies": { "ajv": "^8.11.0", "ajv-formats": "^2.1.1", "fast-uri": "^2.0.0" } }, + "node_modules/@fastify/ajv-compiler/node_modules/fast-uri": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", + "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==" + }, "node_modules/@fastify/cors": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.2.1.tgz", - "integrity": "sha512-2H2MrDD3ea7g707g1CNNLWb9/tYbmw7HS+MK2SDcgjxwzbOFR93JortelTIO8DBFsZqFtEpKNxiZfSyrGgYcbw==", + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.5.0.tgz", + "integrity": "sha512-/oZ1QSb02XjP0IK1U0IXktEsw/dUBTxJOW7IpIeO8c/tNalw/KjoNSJv1Sf6eqoBPO+TDGkifq6ynFK3v68HFQ==", "dependencies": { "fastify-plugin": "^4.0.0", - "mnemonist": "0.39.5" + "mnemonist": "0.39.6" } }, "node_modules/@fastify/error": { @@ -1249,41 +1172,40 @@ } }, "node_modules/@fastify/static": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.10.1.tgz", - "integrity": "sha512-DNnG+5QenQcTQw37qk0/191STThnN6SbU+2XMpWtpYR3gQUfUvMax14jTT/jqNINNbCkQJaKMnPtpFPKo4/68g==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.12.0.tgz", + "integrity": "sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==", "dependencies": { "@fastify/accept-negotiator": "^1.0.0", "@fastify/send": "^2.0.0", "content-disposition": "^0.5.3", "fastify-plugin": "^4.0.0", "glob": "^8.0.1", - "p-limit": "^3.1.0", - "readable-stream": "^4.0.0" + "p-limit": "^3.1.0" } }, "node_modules/@fastify/swagger": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-8.3.1.tgz", - "integrity": "sha512-S9suS5WlRyADwobkrlcNeICkVyHFxKKdsimRsoO7h27KS86c+pE/s/8WvU793p5Ftk9QnApoilT9izJ0j3I8GA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-8.15.0.tgz", + "integrity": "sha512-zy+HEEKFqPMS2sFUsQU5X0MHplhKJvWeohBwTCkBAJA/GDYGLGUWQaETEhptiqxK7Hs0fQB9B4MDb3pbwIiCwA==", "dependencies": { "fastify-plugin": "^4.0.0", "json-schema-resolver": "^2.0.0", "openapi-types": "^12.0.0", "rfdc": "^1.3.0", - "yaml": "^2.1.1" + "yaml": "^2.2.2" } }, "node_modules/@fastify/swagger-ui": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-1.8.1.tgz", - "integrity": "sha512-XMfLGZMXi5dl0Gy6R6tlistA4d0XlJJweUfQkPNVeeBq2hO03DmvtM5yrp8adF392Xoi+6rlGHFaneL9EQdsoA==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-1.10.2.tgz", + "integrity": "sha512-f2mRqtblm6eRAFQ3e8zSngxVNEtiYY7rISKQVjPA++ZsWc5WYlPVTb6Bx0G/zy0BIoucNqDr/Q2Vb/kTYkOq1A==", "dependencies": { "@fastify/static": "^6.0.0", "fastify-plugin": "^4.0.0", "openapi-types": "^12.0.2", "rfdc": "^1.3.0", - "yaml": "^2.1.3" + "yaml": "^2.2.2" } }, "node_modules/@fastify/type-provider-typebox": { @@ -1295,14 +1217,49 @@ "fastify": "^4.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -1323,56 +1280,147 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", "dev": true }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "argparse": "^1.0.7", + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { @@ -1418,6 +1466,12 @@ "node": ">=8" } }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -1428,16 +1482,16 @@ } }, "node_modules/@jest/console": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", - "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1445,37 +1499,37 @@ } }, "node_modules/@jest/core": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", - "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { - "@jest/console": "^29.5.0", - "@jest/reporters": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.5.0", - "jest-config": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-resolve-dependencies": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "jest-watcher": "^29.5.0", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, @@ -1492,89 +1546,89 @@ } }, "node_modules/@jest/environment": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", - "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0" + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "dependencies": { - "expect": "^29.5.0", - "jest-snapshot": "^29.5.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", - "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3" + "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", - "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.5.0.tgz", - "integrity": "sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/types": "^29.5.0", - "jest-mock": "^29.5.0" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", - "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", @@ -1582,13 +1636,13 @@ "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", @@ -1610,6 +1664,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1627,24 +1682,30 @@ } }, "node_modules/@jest/schemas": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", - "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { - "@sinclair/typebox": "^0.25.16" + "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/schemas/node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, "node_modules/@jest/source-map": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.4.3.tgz", - "integrity": "sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.15", + "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" }, @@ -1653,13 +1714,13 @@ } }, "node_modules/@jest/test-result": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", - "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "dependencies": { - "@jest/console": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, @@ -1668,14 +1729,14 @@ } }, "node_modules/@jest/test-sequencer": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", - "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "dependencies": { - "@jest/test-result": "^29.5.0", + "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { @@ -1683,22 +1744,22 @@ } }, "node_modules/@jest/transform": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.5.0.tgz", - "integrity": "sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/types": "^29.5.0", - "@jridgewell/trace-mapping": "^0.3.15", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", @@ -1709,12 +1770,12 @@ } }, "node_modules/@jest/types": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", - "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", @@ -1726,71 +1787,65 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, "node_modules/@lukeed/ms": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.1.tgz", - "integrity": "sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", "engines": { "node": ">=8" } }, "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", - "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", @@ -1806,87 +1861,34 @@ "node-pre-gyp": "bin/node-pre-gyp" } }, - "node_modules/@mapbox/node-pre-gyp/node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/@mapbox/node-pre-gyp/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/@mongodb-js/saslprep": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", - "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", "optional": true, "dependencies": { "sparse-bitfield": "^3.0.3" } }, "node_modules/@mui/base": { - "version": "5.0.0-beta.2", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.2.tgz", - "integrity": "sha512-R9R+aqrl1QhZJaO05rhvooqxOaf7SKpQ+EjW80sbP3ticTVmLmrn4YBLQS7/ML+WXdrkrPtqSmKFdSE5Ik3gBQ==", - "dependencies": { - "@babel/runtime": "^7.21.0", - "@emotion/is-prop-valid": "^1.2.1", - "@mui/types": "^7.2.4", - "@mui/utils": "^5.13.1", - "@popperjs/core": "^2.11.7", - "clsx": "^1.2.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "version": "5.0.0-beta.58", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.58.tgz", + "integrity": "sha512-P0E7ZrxOuyYqBvVv9w8k7wm+Xzx/KRu+BGgFcR2htTsGCpJNQJCSUXNUZ50MUmSU9hzqhwbQWNXhV1MBTl6F7A==", + "dependencies": { + "@babel/runtime": "^7.25.0", + "@floating-ui/react-dom": "^2.1.1", + "@mui/types": "^7.2.15", + "@mui/utils": "6.0.0-rc.0", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.1", + "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -1900,11 +1902,11 @@ } }, "node_modules/@mui/types": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.4.tgz", - "integrity": "sha512-LBcwa8rN84bKF+f5sDyku42w1NTxaPgPyYKODsh01U1fVstTClbUoSA96oyRBnSNyEiAVjKm6Gwx9vjR+xyqHA==", + "version": "7.2.17", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.17.tgz", + "integrity": "sha512-oyumoJgB6jDV8JFzRqjBo2daUuHpzDjoO/e3IrRhhHo/FxJlaVhET6mcNrKHUq2E+R+q3ql0qAtvQ4rfWHhAeQ==", "peerDependencies": { - "@types/react": "*" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1913,36 +1915,43 @@ } }, "node_modules/@mui/utils": { - "version": "5.13.1", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz", - "integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==", - "dependencies": { - "@babel/runtime": "^7.21.0", - "@types/prop-types": "^15.7.5", - "@types/react-is": "^18.2.0", + "version": "6.0.0-rc.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.0.0-rc.0.tgz", + "integrity": "sha512-tBp0ILEXDL0bbDDT8PnZOjCqSm5Dfk2N0Z45uzRw+wVl6fVvloC9zw8avl+OdX1Bg3ubs/ttKn8nRNv17bpM5A==", + "dependencies": { + "@babel/runtime": "^7.25.0", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "react-is": "^18.3.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@next/env": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.6.tgz", - "integrity": "sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==" + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.7.tgz", + "integrity": "sha512-uVuRqoj28Ys/AI/5gVEgRAISd0KWI0HRjOO1CTpNgmX3ZsHb5mdn14Y59yk0IxizXdo7ZjsI2S7qbWnO+GNBcA==" }, "node_modules/@next/eslint-plugin-next": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.1.tgz", - "integrity": "sha512-tVPS/2FKlA3ANCRCYZVT5jdbUKasBU8LG6bYqcNhyORDFTlDYa4cAWQJjZ7msIgLwMQIbL8CAsxrOL8maa/4Lg==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.5.7.tgz", + "integrity": "sha512-c4vuEOOXeib4js5gDq+zFqAAdRGXX6T0d+zFETiQkRwy7vyj5lBov1dW0Z09nDst2lvxo7VEcKrQMUBH5Vgx7Q==", "dev": true, "dependencies": { "glob": "7.1.7" @@ -1952,6 +1961,7 @@ "version": "7.1.7", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -1969,9 +1979,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.6.tgz", - "integrity": "sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.7.tgz", + "integrity": "sha512-7SxmxMex45FvKtRoP18eftrDCMyL6WQVYJSEE/s7A1AW/fCkznxjEShKet2iVVzf89gWp8HbXGaL4hCaseux6g==", "cpu": [ "arm64" ], @@ -1984,9 +1994,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.6.tgz", - "integrity": "sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.7.tgz", + "integrity": "sha512-6iENvgyIkGFLFszBL4b1VfEogKC3TDPEB6/P/lgxmgXVXIV09Q4or1MVn+U/tYyYmm7oHMZ3oxGpHAyJ80nA6g==", "cpu": [ "x64" ], @@ -1999,9 +2009,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.6.tgz", - "integrity": "sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.7.tgz", + "integrity": "sha512-P42jDX56wu9zEdVI+Xv4zyTeXB3DpqgE1Gb4bWrc0s2RIiDYr6uKBprnOs1hCGIwfVyByxyTw5Va66QCdFFNUg==", "cpu": [ "arm64" ], @@ -2014,9 +2024,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.6.tgz", - "integrity": "sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.7.tgz", + "integrity": "sha512-A06vkj+8X+tLRzSja5REm/nqVOCzR+x5Wkw325Q/BQRyRXWGCoNbQ6A+BR5M86TodigrRfI3lUZEKZKe3QJ9Bg==", "cpu": [ "arm64" ], @@ -2029,9 +2039,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.6.tgz", - "integrity": "sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.7.tgz", + "integrity": "sha512-UdHm7AlxIbdRdMsK32cH0EOX4OmzAZ4Xm+UVlS0YdvwLkI3pb7AoBEoVMG5H0Wj6Wpz6GNkrFguHTRLymTy6kw==", "cpu": [ "x64" ], @@ -2044,9 +2054,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.6.tgz", - "integrity": "sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.7.tgz", + "integrity": "sha512-c50Y8xBKU16ZGj038H6C13iedRglxvdQHD/1BOtes56gwUrIRDX2Nkzn3mYtpz3Wzax0gfAF9C0Nqljt93IxvA==", "cpu": [ "x64" ], @@ -2059,9 +2069,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.6.tgz", - "integrity": "sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.7.tgz", + "integrity": "sha512-NcUx8cmkA+JEp34WNYcKW6kW2c0JBhzJXIbw+9vKkt9m/zVJ+KfizlqmoKf04uZBtzFN6aqE2Fyv2MOd021WIA==", "cpu": [ "arm64" ], @@ -2074,9 +2084,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.6.tgz", - "integrity": "sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.7.tgz", + "integrity": "sha512-wXp+/3NVcuyJDED6gJiLXs5dqHaWO7moAB6aBtjlKZvsxBDxpcyjsfRbtHPeYtaT20zCkmPs69H0K25lrVZmlA==", "cpu": [ "ia32" ], @@ -2089,9 +2099,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.6.tgz", - "integrity": "sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.7.tgz", + "integrity": "sha512-PLyD3Dl6jTTkLG8AoqhPGd5pXtSs8wbqIhWPQt3yEMfnYld/dGYuF2YPs3YHaVFrijCIF9pXY3+QOyvP23Zn7g==", "cpu": [ "x64" ], @@ -2138,10 +2148,19 @@ "node": ">= 8" } }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "engines": { + "node": ">=12.4.0" + } + }, "node_modules/@panva/hkdf": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.1.1.tgz", - "integrity": "sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", "funding": { "url": "https://github.com/sponsors/panva" } @@ -2151,30 +2170,20 @@ "resolved": "https://registry.npmjs.org/@pieropatron/tinylogger/-/tinylogger-1.0.3.tgz", "integrity": "sha512-1fwaZYhM3bDC2dbkF3ZwDiZq+xb/kFfrLNh6J6xEFBXiLaYq3d+6F/cVdQ/OlIbrbwJKgtviTdzEku7sFPBpOQ==" }, - "node_modules/@pkgr/utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.0.tgz", - "integrity": "sha512-2OCURAmRtdlL8iUDTypMrrxfwe8frXTeXaxGsVOaYtc/wrUyk8Z/0OBetM7cdlsy7ZFWlMX72VogKeh+A4Xcjw==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.2.12", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.5.0" - }, + "optional": true, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" + "node": ">=14" } }, "node_modules/@popperjs/core": { - "version": "2.11.7", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", - "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -2195,10 +2204,16 @@ "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz", "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==" }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true + }, "node_modules/@rushstack/eslint-patch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz", - "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==", + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", + "integrity": "sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==", "dev": true }, "node_modules/@sinclair/typebox": { @@ -2207,21 +2222,21 @@ "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==" }, "node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", - "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { - "@sinonjs/commons": "^2.0.0" + "@sinonjs/commons": "^3.0.0" } }, "node_modules/@swc/helpers": { @@ -2233,20 +2248,20 @@ } }, "node_modules/@tabler/icons": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.22.0.tgz", - "integrity": "sha512-lOsGHqRPIKNARMWHHFkUUJH78C8ptQmUcDnumFBUI4YLRKFouKa7uAZL3ZfuH0HjDpOhsnWqUYZ7FhMCLcGpAQ==", + "version": "2.47.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.47.0.tgz", + "integrity": "sha512-4w5evLh+7FUUiA1GucvGj2ReX2TvOjEr4ejXdwL/bsjoSkof6r1gQmzqI+VHrE2CpJpB3al7bCTulOkFa/RcyA==", "funding": { "type": "github", "url": "https://github.com/sponsors/codecalm" } }, "node_modules/@tabler/icons-react": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.20.0.tgz", - "integrity": "sha512-r2uC0Mi3ozHD2G+IYi0A0Iy2203dbQo5EAFxn055MyIhH7U2VNsvyopTqOj+AVedy7cqR86T9zhryRUGC78WZA==", + "version": "2.47.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.47.0.tgz", + "integrity": "sha512-iqly2FvCF/qUbgmvS8E40rVeYY7laltc5GUjRxQj59DuX0x/6CpKHTXt86YlI2whg4czvd/c8Ce8YR08uEku0g==", "dependencies": { - "@tabler/icons": "2.20.0", + "@tabler/icons": "2.47.0", "prop-types": "^15.7.2" }, "funding": { @@ -2257,19 +2272,10 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0" } }, - "node_modules/@tabler/icons-react/node_modules/@tabler/icons": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.20.0.tgz", - "integrity": "sha512-BsUEJoqREs8bqcrf5HfJBq6/rDvsRI3h+T+0X1o7i8LBHonsH0iAngcyL0I82YKoSy9NiVDvM3LV63zDP0nPYQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/codecalm" - } - }, "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", "dev": true }, "node_modules/@tsconfig/node12": { @@ -2285,15 +2291,15 @@ "dev": true }, "node_modules/@tsconfig/node16": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", - "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "dev": true }, "node_modules/@types/babel__core": { - "version": "7.20.0", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz", - "integrity": "sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", @@ -2304,18 +2310,18 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", @@ -2323,70 +2329,70 @@ } }, "node_modules/@types/babel__traverse": { - "version": "7.18.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.5.tgz", - "integrity": "sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/bcrypt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.0.tgz", - "integrity": "sha512-agtcFKaruL8TmcvqbndlqHPSJgsolhf/qPWchFlgnW1gECTN/nKbFcoFnvKAQRFfKbh+BO6A3SWdJu9t+xF3Lw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/cron": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.0.1.tgz", - "integrity": "sha512-WHa/1rtNtD2Q/H0+YTTZoty+/5rcE66iAFX2IY+JuUoOACsevYyFkSYu/2vdw+G5LrmO7Lxowrqm0av4k3qWNQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/cron/-/cron-2.4.0.tgz", + "integrity": "sha512-5bBaAkqvSFBX8JMi/xCofNzG5E594TNsApMz68dLd/sQYz/HGQqgcxGHTRjOvD4G3Y+YF1Oo3S7QdCvKt1KAJQ==", + "deprecated": "This is a stub types definition. cron provides its own type definitions, so you do not need this installed.", "dev": true, "dependencies": { - "@types/luxon": "*", - "@types/node": "*" + "cron": "*" } }, "node_modules/@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/jest": { - "version": "29.5.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.1.tgz", - "integrity": "sha512-tEuVcHrpaixS36w7hpsfLBLpjtMRJUE09/MHXn923LOVojDwyC14cWcfc0rDs0VEfUyYmt/+iX1kxxp+gZMcaQ==", + "version": "29.5.13", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.13.tgz", + "integrity": "sha512-wd+MVEZCHt23V0/L642O5APvspWply/rGY5BcW4SUETo2UzPU3Z26qr8jC2qxpimI2jjx9h7+2cj2FwIr01bXg==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -2394,9 +2400,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/json5": { @@ -2405,44 +2411,56 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, - "node_modules/@types/luxon": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.0.tgz", - "integrity": "sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg==", + "node_modules/@types/lodash": { + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.9.tgz", + "integrity": "sha512-w9iWudx1XWOHW5lQRS9iKpK/XuRhnN+0T7HvdCCd802FYkT1AMTnxndJHGrNJwRoRHkslGr4S29tjm1cT7x/7w==", "dev": true }, + "node_modules/@types/lodash.clonedeep": { + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", + "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/luxon": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.8.tgz", + "integrity": "sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ==" + }, "node_modules/@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", "dev": true }, "node_modules/@types/node": { - "version": "18.16.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.7.tgz", - "integrity": "sha512-MFg7ua/bRtnA1hYE3pVyWxGd/r7aMqjNOdHvlSsXV3n8iaeGKkOaPzpJh6/ovf4bEXWcojkeMJpTsq3mzXW4IQ==" + "version": "18.19.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.53.tgz", + "integrity": "sha512-GLxgUgHhDKO1Edw9Q0lvMbiO/IQXJwJlMaqxSGBXMpPy8uhkCs2iiPFaB2Q/gmobnFkckD3rqTBMVjXdwq+nKg==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "node_modules/@types/prettier": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", - "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { "version": "18.2.6", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.6.tgz", "integrity": "sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==", + "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -2450,37 +2468,30 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.4.tgz", - "integrity": "sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", "dev": true, "dependencies": { "@types/react": "*" } }, - "node_modules/@types/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==", + "devOptional": true }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "node_modules/@types/swagger-schema-official": { @@ -2490,15 +2501,15 @@ "dev": true }, "node_modules/@types/uuid": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", - "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", "dev": true }, "node_modules/@types/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" }, "node_modules/@types/whatwg-url": { "version": "8.2.2", @@ -2509,33 +2520,41 @@ "@types/webidl-conversions": "*" } }, + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.5.tgz", - "integrity": "sha512-feA9xbVRWJZor+AnLNAr7A8JRWeZqHUf4T9tlP+TN04b05pFVhO5eN7/O93Y/1OUlLMHKbnJisgDURs/qvtqdg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.5", - "@typescript-eslint/type-utils": "5.59.5", - "@typescript-eslint/utils": "5.59.5", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -2559,14 +2578,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.59.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.5.tgz", - "integrity": "sha512-NJXQC4MRnF9N9yWqQE2/KLRSOLvrrlZb48NGVfBa+RuPMN6B7ZcK5jZOvhuygv4D64fRKnZI4L4p8+M+rfeQuw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.59.5", - "@typescript-eslint/types": "5.59.5", - "@typescript-eslint/typescript-estree": "5.59.5", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "engines": { @@ -2586,13 +2605,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.5.tgz", - "integrity": "sha512-jVecWwnkX6ZgutF+DovbBJirZcAxgxC0EOHYt/niMROf8p4PwxxG32Qdhj/iIQQIuOflLjNkxoXyArkcIP7C3A==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.5", - "@typescript-eslint/visitor-keys": "5.59.5" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2603,13 +2622,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.5.tgz", - "integrity": "sha512-4eyhS7oGym67/pSxA2mmNq7X164oqDYNnZCUayBwJZIRVvKpBCMBzFnFxjeoDeShjtO6RQBHBuwybuX3POnDqg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.5", - "@typescript-eslint/utils": "5.59.5", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -2630,9 +2649,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.5.tgz", - "integrity": "sha512-xkfRPHbqSH4Ggx4eHRIO/eGL8XL4Ysb4woL8c87YuAo8Md7AUjyWKa9YMwTL519SyDPrfEgKdewjkxNCVeJW7w==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -2643,13 +2662,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.5.tgz", - "integrity": "sha512-+XXdLN2CZLZcD/mO7mQtJMvCkzRfmODbeSKuMY/yXbGkzvA9rJyDY5qDYNoiz2kP/dmyAxXquL2BvLQLJFPQIg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.5", - "@typescript-eslint/visitor-keys": "5.59.5", + "@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", @@ -2670,17 +2689,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.59.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.5.tgz", - "integrity": "sha512-sCEHOiw+RbyTii9c3/qN74hYDPNORb8yWCoPLmB7BIflhplJ65u2PBpdRla12e3SSTJ2erRkPjz7ngLHhUegxA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.5", - "@typescript-eslint/types": "5.59.5", - "@typescript-eslint/typescript-estree": "5.59.5", + "@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" }, @@ -2696,12 +2715,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.5.tgz", - "integrity": "sha512-qL+Oz+dbeBRTeyJTIy0eniD3uvqU7x+y1QceBismZ41hd4aBSRh8UAw4pZP0+XzLuPZmx4raNMq/I+59W2lXKA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.5", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -2712,10 +2731,16 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@xmldom/xmldom": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.8.tgz", - "integrity": "sha512-0LNz4EY8B/8xXY86wMrQ4tz6zEHZv9ehFMJPm8u2gq5lQ71cfRKdaKyxfJAx5aUoyzx0qzgURblTisPGgz3d+Q==", + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", "engines": { "node": ">=10.0.0" } @@ -2742,9 +2767,9 @@ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==" }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2763,10 +2788,13 @@ } }, "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } @@ -2783,14 +2811,14 @@ } }, "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -2890,6 +2918,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "deprecated": "This package is no longer supported.", "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" @@ -2898,19 +2927,6 @@ "node": ">=10" } }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -2933,13 +2949,16 @@ } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2952,15 +2971,16 @@ "dev": true }, "node_modules/array-includes": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", - "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -2979,16 +2999,18 @@ "node": ">=8" } }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", - "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -2997,16 +3019,18 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", - "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", + "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -3015,17 +3039,78 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz", - "integrity": "sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==", + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/arrify": { @@ -3038,9 +3123,15 @@ } }, "node_modules/ast-types-flow": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", - "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, "node_modules/atomic-sleep": { @@ -3052,9 +3143,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.14", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", - "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, "funding": [ { @@ -3064,14 +3155,18 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "browserslist": "^4.21.5", - "caniuse-lite": "^1.0.30001464", - "fraction.js": "^4.2.0", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -3085,10 +3180,13 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -3106,33 +3204,33 @@ } }, "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/axobject-query": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", - "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "dependencies": { - "deep-equal": "^2.0.5" + "engines": { + "node": ">= 0.4" } }, "node_modules/babel-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", - "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "dependencies": { - "@jest/transform": "^29.5.0", + "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.5.0", + "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" @@ -3160,10 +3258,35 @@ "node": ">=8" } }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/babel-plugin-jest-hoist": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", - "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "dependencies": { "@babel/template": "^7.3.3", @@ -3176,35 +3299,38 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@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.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.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-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/babel-preset-jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", - "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { - "babel-plugin-jest-hoist": "^29.5.0", + "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { @@ -3239,45 +3365,27 @@ ] }, "node_modules/bcrypt": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.0.tgz", - "integrity": "sha512-RHBS7HI5N5tEnGTmtR/pppX0mmDSBpQ4aCBsj7CEQfYXDcO74A8sIBYcJMuCsis2E81zDxeENYhv66oZwLiA+Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", "hasInstallScript": true, "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.10", + "@mapbox/node-pre-gyp": "^1.0.11", "node-addon-api": "^5.0.0" }, "engines": { "node": ">= 10.0.0" } }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "engines": { "node": ">=8" - } - }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.44" }, - "engines": { - "node": ">= 5.10.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/brace-expansion": { @@ -3301,9 +3409,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "funding": [ { @@ -3313,13 +3421,17 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -3386,21 +3498,6 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dev": true, - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -3413,13 +3510,19 @@ } }, "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3476,9 +3579,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001486", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001486.tgz", - "integrity": "sha512-uv7/gXuHi10Whlj0pp5q/tsK/32J2QSqVRKQhs2j8VsDCjgyruAh/eEXHF822VqO9yT6iZKw3nRwZRSPBE9OQg==", + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==", "funding": [ { "type": "opencollective", @@ -3520,15 +3623,9 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3541,6 +3638,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -3565,9 +3665,9 @@ } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -3580,9 +3680,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", - "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", "dev": true }, "node_modules/client-only": { @@ -3605,9 +3705,9 @@ } }, "node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { "node": ">=6" } @@ -3623,9 +3723,9 @@ } }, "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, "node_modules/color-convert": { @@ -3704,50 +3804,45 @@ } }, "node_modules/conventional-changelog-angular": { - "version": "5.0.13", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", - "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", + "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", "dev": true, "dependencies": { - "compare-func": "^2.0.0", - "q": "^1.5.1" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/conventional-changelog-conventionalcommits": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-5.0.0.tgz", - "integrity": "sha512-lCDbA+ZqVFQGUj7h9QBKoIpLhl8iihkO0nCTyRNzuXtcd7ubODpYB04IFy31JloiJgG0Uovu8ot8oxRzn7Nwtw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz", + "integrity": "sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw==", "dev": true, "dependencies": { - "compare-func": "^2.0.0", - "lodash": "^4.17.15", - "q": "^1.5.1" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/conventional-commits-parser": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", - "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz", + "integrity": "sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==", "dev": true, "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" + "JSONStream": "^1.3.5", + "meow": "^8.1.2", + "split2": "^3.2.2" }, "bin": { "conventional-commits-parser": "cli.js" }, "engines": { - "node": ">=10" + "node": ">=14" } }, "node_modules/convert-source-map": { @@ -3757,22 +3852,22 @@ "dev": true }, "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "engines": { "node": ">= 0.6" } }, "node_modules/cosmiconfig": { - "version": "8.1.3", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.3.tgz", - "integrity": "sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, "dependencies": { - "import-fresh": "^3.2.1", + "import-fresh": "^3.3.0", "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", + "parse-json": "^5.2.0", "path-type": "^4.0.0" }, "engines": { @@ -3780,22 +3875,50 @@ }, "funding": { "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/cosmiconfig-typescript-loader": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.3.0.tgz", - "integrity": "sha512-NTxV1MFfZDLPiBMjxbHRwSh5LaLcPMwNdCutmnHJCKoVnlvldPWlllonKwrsRJ5pYZBIBGRWWU2tfvzxgeSW5Q==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz", + "integrity": "sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==", "dev": true, "engines": { - "node": ">=12", - "npm": ">=6" + "node": ">=v14.21.3" }, "peerDependencies": { "@types/node": "*", "cosmiconfig": ">=7", "ts-node": ">=10", - "typescript": ">=3" + "typescript": ">=4" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/create-require": { @@ -3805,11 +3928,12 @@ "dev": true }, "node_modules/cron": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/cron/-/cron-2.3.1.tgz", - "integrity": "sha512-1eRRlIT0UfIqauwbG9pkg3J6CX9A6My2ytJWqAXoK0T9oJnUZTzGBNPxao0zjodIbPgf8UQWjE62BMb9eVllSQ==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/cron/-/cron-2.4.4.tgz", + "integrity": "sha512-MHlPImXJj3K7x7lyUHjtKEOl69CSlTOWxS89jiFgNkzXfvhVjhMz/nc7/EIfN9vgooZp8XTtXJ1FREdmbyXOiQ==", "dependencies": { - "luxon": "^3.2.1" + "@types/luxon": "~3.3.0", + "luxon": "~3.3.0" } }, "node_modules/cross-spawn": { @@ -3839,9 +3963,9 @@ } }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -3858,6 +3982,57 @@ "node": ">=8" } }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -3874,11 +4049,11 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3924,21 +4099,29 @@ } }, "node_modules/dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } }, "node_modules/deep-equal": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz", - "integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", + "call-bind": "^1.0.5", "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.0", + "get-intrinsic": "^1.2.2", "is-arguments": "^1.1.1", "is-array-buffer": "^3.0.2", "is-date-object": "^1.0.5", @@ -3948,11 +4131,14 @@ "object-is": "^1.1.5", "object-keys": "^1.1.1", "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", + "regexp.prototype.flags": "^1.5.1", "side-channel": "^1.0.4", "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3973,168 +4159,30 @@ "node": ">=0.10.0" } }, - "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dev": true, - "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/execa": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", - "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", - "dev": true, - "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" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/default-browser/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/default-browser/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, "engines": { - "node": ">=12" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", - "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { + "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" }, @@ -4159,9 +4207,9 @@ } }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "engines": { "node": ">=8" } @@ -4191,9 +4239,9 @@ } }, "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4252,17 +4300,41 @@ } }, "node_modules/dotenv": { - "version": "16.0.3", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", - "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/electron-to-chromium": { - "version": "1.4.391", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.391.tgz", - "integrity": "sha512-GqydVV1+kUWY5qlEzaw34/hyWTApuQrHiGrcGA2Kk/56nEK44i+YUW45VH43JuZT0Oo7uY8aVtpPhBBZXEWtSA==", + "version": "1.5.29", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.29.tgz", + "integrity": "sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw==", "dev": true }, "node_modules/emittery": { @@ -4284,9 +4356,9 @@ "dev": true }, "node_modules/enhanced-resolve": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.0.tgz", - "integrity": "sha512-+DCows0XNwLDcUhbFJPdlQEVnT2zXlCv7hPxemTz86/O+B/hCQ+mb7ydkPKiflpVraqLPCAfu7lDy+hBXueojw==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -4315,45 +4387,57 @@ } }, "node_modules/es-abstract": { - "version": "1.21.2", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.2.tgz", - "integrity": "sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", + "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -4362,6 +4446,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-get-iterator": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", @@ -4382,27 +4487,64 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.7", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", - "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", - "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.0" } }, "node_modules/es-to-primitive": { @@ -4429,9 +4571,9 @@ "dev": true }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" @@ -4455,27 +4597,28 @@ } }, "node_modules/eslint": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.40.0.tgz", - "integrity": "sha512-bvR+TsP9EHL3TqNtj9sCNJVAFK3fBN8Q7g5waghxyRsPLIMwL73XSKnZFK0hk/O2ANC+iAoq6PWMQ+IfBAJIiQ==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.40.0", - "@humanwhocodes/config-array": "^0.11.8", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@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.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "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", @@ -4483,22 +4626,19 @@ "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "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.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -4512,20 +4652,20 @@ } }, "node_modules/eslint-config-next": { - "version": "13.4.1", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.4.1.tgz", - "integrity": "sha512-ajuxjCkW1hvirr0EQZb3/B/bFH52Z7CT89uCtTcICFL9l30i5c8hN4p0LXvTjdOXNPV5fEDcxBgGHgXdzTj1/A==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.5.7.tgz", + "integrity": "sha512-pdeUuL9KZ8qFzzKqCbxk6FXwG9dNEnot/3+qSFJqxdSGgkFUH8cgZus/meyCi2S0cTAsDbBEE030E6zvL9pUYQ==", "dev": true, "dependencies": { - "@next/eslint-plugin-next": "13.4.1", - "@rushstack/eslint-patch": "^1.1.3", - "@typescript-eslint/parser": "^5.42.0", + "@next/eslint-plugin-next": "13.5.7", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.31.7", - "eslint-plugin-react-hooks": "^4.5.0" + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0", @@ -4538,9 +4678,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", - "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -4550,14 +4690,14 @@ } }, "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", - "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", "dev": true, "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" } }, "node_modules/eslint-import-resolver-node/node_modules/debug": { @@ -4568,21 +4708,21 @@ "dependencies": { "ms": "^2.1.1" } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.5.5.tgz", - "integrity": "sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.12.0", - "eslint-module-utils": "^2.7.4", - "get-tsconfig": "^4.5.0", - "globby": "^13.1.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "synckit": "^0.8.5" + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.3.tgz", + "integrity": "sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==", + "dev": true, + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.3.5", + "enhanced-resolve": "^5.15.0", + "eslint-module-utils": "^2.8.1", + "fast-glob": "^3.3.2", + "get-tsconfig": "^4.7.5", + "is-bun-module": "^1.0.2", + "is-glob": "^4.0.3" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4592,44 +4732,22 @@ }, "peerDependencies": { "eslint": "*", - "eslint-plugin-import": "*" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/globby": { - "version": "13.1.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", - "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } } }, "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", "dev": true, "dependencies": { "debug": "^3.2.7" @@ -4653,26 +4771,29 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.30.0.tgz", + "integrity": "sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", "debug": "^3.2.7", "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.9.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" @@ -4712,42 +4833,33 @@ } }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", - "integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.0.tgz", + "integrity": "sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==", "dev": true, "dependencies": { - "@babel/runtime": "^7.20.7", - "aria-query": "^5.1.3", - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.6.2", - "axobject-query": "^3.1.1", + "aria-query": "~5.1.3", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.3", - "language-tags": "=1.0.5", + "es-iterator-helpers": "^1.0.19", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "semver": "^6.3.0" + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.0" }, "engines": { "node": ">=4.0" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "node_modules/eslint-plugin-prettier": { @@ -4772,38 +4884,41 @@ } }, "node_modules/eslint-plugin-react": { - "version": "7.32.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", - "integrity": "sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==", + "version": "7.37.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.0.tgz", + "integrity": "sha512-IHBePmfWH5lKhJnJ7WB1V+v/GolbB0rjS8XYVCSQCZKaQCAUhMoVoOEn1Ef8Z8Wf0a7l8KTJvuZg5/e4qrZ6nA==", "dev": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.2", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", + "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.0", "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.11", + "string.prototype.repeat": "^1.0.0" }, "engines": { "node": ">=4" }, "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", + "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true, "engines": { "node": ">=10" @@ -4825,12 +4940,12 @@ } }, "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz", - "integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==", + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", "dev": true, "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4873,9 +4988,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -4901,9 +5016,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -4923,12 +5038,12 @@ "dev": true }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -4953,9 +5068,9 @@ } }, "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -5055,16 +5170,16 @@ } }, "node_modules/expect": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", - "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "@jest/expect-utils": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -5086,15 +5201,15 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5155,6 +5270,11 @@ } } }, + "node_modules/fast-json-stringify/node_modules/fast-uri": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", + "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==" + }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -5184,9 +5304,9 @@ "dev": true }, "node_modules/fast-uri": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.2.0.tgz", - "integrity": "sha512-cIusKBIt/R/oI6z/1nyfe2FvGKVTohVRfvkOhvx0nCEW+xf5NoCXjAHcWp93uOUBchzYcsvPlrapAdX1uW+YGg==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.2.tgz", + "integrity": "sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==" }, "node_modules/fastify": { "version": "4.28.1", @@ -5222,9 +5342,9 @@ } }, "node_modules/fastify-plugin": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.0.tgz", - "integrity": "sha512-79ak0JxddO0utAXAQ5ccKhvs6vX2MGyHHMMsmZkBANrq3hXc1CHzvNPHOcvTsVMEPl5I+NT+RO4YKMGehOfSIg==" + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", + "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==" }, "node_modules/fastq": { "version": "1.17.1", @@ -5255,6 +5375,36 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -5296,12 +5446,13 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -5309,9 +5460,9 @@ } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/for-each": { @@ -5323,6 +5474,34 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5332,22 +5511,22 @@ } }, "node_modules/fraction.js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", - "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true, "engines": { "node": "*" }, "funding": { "type": "patreon", - "url": "https://www.patreon.com/infusion" + "url": "https://github.com/sponsors/rawify" } }, "node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -5391,9 +5570,9 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -5404,21 +5583,24 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", - "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" }, "engines": { "node": ">= 0.4" @@ -5440,6 +5622,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "deprecated": "This package is no longer supported.", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", @@ -5474,14 +5657,19 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5509,13 +5697,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" @@ -5525,10 +5714,13 @@ } }, "node_modules/get-tsconfig": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.5.0.tgz", - "integrity": "sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", + "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", "dev": true, + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, "funding": { "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } @@ -5556,6 +5748,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -5619,9 +5812,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -5634,12 +5827,13 @@ } }, "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "define-properties": "^1.2.1", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -5669,9 +5863,9 @@ } }, "node_modules/goober": { - "version": "2.1.13", - "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.13.tgz", - "integrity": "sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==", + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz", + "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==", "peerDependencies": { "csstype": "^3.0.10" } @@ -5693,10 +5887,10 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "node_modules/hard-rejection": { @@ -5708,18 +5902,6 @@ "node": ">=6" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -5739,21 +5921,21 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.1" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, "engines": { "node": ">= 0.4" @@ -5775,12 +5957,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -5794,6 +5976,18 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -5920,9 +6114,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "engines": { "node": ">= 4" @@ -5934,9 +6128,9 @@ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" }, "node_modules/immutable": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.0.tgz", - "integrity": "sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", + "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", "devOptional": true }, "node_modules/import-fresh": { @@ -5965,9 +6159,9 @@ } }, "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "dependencies": { "pkg-dir": "^4.2.0", @@ -6005,6 +6199,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -6022,13 +6217,13 @@ "dev": true }, "node_modules/internal-slot": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz", - "integrity": "sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", + "es-errors": "^1.3.0", + "hasown": "^2.0.0", "side-channel": "^1.0.4" }, "engines": { @@ -6047,11 +6242,6 @@ "node": ">= 12" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -6077,14 +6267,16 @@ } }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6096,6 +6288,21 @@ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -6135,6 +6342,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-bun-module": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-1.2.1.tgz", + "integrity": "sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==", + "dev": true, + "dependencies": { + "semver": "^7.6.3" + } + }, + "node_modules/is-bun-module/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -6148,24 +6376,27 @@ } }, "node_modules/is-core-module": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz", - "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dev": true, "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -6174,19 +6405,19 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, - "bin": { - "is-docker": "cli.js" + "dependencies": { + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-extglob": { @@ -6197,6 +6428,18 @@ "node": ">=0.10.0" } }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -6214,6 +6457,21 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -6225,37 +6483,22 @@ "node": ">=0.10.0" } }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -6331,21 +6574,27 @@ } }, "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6406,16 +6655,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", - "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -6425,10 +6670,13 @@ } }, "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6446,43 +6694,19 @@ } }, "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/isarray": { @@ -6498,51 +6722,57 @@ "dev": true }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "semver": "^7.5.4" }, "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node": ">=10" } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/istanbul-lib-source-maps": { @@ -6560,9 +6790,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -6572,16 +6802,62 @@ "node": ">=8" } }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jest": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", - "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "@jest/core": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", "import-local": "^3.0.2", - "jest-cli": "^29.5.0" + "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" @@ -6599,12 +6875,13 @@ } }, "node_modules/jest-changed-files": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", - "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { "execa": "^5.0.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { @@ -6612,28 +6889,28 @@ } }, "node_modules/jest-circus": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", - "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/expect": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", - "dedent": "^0.7.0", + "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", - "jest-each": "^29.5.0", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "p-limit": "^3.1.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" @@ -6643,22 +6920,21 @@ } }, "node_modules/jest-cli": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", - "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { - "@jest/core": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", + "create-jest": "^29.7.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.9", "import-local": "^3.0.2", - "jest-config": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", - "prompts": "^2.0.1", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { @@ -6677,31 +6953,31 @@ } }, "node_modules/jest-config": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", - "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.5.0", - "@jest/types": "^29.5.0", - "babel-jest": "^29.5.0", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-circus": "^29.5.0", - "jest-environment-node": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-runner": "^29.5.0", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, @@ -6725,6 +7001,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -6742,24 +7019,24 @@ } }, "node_modules/jest-diff": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", - "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^29.4.3", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", - "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" @@ -6769,62 +7046,62 @@ } }, "node_modules/jest-each": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", - "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", - "jest-util": "^29.5.0", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", - "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.5.0", - "jest-util": "^29.5.0" + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", - "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.5.0.tgz", - "integrity": "sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.4.3", - "jest-util": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, @@ -6836,46 +7113,46 @@ } }, "node_modules/jest-leak-detector": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", - "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "dependencies": { - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", - "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "pretty-format": "^29.5.0" + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", - "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", - "pretty-format": "^29.5.0", + "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, @@ -6884,14 +7161,14 @@ } }, "node_modules/jest-mock": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", - "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "jest-util": "^29.5.0" + "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -6915,26 +7192,26 @@ } }, "node_modules/jest-regex-util": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.4.3.tgz", - "integrity": "sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", - "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", + "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.5.0", - "jest-validate": "^29.5.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" @@ -6944,43 +7221,43 @@ } }, "node_modules/jest-resolve-dependencies": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", - "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "dependencies": { - "jest-regex-util": "^29.4.3", - "jest-snapshot": "^29.5.0" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", - "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { - "@jest/console": "^29.5.0", - "@jest/environment": "^29.5.0", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.4.3", - "jest-environment-node": "^29.5.0", - "jest-haste-map": "^29.5.0", - "jest-leak-detector": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-resolve": "^29.5.0", - "jest-runtime": "^29.5.0", - "jest-util": "^29.5.0", - "jest-watcher": "^29.5.0", - "jest-worker": "^29.5.0", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, @@ -6989,31 +7266,31 @@ } }, "node_modules/jest-runtime": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.5.0.tgz", - "integrity": "sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.5.0", - "@jest/fake-timers": "^29.5.0", - "@jest/globals": "^29.5.0", - "@jest/source-map": "^29.4.3", - "@jest/test-result": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-mock": "^29.5.0", - "jest-regex-util": "^29.4.3", - "jest-resolve": "^29.5.0", - "jest-snapshot": "^29.5.0", - "jest-util": "^29.5.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, @@ -7025,6 +7302,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -7042,46 +7320,43 @@ } }, "node_modules/jest-snapshot": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", - "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.5.0", - "@jest/transform": "^29.5.0", - "@jest/types": "^29.5.0", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.5.0", + "expect": "^29.7.0", "graceful-fs": "^4.2.9", - "jest-diff": "^29.5.0", - "jest-get-type": "^29.4.3", - "jest-matcher-utils": "^29.5.0", - "jest-message-util": "^29.5.0", - "jest-util": "^29.5.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", "natural-compare": "^1.4.0", - "pretty-format": "^29.5.0", - "semver": "^7.3.5" + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-util": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", - "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", @@ -7093,17 +7368,17 @@ } }, "node_modules/jest-validate": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", - "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { - "@jest/types": "^29.5.0", + "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^29.4.3", + "jest-get-type": "^29.6.3", "leven": "^3.1.0", - "pretty-format": "^29.5.0" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -7122,18 +7397,18 @@ } }, "node_modules/jest-watcher": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", - "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { - "@jest/test-result": "^29.5.0", - "@jest/types": "^29.5.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", - "jest-util": "^29.5.0", + "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { @@ -7141,13 +7416,13 @@ } }, "node_modules/jest-worker": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", - "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", - "jest-util": "^29.5.0", + "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -7171,32 +7446,22 @@ } }, "node_modules/jiti": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz", - "integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==", + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "dev": true, "bin": { "jiti": "bin/jiti.js" } }, "node_modules/jose": { - "version": "4.15.5", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz", - "integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==", + "version": "4.15.9", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.9.tgz", + "integrity": "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==", "funding": { "url": "https://github.com/sponsors/panva" } }, - "node_modules/js-sdsl": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.0.tgz", - "integrity": "sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7231,6 +7496,12 @@ "node": ">=4" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -7322,18 +7593,29 @@ } }, "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz", - "integrity": "sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", "dev": true, "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" }, "engines": { "node": ">=4.0" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -7353,18 +7635,21 @@ } }, "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", "dev": true }, "node_modules/language-tags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz", - "integrity": "sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", "dev": true, "dependencies": { - "language-subtag-registry": "~0.3.2" + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" } }, "node_modules/leven": { @@ -7390,11 +7675,11 @@ } }, "node_modules/light-my-request": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.14.0.tgz", - "integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.13.0.tgz", + "integrity": "sha512-9IjUN9ZyCS9pTG+KqTDEQo68Sui2lHsYBrfMyVUTTZ3XhH8PMZq7xO94Kr+eP9dhi/kcKsx4N41p2IXEBil1pQ==", "dependencies": { - "cookie": "^0.7.0", + "cookie": "^0.6.0", "process-warning": "^3.0.0", "set-cookie-parser": "^2.4.1" } @@ -7441,6 +7726,12 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -7770,19 +8061,19 @@ } }, "node_modules/mnemonist": { - "version": "0.39.5", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.5.tgz", - "integrity": "sha512-FPUtkhtJ0efmEFGpU14x7jGbTB+s18LrzRL2KgoWz9YvcY3cPomz8tih01GbHwnGk/OmkOKfqd/RAQoc8Lm7DQ==", + "version": "0.39.6", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", + "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==", "dependencies": { "obliterator": "^2.0.1" } }, "node_modules/mongodb": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.8.0.tgz", - "integrity": "sha512-xx4CXmxcj3bNe7iGBlhntVrUqrNARYhUZteXaz4epEESv4oXD/FONAovcyoCaEffdYlw25Yz284OxMfpnPLlgQ==", + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-5.9.2.tgz", + "integrity": "sha512-H60HecKO4Bc+7dhOv4sJlgvenK4fQNqqUIlXxZYQNbfEWSALGAwGoyJd/0Qwk4TttFXUOHJ2ZJQe/52ScaUwtQ==", "dependencies": { - "bson": "^5.4.0", + "bson": "^5.5.0", "mongodb-connection-string-url": "^2.6.0", "socks": "^2.7.1" }, @@ -7827,9 +8118,9 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mz": { "version": "2.7.0", @@ -7843,9 +8134,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -7872,11 +8163,11 @@ "dev": true }, "node_modules/next": { - "version": "13.5.6", - "resolved": "https://registry.npmjs.org/next/-/next-13.5.6.tgz", - "integrity": "sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/next/-/next-13.5.7.tgz", + "integrity": "sha512-W7KIRTE+hPcgGdq89P3mQLDX3m7pJ6nxSyC+YxYaUExE+cS4UledB+Ntk98tKoyhsv6fjb2TRAnD7VDvoqmeFg==", "dependencies": { - "@next/env": "13.5.6", + "@next/env": "13.5.7", "@swc/helpers": "0.5.2", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", @@ -7891,15 +8182,15 @@ "node": ">=16.14.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "13.5.6", - "@next/swc-darwin-x64": "13.5.6", - "@next/swc-linux-arm64-gnu": "13.5.6", - "@next/swc-linux-arm64-musl": "13.5.6", - "@next/swc-linux-x64-gnu": "13.5.6", - "@next/swc-linux-x64-musl": "13.5.6", - "@next/swc-win32-arm64-msvc": "13.5.6", - "@next/swc-win32-ia32-msvc": "13.5.6", - "@next/swc-win32-x64-msvc": "13.5.6" + "@next/swc-darwin-arm64": "13.5.7", + "@next/swc-darwin-x64": "13.5.7", + "@next/swc-linux-arm64-gnu": "13.5.7", + "@next/swc-linux-arm64-musl": "13.5.7", + "@next/swc-linux-x64-gnu": "13.5.7", + "@next/swc-linux-x64-musl": "13.5.7", + "@next/swc-win32-arm64-msvc": "13.5.7", + "@next/swc-win32-ia32-msvc": "13.5.7", + "@next/swc-win32-x64-msvc": "13.5.7" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -7947,6 +8238,14 @@ } } }, + "node_modules/next-auth/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next-auth/node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -7955,11 +8254,57 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-fetch-h2": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", @@ -7972,6 +8317,25 @@ "node": "4.x || >=6.0.0" } }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -7988,9 +8352,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz", - "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==", + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", "dev": true }, "node_modules/nodemon": { @@ -8040,9 +8404,9 @@ } }, "node_modules/nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", "dependencies": { "abbrev": "1" }, @@ -8050,7 +8414,7 @@ "nopt": "bin/nopt.js" }, "engines": { - "node": "*" + "node": ">=6" } }, "node_modules/normalize-package-data": { @@ -8101,6 +8465,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "deprecated": "This package is no longer supported.", "dependencies": { "are-we-there-yet": "^2.0.0", "console-control-strings": "^1.1.0", @@ -8219,31 +8584,33 @@ } }, "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", "engines": { "node": ">= 6" } }, "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", "dev": true, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -8262,13 +8629,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -8280,28 +8647,29 @@ } }, "node_modules/object.entries": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz", - "integrity": "sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/object.fromentries": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz", - "integrity": "sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8310,28 +8678,29 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.hasown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz", - "integrity": "sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==", + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", "dev": true, "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 0.4" } }, "node_modules/object.values": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", - "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", + "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -8384,35 +8753,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dev": true, - "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/openapi-types": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.0.tgz", - "integrity": "sha512-XpeCy01X6L5EpP+6Hc3jWN7rMZJ+/k1lwki/kTmWzbVhdPie3jd5O2ZtedEx8Yp58icJ0osVldLMrTB/zslQXA==" + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" }, "node_modules/openid-client": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.4.2.tgz", - "integrity": "sha512-lIhsdPvJ2RneBm3nGBBhQchpe3Uka//xf7WPHTIglery8gnckvW7Bd9IaQzekzXJvWthCMyi/xVEyGW0RFPytw==", + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.7.0.tgz", + "integrity": "sha512-4GCCGZt1i2kTHpwvaC/sCpTpQqDnBzDzuJcJMbH+y1Q5qI8U8RBvoSh28svarXszZHR5BAMXbJPX1PGPRE3VOA==", "dependencies": { - "jose": "^4.14.1", + "jose": "^4.15.9", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" @@ -8432,23 +8783,15 @@ "node": ">=10" } }, - "node_modules/openid-client/node_modules/object-hash": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", - "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", - "engines": { - "node": ">= 6" - } - }, "node_modules/openid-client/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "dependencies": { "deep-is": "^0.1.3", @@ -8456,7 +8799,7 @@ "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" @@ -8500,6 +8843,12 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -8562,6 +8911,28 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -8572,9 +8943,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -8626,6 +8997,21 @@ "split2": "^4.0.0" } }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/pino-abstract-transport/node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -8645,9 +9031,9 @@ "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==" }, "node_modules/pirates": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", - "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, "engines": { "node": ">= 6" @@ -8717,10 +9103,20 @@ "node": ">=8" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -8736,9 +9132,9 @@ } ], "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -8781,21 +9177,27 @@ } }, "node_modules/postcss-load-config": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", - "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^2.1.1" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, "engines": { "node": ">= 14" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" @@ -8809,29 +9211,47 @@ } } }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "postcss-selector-parser": "^6.0.11" + "postcss-selector-parser": "^6.1.1" }, "engines": { "node": ">=12.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, "peerDependencies": { "postcss": "^8.2.14" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz", - "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -8848,9 +9268,9 @@ "dev": true }, "node_modules/preact": { - "version": "10.15.1", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.15.1.tgz", - "integrity": "sha512-qs2ansoQEwzNiV5eAcRT1p1EC/dmEzaATVDJNiB3g2sRDWdA7b7MurXdJjB2+/WQktGWZwxvDrnuRFbWuIr64g==", + "version": "10.24.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.1.tgz", + "integrity": "sha512-PnBAwFI3Yjxxcxw75n6VId/5TFxNW/81zexzWD9jn1+eSrOP84NdsS38H5IkF/UH3frqRPT+MvuCoVHjTDTnDw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -8909,12 +9329,12 @@ } }, "node_modules/pretty-format": { - "version": "29.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", - "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "@jest/schemas": "^29.4.3", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -8993,17 +9413,17 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "engines": { "node": ">=6" } }, "node_modules/pure-rand": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", - "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { @@ -9016,16 +9436,6 @@ } ] }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -9061,9 +9471,9 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dependencies": { "loose-envify": "^1.1.0" }, @@ -9109,15 +9519,15 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-hot-toast": { @@ -9136,9 +9546,9 @@ } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" }, "node_modules/read-cache": { "version": "1.0.0", @@ -9279,17 +9689,16 @@ } }, "node_modules/readable-stream": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.4.0.tgz", - "integrity": "sha512-kDMOq0qLtxV9f/SQv522h8cxZBqNZXuXNyjyezmfAAuribMyVXziljpQ/uQhfE1XLg2/TLTW2DsnoE4VAi/krg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 6" } }, "node_modules/readdirp": { @@ -9332,6 +9741,27 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", + "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.1", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/reftools": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", @@ -9342,19 +9772,20 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz", - "integrity": "sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -9381,12 +9812,12 @@ } }, "node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -9430,6 +9861,15 @@ "node": ">=8" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -9457,14 +9897,15 @@ } }, "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dependencies": { "glob": "^7.1.3" }, @@ -9479,6 +9920,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -9494,21 +9936,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -9532,6 +9959,24 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9552,15 +9997,18 @@ ] }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -9582,12 +10030,12 @@ } }, "node_modules/sass": { - "version": "1.62.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.62.1.tgz", - "integrity": "sha512-NHpxIzN29MXvWiuswfc1W3I0N8SXBd8UR26WntmDlRYf0bSADnwnOjsyMZ3lMezSlArD33Vs3YFhp7dWvL770A==", + "version": "1.79.3", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.79.3.tgz", + "integrity": "sha512-m7dZxh0W9EZ3cw50Me5GOuYm/tVAJAn91SUnohLRo9cXBixGUOdvmryN+dXpwR831bhoY3Zv7rEFt85PUwTmzA==", "devOptional": true, "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", + "chokidar": "^4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, @@ -9598,10 +10046,38 @@ "node": ">=14.0.0" } }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", + "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", + "devOptional": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.1.tgz", + "integrity": "sha512-GkMg9uOTpIWWKbSsgwb5fA4EavTR+SG/PMPoAY8hkhHfEEY0/vqljY+XHqtDf2cr2IJtoNRDbrrEpZUiZCkYRw==", + "devOptional": true, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dependencies": { "loose-envify": "^1.1.0" } @@ -9651,6 +10127,38 @@ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.0.tgz", "integrity": "sha512-lXLOiqpkUumhRdFF3k1osNXCy9akgx/dyPZ5p8qAg9seJzXr5ZrlqZuWIMuY6ejOsVLE6flJ5/h3lsn57fQ/PQ==" }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", @@ -9732,14 +10240,18 @@ "dev": true }, "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9816,9 +10328,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -9853,9 +10365,9 @@ } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", "dev": true }, "node_modules/spdx-expression-parse": { @@ -9869,9 +10381,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.20.tgz", + "integrity": "sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==", "dev": true }, "node_modules/split2": { @@ -9883,25 +10395,10 @@ "readable-stream": "^3.0.0" } }, - "node_modules/split2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "node_modules/stack-utils": { "version": "2.0.6", @@ -9986,39 +10483,88 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/string.prototype.includes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz", + "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.matchall": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", - "integrity": "sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", + "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" + "internal-slot": "^1.0.7", + "regexp.prototype.flags": "^1.5.2", + "set-function-name": "^2.0.2", + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.trim": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz", - "integrity": "sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -10028,28 +10574,31 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", - "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", - "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -10066,6 +10615,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -10131,14 +10693,14 @@ } }, "node_modules/sucrase": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", - "integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==", + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", - "glob": "7.1.6", + "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", @@ -10149,29 +10711,62 @@ "sucrase-node": "bin/sucrase-node" }, "engines": { - "node": ">=8" + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/sucrase/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "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" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/sucrase/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -10270,6 +10865,19 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/swagger-typescript-api/node_modules/typescript": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/swagger2openapi": { "version": "7.0.8", "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", @@ -10297,48 +10905,6 @@ "url": "https://github.com/Mermade/oas-kit?sponsor=1" } }, - "node_modules/swagger2openapi/node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/swagger2openapi/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, - "node_modules/swagger2openapi/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, - "node_modules/swagger2openapi/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/swagger2openapi/node_modules/yaml": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", @@ -10348,35 +10914,19 @@ "node": ">= 6" } }, - "node_modules/synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", - "dev": true, - "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/tailwind-merge": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.13.2.tgz", - "integrity": "sha512-R2/nULkdg1VR/EL4RXg4dEohdoxNUJGLMnWIQnPKL+O9Twu7Cn3Rxi4dlXkDzZrEGtR+G+psSXFouWlpTyLhCQ==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz", + "integrity": "sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==", "funding": { "type": "github", "url": "https://github.com/sponsors/dcastil" } }, "node_modules/tailwindcss": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz", - "integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==", + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", + "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -10384,10 +10934,10 @@ "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", - "fast-glob": "^3.2.12", + "fast-glob": "^3.3.0", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.18.2", + "jiti": "^1.21.0", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", @@ -10399,7 +10949,6 @@ "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", - "postcss-value-parser": "^4.2.0", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, @@ -10411,6 +10960,15 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss/node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -10459,6 +11017,7 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", @@ -10548,18 +11107,6 @@ "node": ">= 6" } }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -10603,12 +11150,9 @@ } }, "node_modules/touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dependencies": { - "nopt": "~1.0.10" - }, + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", "bin": { "nodetouch": "bin/nodetouch.js" } @@ -10640,28 +11184,30 @@ "dev": true }, "node_modules/ts-jest": { - "version": "29.1.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.0.tgz", - "integrity": "sha512-ZhNr7Z4PcYa+JjMl62ir+zPiNJfXJN6E8hSLnaUKhOgqcn8vb3e537cpkd0FuAfRK3sR1LSqM1MOhliXNgOFPA==", + "version": "29.2.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.2.5.tgz", + "integrity": "sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA==", "dev": true, "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", + "bs-logger": "^0.2.6", + "ejs": "^3.1.10", + "fast-json-stable-stringify": "^2.1.0", "jest-util": "^29.0.0", "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.6.3", + "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0", "@jest/types": "^29.0.0", "babel-jest": "^29.0.0", "jest": "^29.0.0", @@ -10671,6 +11217,9 @@ "@babel/core": { "optional": true }, + "@jest/transform": { + "optional": true + }, "@jest/types": { "optional": true }, @@ -10682,6 +11231,18 @@ } } }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ts-jest/node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", @@ -10692,9 +11253,9 @@ } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -10741,9 +11302,9 @@ "dev": true }, "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", @@ -10774,9 +11335,9 @@ } }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -10832,24 +11393,83 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -10879,28 +11499,24 @@ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, "engines": { "node": ">= 10.0.0" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -10917,8 +11533,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -10941,9 +11557,13 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -10955,25 +11575,19 @@ "dev": true }, "node_modules/v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" + "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, - "node_modules/v8-to-istanbul/node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -11056,33 +11670,61 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/which-builtin-type": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.4.tgz", + "integrity": "sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/which-typed-array": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", - "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11125,6 +11767,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -11143,6 +11803,26 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -11159,9 +11839,12 @@ "dev": true }, "node_modules/yaml": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", - "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "bin": { + "yaml": "bin.mjs" + }, "engines": { "node": ">= 14" } diff --git a/package.json b/package.json index 62e1d933..dab56da9 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "pretty:format": "prettier --write .", "typecheck": "tsc --noEmit -p tsconfig.json", "lint": "next lint", - "dev": "./update_gui_version.sh && next dev", + "dev": "next dev", "build": "next build", "start": "next start", "version:rc": "npm version prerelease --preid=rc", @@ -32,11 +32,13 @@ "@sinclair/typebox": "^0.25.24", "@tabler/icons": "^2.22.0", "@tabler/icons-react": "^2.20.0", + "@types/ws": "^8.5.12", "bcrypt": "^5.1.0", "cron": "^2.3.1", "date-fns": "^2.30.0", "dotenv": "^16.0.3", "fastify": "^4.28.1", + "lodash.clonedeep": "^4.5.0", "lodash.get": "^4.4.2", "mongodb": "^5.8.0", "next": "^13.5.6", @@ -48,7 +50,8 @@ "react-dom": "^18.2.0", "react-hot-toast": "^2.4.1", "tailwind-merge": "^1.13.2", - "uuid": "^9.0.0" + "uuid": "^9.0.0", + "ws": "^8.18.0" }, "devDependencies": { "@commitlint/cli": "^17.4.2", @@ -56,6 +59,7 @@ "@types/bcrypt": "^5.0.0", "@types/cron": "^2.0.1", "@types/jest": "^29.4.0", + "@types/lodash.clonedeep": "^4.5.9", "@types/node": "^18.16.7", "@types/react": "18.2.6", "@types/react-dom": "^18.2.4", diff --git a/public/images/ateliere-logo.png b/public/images/ateliere-logo.png new file mode 100644 index 00000000..d999352e Binary files /dev/null and b/public/images/ateliere-logo.png differ diff --git a/src/api/ateliereLive/ingest.ts b/src/api/ateliereLive/ingest.ts index 0c7b5218..71775c2f 100644 --- a/src/api/ateliereLive/ingest.ts +++ b/src/api/ateliereLive/ingest.ts @@ -1,10 +1,13 @@ import { ResourcesCompactIngestResponse, ResourcesIngestResponse, + ResourcesIngestStreamResponse, + ResourcesSourceResponse, ResourcesThumbnailResponse } from '../../../types/ateliere-live'; import { LIVE_BASE_API_PATH } from '../../constants'; import { getAuthorizationHeader } from './utils/authheader'; +import { SrtSource } from '../../interfaces/Source'; // TODO: create proper cache... const INGEST_UUID_CACHE: Map = new Map(); @@ -14,19 +17,21 @@ export async function getUuidFromIngestName( ingestName: string, useCache = true ) { - const cache = INGEST_UUID_CACHE.get(ingestName); - if (cache && useCache) { - return cache; - } - const ingests = await getIngests(); - const ingest = ingests.find((ingest) => ingest.name === ingestName); + if (ingestName !== undefined) { + const cache = INGEST_UUID_CACHE.get(ingestName); + if (cache && useCache) { + return cache; + } + const ingests = await getIngests(); + const ingest = ingests.find((ingest) => ingest.name === ingestName); - if (ingest && ingest.uuid) { + if (!ingest) { + console.warn(`Could not find ingest ${ingestName}`); + throw 'get_uuid'; + } INGEST_UUID_CACHE.set(ingestName, ingest.uuid); return ingest.uuid; } - console.warn(`Could not find UUID for ${ingestName}`); - throw 'get_uuid'; } export async function getSourceIdFromSourceName( @@ -34,26 +39,28 @@ export async function getSourceIdFromSourceName( sourceName: string, useCache = true ) { - let ingestCache = SOURCE_ID_CACHE.get(ingestUuid); - if (!ingestCache) { - ingestCache = new Map(); - SOURCE_ID_CACHE.set(ingestUuid, ingestCache); - } - const cache = ingestCache?.get(sourceName); - if (cache && useCache) { - return cache; - } - const ingest = await getIngest(ingestUuid); - const source = ingest.sources?.find((source) => source.name === sourceName); + if (ingestUuid !== undefined && sourceName !== undefined) { + let ingestCache = SOURCE_ID_CACHE.get(ingestUuid); + if (!ingestCache) { + ingestCache = new Map(); + SOURCE_ID_CACHE.set(ingestUuid, ingestCache); + } + const cache = ingestCache?.get(sourceName); + if (cache && useCache) { + return cache; + } + const ingest = await getIngest(ingestUuid); + const source = ingest.sources?.find((source) => source.name === sourceName); - if (source && source.source_id !== undefined) { - ingestCache.set(sourceName, source.source_id); - return source.source_id; + if (source && source.source_id !== undefined) { + ingestCache.set(sourceName, source.source_id); + return source.source_id; + } + console.warn( + `Could not find id for source ${sourceName} in ingest ${ingestUuid}` + ); + throw `Could not find id for source ${sourceName} in ingest ${ingestUuid}`; } - console.warn( - `Could not find id for source ${sourceName} in ingest ${ingestUuid}` - ); - throw `Could not find id for source ${sourceName} in ingest ${ingestUuid}`; } export async function getIngests(): Promise { @@ -106,6 +113,7 @@ export async function getSourceThumbnail( process.env.LIVE_URL ), { + next: { tags: ['image'] }, method: 'POST', body: JSON.stringify({ encoder: 'auto', @@ -114,7 +122,8 @@ export async function getSourceThumbnail( width }), headers: { - authorization: getAuthorizationHeader() + authorization: getAuthorizationHeader(), + cache: 'no-store' } } ); @@ -146,3 +155,92 @@ export async function deleteSrtSource(ingestUuid: string, sourceId: number) { } throw await response.text(); } + +export async function createSrtSource( + ingestUuid: string, + srtPayload: SrtSource +) { + let payload; + + // Making sure remote_port is only passed if mode is 'caller' + if (srtPayload.mode === 'caller') { + payload = { + srt_source: { + ...srtPayload, + local_port: Number(srtPayload.local_port), + latency_ms: Number(srtPayload.latency_ms), + remote_port: Number(srtPayload.remote_port) + } + }; + } else { + payload = { + srt_source: { + ...srtPayload, + local_port: Number(srtPayload.local_port), + latency_ms: Number(srtPayload.latency_ms) + } + }; + } + + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/ingests/${ingestUuid}/sources`, + process.env.LIVE_URL + ), + { + method: 'POST', + body: JSON.stringify(payload), + headers: { + authorization: getAuthorizationHeader() + } + } + ); + if (response.ok) { + return response.json(); + } + const errorText = await response.text(); + throw new Error(errorText); +} + +export async function getIngestSources( + ingestUuid: string +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/ingests/${ingestUuid}/sources?expand=true`, + process.env.LIVE_URL + ), + { + method: 'GET', + headers: { + authorization: getAuthorizationHeader() + } + } + ); + if (response.ok) { + return response.json(); + } + const errorText = await response.text(); + throw new Error(errorText); +} + +export async function getIngestStreams( + ingestUuid: string +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/ingests/${ingestUuid}/streams?expand=true`, + process.env.LIVE_URL + ), + { + method: 'GET', + headers: { + authorization: getAuthorizationHeader() + } + } + ); + if (response.ok) { + return response.json(); + } + throw await response.text(); +} diff --git a/src/api/ateliereLive/pipelines/multiviews/multiviews.ts b/src/api/ateliereLive/pipelines/multiviews/multiviews.ts index 5f24a869..738d6782 100644 --- a/src/api/ateliereLive/pipelines/multiviews/multiviews.ts +++ b/src/api/ateliereLive/pipelines/multiviews/multiviews.ts @@ -64,13 +64,12 @@ export async function createMultiviewForPipeline( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion productionSettings.pipelines[multiviewIndex].pipeline_id!; const sources = await getSourcesByIds( - sourceRefs.map((ref) => ref._id.toString()) + sourceRefs.map((ref) => (ref._id ? ref._id.toString() : '')) ); const sourceRefsWithLabels = sourceRefs.map((ref) => { + const refId = ref._id ? ref._id.toString() : ''; if (!ref.label) { - const source = sources.find( - (source) => source._id.toString() === ref._id.toString() - ); + const source = sources.find((source) => source._id.toString() === refId); ref.label = source?.name || ''; } return ref; @@ -120,6 +119,7 @@ export async function createMultiviewForPipeline( srt_mode: multiview.output.srt_mode, srt_latency_ms: multiview.output.srt_latency_ms, srt_passphrase: multiview.output.srt_passphrase, + srt_stream_id: multiview.output.srt_stream_id, video_format: multiview.output.video_format, video_kilobit_rate: multiview.output.video_kilobit_rate } @@ -143,8 +143,14 @@ export async function createMultiviewForPipeline( ); if (response.ok) { + Log().info( + `Created multiview for pipeline '${pipelineUUID}' from preset` + ); return await response.json(); } + Log().info( + `ERROR: Could not create multiview for pipeline '${pipelineUUID}' from preset` + ); throw await response.text(); } ); @@ -174,8 +180,14 @@ export async function deleteMultiviewFromPipeline( ); if (response.ok) { + Log().info( + `Deleted multiview ${multiviewId} for pipeline '${pipelineUUID}' from preset` + ); return; } + Log().info( + `ERROR: Could not delete multiview ${multiviewId} for pipeline '${pipelineUUID}' from preset` + ); throw await response.text(); } @@ -217,7 +229,13 @@ export async function updateMultiviewForPipeline( } ); if (response.ok) { + Log().info( + `Updated multiview ${multiviewId} for pipeline '${pipelineUUID}' from preset` + ); return await response.json(); } + Log().info( + `ERROR: Could not update multiview ${multiviewId} for pipeline '${pipelineUUID}' from preset` + ); throw await response.text(); } diff --git a/src/api/ateliereLive/pipelines/pipelines.ts b/src/api/ateliereLive/pipelines/pipelines.ts index 32fc3090..e3f96f85 100644 --- a/src/api/ateliereLive/pipelines/pipelines.ts +++ b/src/api/ateliereLive/pipelines/pipelines.ts @@ -3,6 +3,7 @@ import { ResourcesPipelineResponse } from '../../../../types/ateliere-live'; import { + PipelineOutput, PipelineOutputSettings, PipelineSettings } from '../../../interfaces/pipeline'; @@ -92,7 +93,9 @@ export async function getPipelines(): Promise< name: pipeline.name, uuid: pipeline.uuid, outputs: pipeline.outputs, - streams: pipeline.streams + streams: pipeline.streams, + feedback_streams: pipeline.feedback_streams, + control_receiver: pipeline.control_receiver }; }); } @@ -161,44 +164,28 @@ export async function removePipelineStreams(pipeId: string) { } export async function createPipelineOutputs(pipeline: PipelineSettings) { - const outputs = await getPipelineOutputs(pipeline.pipeline_id!).catch( + const outputs = pipeline.outputs; + if (!outputs) return; + const startOutputStreamsPromises = outputs.map((o) => { + return startPipelineStream( + pipeline.pipeline_id!, + o.uuid, + buildOutputStreamSettings(o) + ); + }); + const startedOutputs = await Promise.all(startOutputStreamsPromises).catch( (error) => { Log().error( - `Failed to get outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}'`, + `Failed to create outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}'`, error ); - throw `Failed to get outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}': ${error}`; + throw `Failed to create outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}': ${error.message}`; } ); - - Log().info( - `Creating outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}'` - ); - - let outputUuid = outputs[0].uuid!; - if (outputs.length > 0) { - const outputToUse = outputs.find( - (output) => output.name.toLowerCase().indexOf('program') !== -1 - ); - if (outputToUse) { - outputUuid = outputToUse.uuid!; - } - } - - await startPipelineStream( - pipeline.pipeline_id!, - outputUuid, - buildPipelineSettingsBody(pipeline) - ).catch((error) => { - Log().error( - `Failed to create outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}' at output '${outputUuid}'`, - error - ); - throw `Failed to create outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}' at output '${outputUuid}': ${error.message}`; - }); Log().info( `Outputs for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}' created` ); + return startedOutputs; } export async function connectControlPanelToPipeline( @@ -284,30 +271,28 @@ export async function connectControlPanelToPipeline( await Promise.all(connectSendersToReceivers); } -function buildPipelineSettingsBody( - pipeline: PipelineSettings +function buildOutputStreamSettings( + output: PipelineOutput ): PipelineOutputSettings[] { - const outputSettings = pipeline.program_output.map((output) => { + const outputSettings = output.streams.map((s) => { return { - audio_format: output.audio_format, - audio_kilobit_rate: output.audio_kilobit_rate, - format: output.format, - local_ip: output.local_ip, - local_port: output.srt_mode === 'caller' ? 0 : output.port, - remote_ip: output.remote_ip, // only used in caller mode - remote_port: output.port, - srt_mode: output.srt_mode, // 'listener' or 'caller' - srt_latency_ms: output.srt_latency_ms, // 120 - srt_passphrase: output.srt_passphrase, // '' - video_bit_depth: output.video_bit_depth, - video_format: output.video_format, - video_gop_length: output.video_gop_length, - video_kilobit_rate: output.video_kilobit_rate + video_format: output.settings.video_format, + video_bit_depth: output.settings.video_bit_depth, + video_kilobit_rate: output.settings.video_kilobit_rate, + audio_format: s.audio_format, + audio_kilobit_rate: s.audio_kilobit_rate, + format: s.format, + local_ip: s.srt_mode === 'caller' ? '0.0.0.0' : s.local_ip, + local_port: s.srt_mode === 'caller' ? 0 : s.local_port, + remote_ip: s.remote_ip, // only used in caller mode + remote_port: s.remote_port, + srt_mode: s.srt_mode, // 'listener' or 'caller' + srt_latency_ms: s.srt_latency_ms, // 120 + srt_passphrase: s.srt_passphrase, // '' + video_gop_length: s.video_gop_length, + srt_stream_id: s.srt_stream_id }; }); - Log().info( - `Output settings for pipeline '${pipeline.pipeline_name}/${pipeline.pipeline_id}'`, - outputSettings - ); + Log().info(`Creating streams for output ${output.uuid}.`); return outputSettings; } diff --git a/src/api/ateliereLive/pipelines/renderingengine/renderingengine.ts b/src/api/ateliereLive/pipelines/renderingengine/renderingengine.ts new file mode 100644 index 00000000..f3a7352b --- /dev/null +++ b/src/api/ateliereLive/pipelines/renderingengine/renderingengine.ts @@ -0,0 +1,688 @@ +import { + ResourcesHTMLBrowser, + ResourcesMediaPlayer, + ResourcesRenderingEngineResponse +} from '../../../../../types/ateliere-live'; +import { LIVE_BASE_API_PATH } from '../../../../constants'; +import { MultiviewSettings } from '../../../../interfaces/multiview'; +import { Production } from '../../../../interfaces/production'; +import { + HTMLSource, + MediaSource +} from '../../../../interfaces/renderingEngine'; +import { SourceReference } from '../../../../interfaces/Source'; +import { Log } from '../../../logger'; +import { getAuthorizationHeader } from '../../utils/authheader'; +import { + getMultiviewsForPipeline, + updateMultiviewForPipeline +} from '../multiviews/multiviews'; + +export async function getPipelineHtmlSources( + pipelineUuid: string +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/pipelines/${pipelineUuid}/renderingengine/html`, + process.env.LIVE_URL + ), + { + method: 'GET', + headers: { + authorization: getAuthorizationHeader() + }, + next: { + revalidate: 0 + } + } + ); + if (response.ok) { + const text = await response.text(); + return text ? JSON.parse(text) : {}; + } + throw await response.json(); +} + +export async function createPipelineHtmlSource( + production: Production, + inputSlot: number, + data: HTMLSource, + source: SourceReference +) { + const payload = { + height: Number(data.height), + input_slot: Number(inputSlot), + url: data.url || '', + width: Number(data.width) + }; + + try { + const { production_settings } = production; + + for (let i = 0; i < production_settings.pipelines.length; i++) { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + + `/pipelines/${production_settings.pipelines[i].pipeline_id}/renderingengine/html`, + process.env.LIVE_URL + ), + { + method: 'POST', + headers: { + authorization: getAuthorizationHeader() + }, + body: JSON.stringify(payload) + } + ); + const text = await response.text(); + + if (response.status === 201) { + if (text.trim().length > 0) { + Log().warn('Unexpected content for 201 response:', text); + } + } else if ( + response.status === 400 || + response.status === 404 || + response.status === 500 + ) { + try { + const errorResponse = JSON.parse(text); + Log().error('API error response:', errorResponse); + throw new Error( + `API error ${response.status}: ${errorResponse.message}` + ); + } catch (parseError) { + Log().error('Failed to parse error response as JSON:', text); + throw new Error(`API error ${response.status}: ${text}`); + } + } else { + Log().error(`Unexpected status code ${response.status} received`); + Log().error('Response body:', text); + throw new Error(`Unexpected response status: ${response.status}`); + } + } + } catch (e) { + Log().error('Could not add html'); + Log().error(e); + if (typeof e !== 'string') { + return { + ok: false, + value: { + success: false, + steps: [ + { + step: 'add_html', + success: false + } + ] + }, + error: 'Could not add html' + }; + } + return { + ok: false, + value: { + success: false, + steps: [ + { + step: 'add_html', + success: false, + message: e + } + ] + }, + error: e + }; + } + + try { + if (!production.production_settings.pipelines[0].pipeline_id) { + Log().error( + `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}` + ); + throw `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}`; + } + const multiviewsResponse = await getMultiviewsForPipeline( + production.production_settings.pipelines[0].pipeline_id + ); + + const multiviews = multiviewsResponse.filter((multiview) => { + const pipeline = production.production_settings.pipelines[0]; + const multiviewArray = pipeline.multiviews; + + if (Array.isArray(multiviewArray)) { + return multiviewArray.some( + (item) => item.multiview_id === multiview.id + ); + } else if (multiviewArray) { + return ( + (multiviewArray as MultiviewSettings).multiview_id === multiview.id + ); + } + + return false; + }); + + if (multiviews.length === 0 || !multiviews) { + Log().error( + `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}` + ); + throw `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}`; + } + + await Promise.all( + multiviews.map(async (multiview) => { + const views = multiview.layout.views; + const viewsForSource = views.filter( + (view) => view.input_slot === inputSlot + ); + if (!viewsForSource || viewsForSource.length === 0) { + Log().info( + `No view found for input slot: ${inputSlot}. Will not connect source to view` + ); + return { + ok: true, + value: { + success: true, + steps: [ + { + step: 'add_html', + success: true + }, + { + step: 'update_multiview', + success: true + } + ] + } + }; + } + const updatedViewsForSource = viewsForSource.map((v) => { + return { ...v, label: source.label }; + }); + + const updatedViews = [ + ...views.filter((view) => view.input_slot !== inputSlot), + ...updatedViewsForSource + ]; + + await updateMultiviewForPipeline( + production.production_settings.pipelines[0].pipeline_id!, + multiview.id, + updatedViews + ); + }) + ); + } catch (e) { + Log().error('Could not update multiview'); + Log().error(e); + if (typeof e !== 'string') { + return { + ok: false, + value: { + success: false, + steps: [ + { + step: 'add_html', + success: true + }, + { + step: 'update_multiview', + success: false + } + ] + }, + error: 'Could not update multiview' + }; + } + return { + ok: false, + value: { + success: false, + steps: [ + { + step: 'add_html', + success: true + }, + { + step: 'update_multiview', + success: false, + message: e + } + ] + }, + error: e + }; + } + + return { + ok: true, + value: { + success: true, + steps: [ + { + step: 'add_html', + success: true + }, + { + step: 'update_multiview', + success: true + } + ] + } + }; +} + +export async function deleteHtmlFromPipeline( + pipelineUuid: string, + inputSlot: number +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + + `/pipelines/${pipelineUuid}/renderingengine/html/${inputSlot}`, + process.env.LIVE_URL + ), + { + method: 'DELETE', + headers: { + authorization: getAuthorizationHeader() + } + } + ); + if (response.ok) { + const text = await response.text(); + + if (text) { + return JSON.parse(text); + } + return; + } + throw await response.json(); +} + +export async function getPipelineMediaSources( + pipelineUuid: string +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/pipelines/${pipelineUuid}/renderingengine/media`, + process.env.LIVE_URL + ), + { + method: 'GET', + headers: { + authorization: getAuthorizationHeader() + }, + next: { + revalidate: 0 + } + } + ); + if (response.ok) { + return await response.json(); + } + throw await response.json(); +} + +export async function createPipelineMediaSource( + production: Production, + inputSlot: number, + data: MediaSource, + source: SourceReference +) { + const payload = { + filename: data.filename, + input_slot: Number(inputSlot) + }; + + try { + const { production_settings } = production; + + for (let i = 0; i < production_settings.pipelines.length; i++) { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + + `/pipelines/${production_settings.pipelines[i].pipeline_id}/renderingengine/media`, + process.env.LIVE_URL + ), + { + method: 'POST', + headers: { + authorization: getAuthorizationHeader() + }, + body: JSON.stringify(payload) + } + ); + const text = await response.text(); + + if (response.status === 201) { + if (text.trim().length > 0) { + Log().warn('Unexpected content for 201 response:', text); + } + } else if ( + response.status === 400 || + response.status === 404 || + response.status === 500 + ) { + try { + const errorResponse = JSON.parse(text); + Log().error('API error response:', errorResponse); + throw new Error( + `API error ${response.status}: ${errorResponse.message}` + ); + } catch (parseError) { + Log().error('Failed to parse error response as JSON:', text); + throw new Error(`API error ${response.status}: ${text}`); + } + } else { + Log().error(`Unexpected status code ${response.status} received`); + Log().error('Response body:', text); + throw new Error(`Unexpected response status: ${response.status}`); + } + } + } catch (e) { + Log().error('Could not add media'); + Log().error(e); + if (typeof e !== 'string') { + return { + ok: false, + value: { + success: false, + steps: [ + { + step: 'add_media', + success: false + } + ] + }, + error: 'Could not add media' + }; + } + return { + ok: false, + value: { + success: false, + steps: [ + { + step: 'add_media', + success: false, + message: e + } + ] + }, + error: e + }; + } + + try { + if (!production.production_settings.pipelines[0].pipeline_id) { + Log().error( + `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}` + ); + throw `Missing pipeline_id for: ${production.production_settings.pipelines[0].pipeline_name}`; + } + const multiviewsResponse = await getMultiviewsForPipeline( + production.production_settings.pipelines[0].pipeline_id + ); + + const multiviews = multiviewsResponse.filter((multiview) => { + const pipeline = production.production_settings.pipelines[0]; + const multiviewArray = pipeline.multiviews; + + if (Array.isArray(multiviewArray)) { + return multiviewArray.some( + (item) => item.multiview_id === multiview.id + ); + } else if (multiviewArray) { + return ( + (multiviewArray as MultiviewSettings).multiview_id === multiview.id + ); + } + + return false; + }); + + if (multiviews.length === 0 || !multiviews) { + Log().error( + `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}` + ); + throw `No multiview found for pipeline: ${production.production_settings.pipelines[0].pipeline_id}`; + } + + await Promise.all( + multiviews.map(async (multiview) => { + const views = multiview.layout.views; + const viewsForSource = views.filter( + (view) => view.input_slot === inputSlot + ); + if (!viewsForSource || viewsForSource.length === 0) { + Log().info( + `No view found for input slot: ${inputSlot}. Will not connect source to view` + ); + return { + ok: true, + value: { + success: true, + steps: [ + { + step: 'add_media', + success: true + }, + { + step: 'update_multiview', + success: true + } + ] + } + }; + } + const updatedViewsForSource = viewsForSource.map((v) => { + return { ...v, label: source.label }; + }); + + const updatedViews = [ + ...views.filter((view) => view.input_slot !== inputSlot), + ...updatedViewsForSource + ]; + + await updateMultiviewForPipeline( + production.production_settings.pipelines[0].pipeline_id!, + multiview.id, + updatedViews + ); + }) + ); + } catch (e) { + Log().error('Could not update multiview'); + Log().error(e); + if (typeof e !== 'string') { + return { + ok: false, + value: { + success: false, + steps: [ + { + step: 'add_media', + success: true + }, + { + step: 'update_multiview', + success: false + } + ] + }, + error: 'Could not update multiview' + }; + } + return { + ok: false, + value: { + success: false, + steps: [ + { + step: 'add_media', + success: true + }, + { + step: 'update_multiview', + success: false, + message: e + } + ] + }, + error: e + }; + } +} + +export async function deleteMediaFromPipeline( + pipelineUuid: string, + inputSlot: number +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + + `/pipelines/${pipelineUuid}/renderingengine/media/${inputSlot}`, + process.env.LIVE_URL + ), + { + method: 'DELETE', + headers: { + authorization: getAuthorizationHeader() + } + } + ); + if (response.ok) { + const text = await response.text(); + + if (text) { + return JSON.parse(text); + } + return; + } + throw await response.json(); +} + +export async function getPipelineRenderingEngine( + pipelineUuid: string +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/pipelines/${pipelineUuid}/renderingengine`, + process.env.LIVE_URL + ), + { + method: 'GET', + headers: { + authorization: getAuthorizationHeader() + }, + next: { + revalidate: 0 + } + } + ); + + if (response.ok) { + try { + return await response.json(); + } catch (error) { + throw new Error('Parsing error in successful response.'); + } + } + + const contentType = response.headers.get('content-type'); + const responseText = await response.text(); + + if (contentType && contentType.includes('application/json')) { + try { + throw JSON.parse(responseText); + } catch (error) { + throw new Error(`Failed to parse JSON error response: ${responseText}`); + } + } else { + throw new Error(`Unexpected non-JSON response: ${responseText}`); + } +} + +export async function getPipelineRenderingEngineHtml( + pipelineUuid: string +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/pipelines/${pipelineUuid}/renderingengine/html`, + process.env.LIVE_URL + ), + { + method: 'GET', + headers: { + authorization: getAuthorizationHeader() + }, + next: { + revalidate: 0 + } + } + ); + + if (response.ok) { + try { + return await response.json(); + } catch (error) { + throw new Error('Parsing error in successful response.'); + } + } + + const contentType = response.headers.get('content-type'); + const responseText = await response.text(); + + if (contentType && contentType.includes('application/json')) { + try { + throw JSON.parse(responseText); + } catch (error) { + throw new Error(`Failed to parse JSON error response: ${responseText}`); + } + } else { + throw new Error(`Unexpected non-JSON response: ${responseText}`); + } +} + +export async function getPipelineRenderingEngineMedia( + pipelineUuid: string +): Promise { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/pipelines/${pipelineUuid}/renderingengine/media`, + process.env.LIVE_URL + ), + { + method: 'GET', + headers: { + authorization: getAuthorizationHeader() + }, + next: { + revalidate: 0 + } + } + ); + + if (response.ok) { + try { + return await response.json(); + } catch (error) { + throw new Error('Parsing error in successful response.'); + } + } + + const contentType = response.headers.get('content-type'); + const responseText = await response.text(); + + if (contentType && contentType.includes('application/json')) { + try { + throw JSON.parse(responseText); + } catch (error) { + throw new Error(`Failed to parse JSON error response: ${responseText}`); + } + } else { + throw new Error(`Unexpected non-JSON response: ${responseText}`); + } +} diff --git a/src/api/ateliereLive/pipelines/streams/streams.ts b/src/api/ateliereLive/pipelines/streams/streams.ts index fc5e108f..879c1760 100644 --- a/src/api/ateliereLive/pipelines/streams/streams.ts +++ b/src/api/ateliereLive/pipelines/streams/streams.ts @@ -66,6 +66,7 @@ export async function createStream( return pipeline.uuid; }) ); + const ingestUuid = await getUuidFromIngestName( source.ingest_name, false @@ -75,10 +76,11 @@ export async function createStream( }); const sourceId = await getSourceIdFromSourceName( - ingestUuid, + ingestUuid || '', source.ingest_source_name, false ); + const audioMapping = source.audio_stream.audio_mapping && source.audio_stream.audio_mapping.length > 0 @@ -86,6 +88,7 @@ export async function createStream( : [[0, 1]]; await initDedicatedPorts(); + for (const pipeline of production_settings.pipelines) { const availablePorts = getAvailablePortsForIngest( source.ingest_name, @@ -98,31 +101,43 @@ export async function createStream( } const availablePort = availablePorts.values().next().value; + if (!availablePort) + throw `Allocated port ${availablePort} on '${source.ingest_name}' for ${source.ingest_source_name} cannot be undefined`; Log().info( `Allocated port ${availablePort} on '${source.ingest_name}' for ${source.ingest_source_name}` ); + + const pipelineSource = pipeline.sources?.find( + (s) => + s.ingest_source_name === source.ingest_source_name && + s.ingest_name === source.ingest_name + ); + const stream: PipelineStreamSettings = { + ingest_id: ingestUuid || '', + source_id: sourceId || 0, pipeline_id: pipeline.pipeline_id!, - alignment_ms: pipeline.alignment_ms, - audio_format: pipeline.audio_format, - audio_sampling_frequency: pipeline.audio_sampling_frequency, - bit_depth: pipeline.bit_depth, - convert_color_range: pipeline.convert_color_range, - encoder: pipeline.encoder, - encoder_device: pipeline.encoder_device, - format: pipeline.format, + input_slot: input_slot, + alignment_ms: + pipelineSource?.settings.alignment_ms || pipeline.alignment_ms, + max_network_latency_ms: + pipelineSource?.settings.max_network_latency_ms || + pipeline.max_network_latency_ms, + width: pipeline.width, + height: pipeline.height, frame_rate_d: pipeline.frame_rate_d, frame_rate_n: pipeline.frame_rate_n, + format: pipeline.format, + encoder: pipeline.encoder, + encoder_device: pipeline.encoder_device, gop_length: pipeline.gop_length, - height: pipeline.height, - max_network_latency_ms: pipeline.max_network_latency_ms, pic_mode: pipeline.pic_mode, - speed_quality_balance: pipeline.speed_quality_balance, video_kilobit_rate: pipeline.video_kilobit_rate, - width: pipeline.width, - ingest_id: ingestUuid, - source_id: sourceId, - input_slot, + bit_depth: pipeline.bit_depth, + speed_quality_balance: pipeline.speed_quality_balance, + convert_color_range: pipeline.convert_color_range, + audio_sampling_frequency: pipeline.audio_sampling_frequency, + audio_format: pipeline.audio_format, audio_mapping: JSON.stringify(audioMapping), interfaces: [ { @@ -131,6 +146,7 @@ export async function createStream( } ] }; + try { Log().info( `Connecting '${source.ingest_name}/${ingestUuid}}:${source.ingest_source_name}' to '${pipeline.pipeline_name}/${pipeline.pipeline_id}'` @@ -147,6 +163,7 @@ export async function createStream( Log().info( `Stream '${result.stream_uuid}' from '${source.ingest_name}/${ingestUuid}' to '${pipeline.pipeline_name}/${pipeline.pipeline_id}' connected` ); + sourceToPipelineStreams.push({ source_id: source._id.toString(), stream_uuid: result.stream_uuid, @@ -254,7 +271,7 @@ export async function createStream( }; } const updatedViewsForSource = viewsForSource.map((v) => { - return { ...v, label: source.name }; + return { ...v, label: v.label || source.name }; }); const updatedViews = [ @@ -349,3 +366,26 @@ export async function deleteStream(streamUuid: string) { } throw await response.json(); } + +export async function updateStream(streamUuid: string, alignment_ms: number) { + const response = await fetch( + new URL( + LIVE_BASE_API_PATH + `/streams/${streamUuid}`, + process.env.LIVE_URL + ), + { + method: 'PATCH', + headers: { + authorization: getAuthorizationHeader() + }, + body: JSON.stringify({ alignment_ms: alignment_ms }), + next: { + revalidate: 0 + } + } + ); + if (response.ok) { + return true; + } + throw await response.json(); +} diff --git a/src/api/manager/auth.ts b/src/api/manager/auth.ts index a1f26394..8d8c72c5 100644 --- a/src/api/manager/auth.ts +++ b/src/api/manager/auth.ts @@ -37,7 +37,15 @@ export const authOptions: NextAuthOptions = { .catch(() => null); } }) - ] + ], + callbacks: { + session: async ({ session, token }) => { + if (session?.user) { + session.user.name = token.sub; + } + return session; + } + } }; export async function isAuthenticated() { diff --git a/src/api/manager/inventory.ts b/src/api/manager/inventory.ts index 7d47e1ea..a60daf33 100644 --- a/src/api/manager/inventory.ts +++ b/src/api/manager/inventory.ts @@ -39,3 +39,11 @@ export async function purgeInventorySourceItem( return result as UpdateResult; } + +// Used for SRT sources which unlike other sources can de deleted from the API +export async function removeInventorySource(id: string) { + const db = await getDatabase(); + const objectId = new ObjectId(id); + + return await db.collection('inventory').deleteOne({ _id: objectId }); +} diff --git a/src/api/manager/job/syncInventory.ts b/src/api/manager/job/syncInventory.ts index 2e813d0a..a3138573 100644 --- a/src/api/manager/job/syncInventory.ts +++ b/src/api/manager/job/syncInventory.ts @@ -19,31 +19,31 @@ async function getSourcesFromAPI(): Promise { .map((result) => result.value); const sources: SourceWithoutLastConnected[] = resolvedIngests.flatMap( (ingest) => { - return ingest.sources.map( - (source) => - ({ - status: source.active ? 'new' : 'gone', - name: source.name, - type: 'camera', - tags: { - location: 'Unknown' - }, - ingest_name: ingest.name, - ingest_source_name: source.name, - ingest_type: source.type, - video_stream: { - width: source?.video_stream?.width, - height: source?.video_stream?.height, - frame_rate: - source?.video_stream?.frame_rate_n / - source?.video_stream?.frame_rate_d - }, - audio_stream: { - number_of_channels: source?.audio_stream?.number_of_channels, - sample_rate: source?.audio_stream?.sample_rate - } - } satisfies SourceWithoutLastConnected) - ); + return ingest.sources.map((source) => { + return { + status: source.active ? 'new' : 'gone', + name: source.name, + type: 'camera', + tags: { + location: 'Unknown' + }, + ingest_name: ingest.name, + ingest_source_name: source.name, + ingest_type: source.type, + video_stream: { + width: source?.video_stream?.width, + height: source?.video_stream?.height, + frame_rate: + source?.video_stream?.frame_rate_n / + source?.video_stream?.frame_rate_d + }, + audio_stream: { + number_of_channels: source?.audio_stream?.number_of_channels, + sample_rate: source?.audio_stream?.sample_rate + }, + srt: source.srt + } satisfies SourceWithoutLastConnected; + }); } ); return sources; @@ -52,8 +52,9 @@ async function getSourcesFromAPI(): Promise { /** * Syncs the inventory with the ingests in Ateliere Live. * - Adds new sources to the inventory with the status 'new' - * - Updates the status of sources depending on wheter or not they are still present in the ingests + * - Updates the status of sources depending on whether or not they are still present in the ingests */ + export async function runSyncInventory() { const db = await getDatabase(); const apiSources = await getSourcesFromAPI(); @@ -80,6 +81,34 @@ export async function runSyncInventory() { } }; + const updateSrtMetadata = ( + inventorySource: WithId, + apiSource: SourceWithoutLastConnected + ) => { + if ( + apiSource.status === 'new' && + apiSource.ingest_type === 'SRT' && + apiSource.srt && + apiSource.srt.video_format && + inventorySource.srt + ) { + const updatedSrt = { + ...inventorySource.srt, + video_format: apiSource.srt.video_format + }; + + return updatedSrt; + } else if ( + apiSource.ingest_type === 'SRT' && + !inventorySource.srt && + apiSource.srt + ) { + return apiSource.srt; + } else { + return inventorySource.srt; + } + }; + // Update status of all sources in the inventory to the status found in API. // If a source is not found in the API, it is marked as gone. const dbInventoryWithCorrectStatus = dbInventory.map((inventorySource) => { @@ -90,8 +119,11 @@ export async function runSyncInventory() { ); }); if (!apiSource) { - // If source was not found in response from API, always mark it as gone - return { ...inventorySource, status: 'gone' } satisfies WithId; + // If source was not found in response from API, check if status is purge otherwise mark it as gone + return { + ...inventorySource, + status: inventorySource.status === 'purge' ? 'purge' : 'gone' + } satisfies WithId; } const lastConnected = apiSource.status !== 'gone' ? new Date() : inventorySource.lastConnected; @@ -100,7 +132,22 @@ export async function runSyncInventory() { return { ...inventorySource, status: statusUpdateCheck(inventorySource, apiSource, lastConnected), - lastConnected: lastConnected + lastConnected: lastConnected, + video_stream: + apiSource.ingest_type === 'SRT' && apiSource.status === 'gone' + ? inventorySource.video_stream + : apiSource.video_stream, + audio_stream: + apiSource.ingest_type === 'SRT' && apiSource.status === 'gone' + ? inventorySource.audio_stream + : { + number_of_channels: apiSource.audio_stream.number_of_channels, + sample_rate: apiSource.audio_stream.sample_rate, + audio_mapping: + inventorySource.audio_stream.audio_mapping || undefined + }, + // Add srt metadata if missing from SRT sources + srt: updateSrtMetadata(inventorySource, apiSource) } satisfies WithId; }); diff --git a/src/api/manager/multiview-presets.ts b/src/api/manager/multiview-presets.ts new file mode 100644 index 00000000..ef8fb510 --- /dev/null +++ b/src/api/manager/multiview-presets.ts @@ -0,0 +1,20 @@ +import { ObjectId, WithId } from 'mongodb'; +import { MultiviewPreset } from '../../interfaces/preset'; +import { getDatabase } from '../mongoClient/dbClient'; + +export async function getMultiviewPresets(): Promise { + const db = await getDatabase(); + return await db + .collection('multiview-presets') + .find({}) + .toArray(); +} + +export async function getMultiviewPreset( + id: string +): Promise> { + const db = await getDatabase(); + return (await db + .collection('multiview-presets') + .findOne({ _id: new ObjectId(id) })) as WithId; +} diff --git a/src/api/manager/multiviews.ts b/src/api/manager/multiviews.ts new file mode 100644 index 00000000..7eab674e --- /dev/null +++ b/src/api/manager/multiviews.ts @@ -0,0 +1,57 @@ +import { ObjectId, WithId } from 'mongodb'; +import { TMultiviewLayout } from '../../interfaces/preset'; +import { getDatabase } from '../mongoClient/dbClient'; +import { Log } from '../logger'; + +export async function getMultiviewLayouts(): Promise { + const db = await getDatabase(); + return await db.collection('multiviews').find({}).toArray(); +} + +export async function getMultiviewLayout( + id: string +): Promise> { + const db = await getDatabase(); + return (await db + .collection('multiviews') + .findOne({ _id: new ObjectId(id) })) as WithId; +} + +export async function putMultiviewLayout( + newMultiviewLayout: TMultiviewLayout +): Promise { + const db = await getDatabase(); + const collection = db.collection('multiviews'); + const editLayout = await collection.findOne({ + _id: new ObjectId(newMultiviewLayout._id), + name: newMultiviewLayout.name + }); + + const newMultiviewLayoutWithoutID = { ...newMultiviewLayout }; + delete newMultiviewLayoutWithoutID._id; + + if (editLayout) { + await collection.updateOne( + { name: newMultiviewLayout.name }, + { $set: { ...newMultiviewLayoutWithoutID } } + ); + } else { + await collection.insertOne({ ...newMultiviewLayout, _id: new ObjectId() }); + } +} + +export async function deleteLayout(id: string): Promise { + const db = await getDatabase(); + + await db.collection('multiviews').deleteOne({ + _id: { $eq: new ObjectId(id) } + }); + Log().info('Deleted multiview layout', id); +} + +export async function deleteLayouts(id: string): Promise { + const db = await getDatabase(); + + await db.collection('multiviews').deleteMany({ productionId: id }); + Log().info('Deleted layouts for production', id); +} diff --git a/src/api/manager/presets.ts b/src/api/manager/presets.ts index 57fd66d1..cf897402 100644 --- a/src/api/manager/presets.ts +++ b/src/api/manager/presets.ts @@ -1,5 +1,5 @@ -import { ObjectId, WithId } from 'mongodb'; -import { MultiviewPreset, PresetWithId } from '../../interfaces/preset'; +import { ObjectId } from 'mongodb'; +import { PresetWithId } from '../../interfaces/preset'; import { getDatabase } from '../mongoClient/dbClient'; export async function getPresets(): Promise { @@ -14,18 +14,6 @@ export async function getPresetByid(id: string): Promise { .findOne({ _id: new ObjectId(id) })) as PresetWithId; } -export async function getMultiviewPresets(): Promise { - const db = await getDatabase(); - return await db.collection('multiviews').find({}).toArray(); -} -export async function getMultiviewPreset( - id: string -): Promise> { - const db = await getDatabase(); - return (await db - .collection('multiviews') - .findOne({ _id: new ObjectId(id) })) as WithId; -} export async function putPreset( id: string, preset: PresetWithId diff --git a/src/api/manager/productions.ts b/src/api/manager/productions.ts index e68524ad..22713bb2 100644 --- a/src/api/manager/productions.ts +++ b/src/api/manager/productions.ts @@ -17,6 +17,7 @@ export async function getProduction(id: string): Promise { .collection('productions') .findOne({ _id: new ObjectId(id) })) as ProductionWithId; } + export async function setProductionsIsActiveFalse(): Promise< UpdateResult > { @@ -25,17 +26,40 @@ export async function setProductionsIsActiveFalse(): Promise< .collection('productions') .updateMany({}, { $set: { isActive: false } }); } + export async function putProduction( id: string, production: Production -): Promise { +): Promise { const db = await getDatabase(); + const newSourceId = new ObjectId().toString(); + + const sources = production.sources + ? production.sources.flatMap((singleSource) => { + return singleSource._id + ? singleSource + : { + _id: newSourceId, + type: singleSource.type, + label: singleSource.label, + input_slot: singleSource.input_slot, + html_data: + (singleSource.type === 'html' && singleSource.html_data) || + undefined, + media_data: + (singleSource.type === 'mediaplayer' && + singleSource.media_data) || + undefined + }; + }) + : []; + await db.collection('productions').findOneAndReplace( { _id: new ObjectId(id) }, { name: production.name, isActive: production.isActive, - sources: production.sources, + sources: sources, production_settings: production.production_settings } ); @@ -43,6 +67,14 @@ export async function putProduction( if (!production.isActive) { deleteMonitoring(db, id); } + + return { + _id: new ObjectId(id).toString(), + name: production.name, + isActive: production.isActive, + sources: sources, + production_settings: production.production_settings + }; } export async function postProduction(data: Production): Promise { @@ -68,3 +100,188 @@ export async function deleteProduction(id: string): Promise { function deleteMonitoring(db: Db, productionId: string) { db.collection('monitoring').deleteMany({ productionId: productionId }); } + +export async function getProductionPipelineSourceAlignment( + productionId: string, + pipelineId: string, + ingestSourceName: string, + ingestName: string +) { + const production = await getProduction(productionId); + + if (!production) { + console.error('Production not found'); + return null; + } + + const pipeline = production.production_settings.pipelines.find( + (p) => p.pipeline_id === pipelineId + ); + + const source = pipeline?.sources?.find( + (source) => + source.ingest_name === ingestName && + source.ingest_source_name === ingestSourceName + ); + + const alignment = + source?.settings?.alignment_ms !== undefined + ? source.settings.alignment_ms + : pipeline?.alignment_ms; + + return alignment; +} + +export async function setProductionPipelineSourceAlignment( + productionId: string, + pipelineId: string, + ingestName: string, + ingestSourceName: string, + alignment_ms: number +) { + const db = await getDatabase(); + + try { + const result = await db.collection('productions').updateOne( + { + _id: new ObjectId(productionId), + 'production_settings.pipelines.pipeline_id': pipelineId, + 'production_settings.pipelines.sources.ingest_name': ingestName, + 'production_settings.pipelines.sources.ingest_source_name': + ingestSourceName + }, + { + $set: { + 'production_settings.pipelines.$[p].sources.$[s].settings.alignment_ms': + alignment_ms + } + }, + { + arrayFilters: [ + { 'p.pipeline_id': pipelineId }, + { + 's.ingest_name': ingestName, + 's.ingest_source_name': ingestSourceName + } + ] + } + ); + + if (result.matchedCount === 0) { + console.error('No matching pipeline or source found to update'); + return null; + } + + return true; + } catch (error) { + console.error('Database error:', error); + throw new Error('Error updating pipeline source alignment'); + } +} + +export async function getProductionSourceLatency( + productionId: string, + pipelineId: string, + ingestSourceName: string, + ingestName: string +) { + const production = await getProduction(productionId); + + if (!production) { + console.error('Production not found'); + return null; + } + + const pipeline = production.production_settings.pipelines.find( + (p) => p.pipeline_id === pipelineId + ); + + const source = pipeline?.sources?.find( + (source) => + source.ingest_name === ingestName && + source.ingest_source_name === ingestSourceName + ); + + const latency = + source?.settings?.max_network_latency_ms !== undefined + ? source.settings.max_network_latency_ms + : pipeline?.max_network_latency_ms; + + return latency; +} +export async function setProductionPipelineSourceLatency( + productionId: string, + pipelineId: string, + ingestName: string, + ingestSourceName: string, + max_network_latency_ms: number +) { + const db = await getDatabase(); + + try { + const result = await db.collection('productions').updateOne( + { + _id: new ObjectId(productionId), + 'production_settings.pipelines.pipeline_id': pipelineId, + 'production_settings.pipelines.sources.ingest_name': ingestName, + 'production_settings.pipelines.sources.ingest_source_name': + ingestSourceName + }, + { + $set: { + 'production_settings.pipelines.$[p].sources.$[s].settings.max_network_latency_ms': + max_network_latency_ms + } + }, + { + arrayFilters: [ + { 'p.pipeline_id': pipelineId }, + { + 's.ingest_name': ingestName, + 's.ingest_source_name': ingestSourceName + } + ] + } + ); + + if (result.matchedCount === 0) { + console.error('No matching pipeline or source found to update'); + return null; + } + + return true; + } catch (error) { + console.error('Database error:', error); + throw new Error('Error updating pipeline source latency'); + } +} + +export async function replaceProductionSourceStreamIds( + productionId: string, + sourceId: string | ObjectId, + newStreamUuids: string[] +) { + const db = await getDatabase(); + const productionObjectId = new ObjectId(productionId); + + const sourceIdForQuery = + typeof sourceId === 'string' ? sourceId : sourceId.toString(); + + const updateResult = await db.collection('productions').updateOne( + { + _id: productionObjectId, + 'sources._id': sourceIdForQuery + }, + { + $set: { + 'sources.$.stream_uuids': newStreamUuids + } + } + ); + + if (updateResult.matchedCount === 0) { + throw new Error('Production or source not found'); + } + + return updateResult; +} diff --git a/src/api/manager/sources.ts b/src/api/manager/sources.ts index 8bb83e80..4e77f393 100644 --- a/src/api/manager/sources.ts +++ b/src/api/manager/sources.ts @@ -1,6 +1,6 @@ import inventory from './mocks/inventory.json'; import { Source } from '../../interfaces/Source'; -import { ObjectId } from 'mongodb'; +import { ObjectId, OptionalId, WithId } from 'mongodb'; import { getDatabase } from '../mongoClient/dbClient'; export function getMockedSources() { @@ -9,37 +9,45 @@ export function getMockedSources() { export async function postSource(data: Source): Promise { const db = await getDatabase(); - return (await db.collection('inventory').insertOne(data)) - .insertedId as ObjectId; + const insertData: OptionalId> & { _id?: ObjectId } = { + ...data, + _id: typeof data._id === 'string' ? new ObjectId(data._id) : data._id + }; + const result = await db.collection('inventory').insertOne(insertData); + return result.insertedId as ObjectId; } export async function getSources() { const db = await getDatabase(); return await db.collection('inventory').find().toArray(); } - -export async function getSourcesByIds(_ids: string[]) { +export async function getSourcesByIds( + _ids: string[] +): Promise[]> { const db = await getDatabase().catch(() => { - throw "Can't connect to Database"; - }); - const objectIds = _ids.map((id: string) => { - return new ObjectId(id); + throw new Error("Can't connect to Database"); }); + const objectIds = _ids.map((id: string) => new ObjectId(id)); - return ( - await db - .collection('inventory') - .find({ - _id: { - $in: objectIds - } - }) - .toArray() - ).sort( - (a, b) => - _ids.findIndex((id) => a._id.equals(id)) - - _ids.findIndex((id) => b._id.equals(id)) - ); + const sources = await db + .collection('inventory') + .find({ + _id: { + $in: objectIds + } + }) + .toArray(); + + return sources.sort((a, b) => { + const findIndex = (id: ObjectId | string) => + _ids.findIndex((originalId) => + id instanceof ObjectId + ? id.equals(new ObjectId(originalId)) + : id === originalId + ); + + return findIndex(a._id) - findIndex(b._id); + }); } export async function updateSource(source: any) { diff --git a/src/api/manager/teardown.ts b/src/api/manager/teardown.ts new file mode 100644 index 00000000..c4ab2d86 --- /dev/null +++ b/src/api/manager/teardown.ts @@ -0,0 +1,307 @@ +import { + ResourcesCompactIngestResponse, + ResourcesCompactPipelineResponse, + ResourcesConnectionUUIDResponse +} from '../../../types/ateliere-live'; +import { FlowStep, TeardownStepNames } from '../../interfaces/production'; +import { Result } from '../../interfaces/result'; +import { disconnectReceiver } from '../ateliereLive/controlconnections'; +import { + deleteSrtSource, + getIngests, + getIngestSources +} from '../ateliereLive/ingest'; +import { + deleteMultiviewFromPipeline, + getMultiviewsForPipeline +} from '../ateliereLive/pipelines/multiviews/multiviews'; +import { + getPipelineOutputs, + stopAllOutputStreamsByUuid +} from '../ateliereLive/pipelines/outputs/outputs'; +import { + getPipelines, + removePipelineStreams, + resetPipeline +} from '../ateliereLive/pipelines/pipelines'; +import { deleteStreamByUuid } from '../ateliereLive/streams'; + +export interface TeardownOptions { + pipelines?: ResourcesCompactPipelineResponse[]; + resetPipelines?: boolean; + deleteIngestSRTSources?: boolean; +} + +export async function teardown( + options: TeardownOptions +): Promise> { + const { + pipelines: pipes, + resetPipelines = true, + deleteIngestSRTSources = true + } = options; + + const steps: FlowStep[] = []; + + const addStep = (name: TeardownStepNames, success: boolean, error?: any) => { + steps.push({ + step: name, + success, + message: typeof error === 'string' ? error : '' + }); + }; + + const generateResponse = (ok: boolean): Result => { + if (ok) return { ok, value: steps }; + return { + ok, + value: steps, + error: steps[steps.length - 1]?.message || 'Unknown error occured' + }; + }; + + // Pipelines + // Step 1 + // Fetch pipelines + let pipelines: ResourcesCompactPipelineResponse[]; + + if (pipes) { + pipelines = pipes; + } else { + try { + pipelines = await getPipelines().catch(() => { + throw 'Failed to fetch pipelines'; + }); + } catch (e) { + addStep('pipeline_output_streams', false, 'Failed to fetch pipelines'); + return generateResponse(false); + } + } + + // Step 2 + // Fetch pipeline outputs + // Delete pipeline output streams + try { + for (const pipeline of pipelines) { + const outputs = await getPipelineOutputs(pipeline.uuid).catch(() => { + throw `Failed to fetch outputs for pipeline ${pipeline.name}`; + }); + for (const output of outputs) { + if (output.active_streams?.length) { + await stopAllOutputStreamsByUuid(pipeline.uuid, output.uuid).catch( + (e) => { + console.log(e); + throw `Failed to delete streams for output ${output.name} in pipeline ${pipeline.name}`; + } + ); + } + } + } + addStep('pipeline_output_streams', true); + } catch (e) { + addStep('pipeline_output_streams', false, e); + return generateResponse(false); + } + + // Step 3 + // Fetch pipeline multiviewers + // Delete pipeline multiviewers + try { + for (const pipeline of pipelines) { + const multiviewers = await getMultiviewsForPipeline(pipeline.uuid).catch( + () => { + throw `Failed to fetch multiviewers for pipeline ${pipeline.name}`; + } + ); + for (const multiviewer of multiviewers) { + await deleteMultiviewFromPipeline(pipeline.uuid, multiviewer.id).catch( + () => { + throw `Failed to delete multiviewer ${multiviewer.id} in pipeline ${pipeline.name}`; + } + ); + } + } + addStep('pipeline_multiviewers', true); + } catch (e) { + addStep('pipeline_multiviewers', false, e); + return generateResponse(false); + } + + // Step 4 + // Fetch pipeline streams + // Delete pipeline streams + try { + for (const pipeline of pipelines) { + await removePipelineStreams(pipeline.uuid).catch(() => { + throw `Failed to delete streams for pipeline ${pipeline.name}`; + }); + } + addStep('pipeline_streams', true); + } catch (e) { + addStep('pipeline_streams', false, e); + return generateResponse(false); + } + + // Step 5 + // Delete pipeline connections + try { + const disconnectedReceiverIDs: string[] = []; + for (const pipeline of pipelines) { + const connections: ResourcesConnectionUUIDResponse[] = [ + ...(pipeline.control_receiver?.incoming_connections || []), + ...(pipeline.control_receiver?.outgoing_connections || []) + ]; + + for (const connection of connections) { + if (!disconnectedReceiverIDs.includes(connection.connection_uuid)) { + await disconnectReceiver(connection.connection_uuid) + .then(() => + disconnectedReceiverIDs.push(connection.connection_uuid) + ) + .catch(() => { + throw `Failed to disconnect connection ${connection.connection_uuid} in pipeline ${pipeline.name}`; + }); + } + } + } + addStep('pipeline_control_connections', true); + } catch (e) { + addStep('pipeline_control_connections', false, e); + return generateResponse(false); + } + + // Step 6 + // Reset pipelines + // Only do this step if enabled in options + if (resetPipelines) { + try { + for (const pipeline of pipelines) { + await resetPipeline(pipeline.uuid).catch((e) => { + throw `Failed to reset pipeline ${pipeline.name}`; + }); + } + addStep('reset_pipelines', true); + } catch (e) { + addStep('reset_pipelines', false, e); + return generateResponse(false); + } + } + + // Ingests + // Do not do these steps if pipelines were specified in the options + if (!pipes) { + let ingests: ResourcesCompactIngestResponse[]; + // Step 7 + // Fetch ingests + try { + ingests = await getIngests().catch((e) => { + throw 'Failed to fetch ingests'; + }); + } catch (e) { + addStep('ingest_streams', false, 'Failed to fetch ingests'); + return generateResponse(false); + } + + // Step 8 + // Delete ingest streams + try { + for (const ingest of ingests) { + for (const stream of ingest.streams) { + await deleteStreamByUuid(stream.uuid).catch((e) => { + throw `Failed to delete stream ${stream.uuid} for ingest ${ingest.name}`; + }); + } + } + addStep('ingest_streams', true); + } catch (e) { + addStep('ingest_streams', false, e); + return generateResponse(false); + } + + // Step 9 + // Delete ingest SRT sources + // Only do this step if enabled in options + if (deleteIngestSRTSources) { + try { + for (const ingest of ingests) { + const sources = await getIngestSources(ingest.uuid); + for (const source of sources) { + if (source.type.includes('SRT')) { + await deleteSrtSource(ingest.uuid, source.source_id).catch( + (e) => { + throw `Failed to delete SRT source ${source.name} for ingest ${ingest.name}`; + } + ); + } + } + } + addStep('ingest_src_sources', true); + } catch (e) { + addStep('ingest_src_sources', false, e); + return generateResponse(false); + } + } + } + + // Step 10 + // Check that everything was removed/disconnected/deleted + try { + const newPipelines = await getPipelines().catch((e) => { + throw 'Failed to fetch pipelines'; + }); + + for (const pipeline of newPipelines) { + // Check if all output streams have been stopped + const outputs = await getPipelineOutputs(pipeline.uuid).catch((e) => { + throw `Failed to fetch outputs for pipeline ${pipeline.name}`; + }); + for (const output of outputs) { + if (output.active_streams && output.active_streams.length) + throw `Failed to stop all active streams for output ${output.name} in pipeline ${pipeline.name}`; + } + // Check if all multiviewers have been deleted + const multiviewers = await getMultiviewsForPipeline(pipeline.uuid).catch( + () => { + throw `Failed to fetch multiviewers for pipeline ${pipeline.name}`; + } + ); + if (multiviewers?.length) + throw `Failed to delete all multiviewers for pipeline ${pipeline.name}`; + // Check if all pipeline streams have been stopped + if (pipeline.streams?.length) + throw `Failed to stop all streams for pipeline ${pipeline.name}`; + // Check if all pipeline connections have been disconnected + if (pipeline.control_receiver?.incoming_connections?.length) + throw `Failed to disconnect all incoming connections to the control receiver of pipeline ${pipeline.name}`; + if (pipeline.control_receiver?.outgoing_connections?.length) + throw `Failed to disconnect all outgoing connections from the control receiver of pipeline ${pipeline.name}`; + } + + if (!pipes) { + const ingests = await getIngests().catch((e) => { + throw 'Failed to fetch ingests'; + }); + + for (const ingest of ingests) { + // Check if all ingest streams have been stopped + if (ingest.streams?.length) + throw `Failed to stop ingest streams for ingest ${ingest.name}`; + // Check if all SRT sources have been deleted + // Only if delete ingest SRT sources is set to true in options (default) + if (deleteIngestSRTSources && ingest.sources?.length) { + const sources = await getIngestSources(ingest.uuid); + for (const source of sources) { + if (source.type.includes('SRT')) { + throw `Failed to delete SRT source ${source.name} for ingest ${ingest.name}`; + } + } + } + } + } + addStep('teardown_check', true); + return generateResponse(true); + } catch (e) { + addStep('teardown_check', false, e); + return generateResponse(false); + } +} diff --git a/src/api/manager/workflow.ts b/src/api/manager/workflow.ts index 7538be78..0a0fe130 100644 --- a/src/api/manager/workflow.ts +++ b/src/api/manager/workflow.ts @@ -1,3 +1,4 @@ +import { SourceReference, SourceWithId } from './../../interfaces/Source'; import { Production, ProductionSettings, @@ -15,7 +16,9 @@ import { } from '../ateliereLive/pipelines/pipelines'; import { createMultiviewForPipeline, - deleteAllMultiviewsFromPipeline + deleteAllMultiviewsFromPipeline, + deleteMultiviewFromPipeline, + updateMultiviewForPipeline } from '../ateliereLive/pipelines/multiviews/multiviews'; import { getSourceIdFromSourceName, @@ -35,7 +38,7 @@ import { ResourcesSenderNetworkEndpoint } from '../../../types/ateliere-live'; import { getSourcesByIds } from './sources'; -import { SourceWithId, SourceToPipelineStream } from '../../interfaces/Source'; +import { SourceToPipelineStream } from '../../interfaces/Source'; import { getAvailablePortsForIngest, getCurrentlyUsedPorts, @@ -49,6 +52,16 @@ import { Result } from '../../interfaces/result'; import { Monitoring } from '../../interfaces/monitoring'; import { getDatabase } from '../mongoClient/dbClient'; import { updatedMonitoringForProduction } from './job/syncMonitoring'; +import { ObjectId } from 'mongodb'; +import { MultiviewSettings } from '../../interfaces/multiview'; +import { + getPipelineRenderingEngineHtml, + getPipelineRenderingEngineMedia, + createPipelineHtmlSource, + createPipelineMediaSource, + deleteHtmlFromPipeline, + deleteMediaFromPipeline +} from '../ateliereLive/pipelines/renderingengine/renderingengine'; const isUsed = (pipeline: ResourcesPipelineResponse) => { const hasStreams = pipeline.streams.length > 0; @@ -68,15 +81,18 @@ const isUsed = (pipeline: ResourcesPipelineResponse) => { }; async function connectIngestSources( + productionSources: SourceReference[], productionSettings: ProductionSettings, sources: SourceWithId[], usedPorts: Set ) { - let input_slot = 0; const sourceToPipelineStreams: SourceToPipelineStream[] = []; + let input_slot = 0; for (const source of sources) { - input_slot = input_slot + 1; + input_slot = + productionSources.find((s) => s._id === source._id.toString()) + ?.input_slot || input_slot + 1; const ingestUuid = await getUuidFromIngestName( source.ingest_name, false @@ -85,11 +101,12 @@ async function connectIngestSources( throw `Could not find UUID for ${source.ingest_name}`; }); const sourceId = await getSourceIdFromSourceName( - ingestUuid, + ingestUuid || '', source.ingest_source_name, false ); - const audioSettings = await getAudioMapping(source._id); + + const audioSettings = await getAudioMapping(new ObjectId(source._id)); const newAudioMapping = audioSettings?.audio_stream?.audio_mapping; const audioMapping = newAudioMapping?.length ? newAudioMapping : [[0, 1]]; @@ -104,13 +121,21 @@ async function connectIngestSources( throw `No available ports for ingest '${source.ingest_name}'`; } - const availablePort = availablePorts.values().next().value; + const availablePort = availablePorts.values().next().value || 0; Log().info( `Allocated port ${availablePort} on '${source.ingest_name}' for ${source.ingest_source_name}` ); + + const pipelineSource = pipeline.sources?.find( + (s) => + s.ingest_source_name === source.ingest_source_name && + s.ingest_name === source.ingest_name + ); + const stream: PipelineStreamSettings = { pipeline_id: pipeline.pipeline_id!, - alignment_ms: pipeline.alignment_ms, + alignment_ms: + pipelineSource?.settings.alignment_ms || pipeline.alignment_ms, audio_format: pipeline.audio_format, audio_sampling_frequency: pipeline.audio_sampling_frequency, bit_depth: pipeline.bit_depth, @@ -122,14 +147,16 @@ async function connectIngestSources( frame_rate_n: pipeline.frame_rate_n, gop_length: pipeline.gop_length, height: pipeline.height, - max_network_latency_ms: pipeline.max_network_latency_ms, + max_network_latency_ms: + pipelineSource?.settings.max_network_latency_ms || + pipeline.max_network_latency_ms, pic_mode: pipeline.pic_mode, speed_quality_balance: pipeline.speed_quality_balance, video_kilobit_rate: pipeline.video_kilobit_rate, width: pipeline.width, - ingest_id: ingestUuid, - source_id: sourceId, - input_slot, + ingest_id: ingestUuid || '', + source_id: sourceId || 0, + input_slot: input_slot, audio_mapping: JSON.stringify(audioMapping), interfaces: [ { @@ -138,9 +165,10 @@ async function connectIngestSources( } ] }; + try { Log().info( - `Connecting '${source.ingest_name}/${ingestUuid}}:${source.ingest_source_name}' to '${pipeline.pipeline_name}/${pipeline.pipeline_id}'` + `Connecting '${source.ingest_name}/${ingestUuid}:${source.ingest_source_name}' to '${pipeline.pipeline_name}/${pipeline.pipeline_id}'` ); Log().debug(stream); const result = await connectIngestToPipeline(stream).catch((error) => { @@ -150,6 +178,7 @@ async function connectIngestSources( ); throw `Source '${source.ingest_name}/${ingestUuid}:${source.ingest_source_name}' failed to connect to '${pipeline.pipeline_name}/${pipeline.pipeline_id}': ${error.message}`; }); + usedPorts.add(availablePort); sourceToPipelineStreams.push({ source_id: source._id.toString(), @@ -308,11 +337,72 @@ export async function stopProduction( (p) => p.pipeline_id ); + const productionHasRenderingEngineSources = production.sources.some( + (source) => source.type === 'html' || source.type === 'mediaplayer' + ); + + if (productionHasRenderingEngineSources) { + const ingestPipelineUuids = (await getPipelines()).map( + (pipeline) => pipeline.uuid + ); + + for (const pipeline of production.production_settings.pipelines) { + const pipelineId = pipeline.pipeline_id; + + // Make sure pipeline ID exists before attempting to get rendering engine + const pipelineIdExists = pipelineId + ? ingestPipelineUuids.includes(pipelineId) + : false; + + if (pipelineId && pipelineIdExists) { + const htmlSources = await getPipelineRenderingEngineHtml( + pipelineId + ).catch((error) => { + Log().error('Failed to fetch HTML sources from pipeline: ', error); + return []; + }); + const mediaSources = await getPipelineRenderingEngineMedia( + pipelineId + ).catch((error) => { + Log().error('Failed to fetch Media sources from pipeline: ', error); + return []; + }); + + if (htmlSources.length > 0 && htmlSources) { + for (const pipeline of production.production_settings.pipelines) { + for (const htmlSource of htmlSources) { + const pipelineId = pipeline.pipeline_id; + if (pipelineId !== undefined) { + await deleteHtmlFromPipeline(pipelineId, htmlSource.input_slot); + } + } + } + } + + if (mediaSources.length > 0 && mediaSources) { + for (const pipeline of production.production_settings.pipelines) { + for (const mediaSource of mediaSources) { + const pipelineId = pipeline.pipeline_id; + if (pipelineId !== undefined) { + await deleteMediaFromPipeline( + pipelineId, + mediaSource.input_slot + ); + } + } + } + } + } + } + } + for (const source of production.sources) { - for (const stream_uuid of source.stream_uuids || []) { - await deleteStreamByUuid(stream_uuid).catch((error) => { - Log().error('Failed to delete stream! \nError: ', error); - }); + if (source.type === 'ingest_source') { + for (const stream_uuid of source.stream_uuids || []) { + await deleteStreamByUuid(stream_uuid).catch((error) => { + Log().error('Failed to delete stream! \nError: ', error); + }); + } } } @@ -355,6 +445,7 @@ export async function stopProduction( }; } } + try { await removePipelineStreams(id).catch((error) => { Log().error( @@ -371,7 +462,7 @@ export async function stopProduction( value: { step: 'remove_pipeline_streams', success: false, - message: 'Unexpected error occured' + message: `Error occurred when removing streams from pipeline: ${e}` } }; } else { @@ -394,7 +485,7 @@ export async function stopProduction( value: { step: 'remove_pipeline_multiviews', success: false, - message: 'Unexpected error occured' + message: `Error occurred when removing multiviews from pipeline: ${e}` } }; } else { @@ -410,6 +501,7 @@ export async function stopProduction( } Log().info(`Pipeline '${id}' stopped`); } + if ( !disconnectConnectionsStatus.ok || !removePipelineStreamsStatus.ok || @@ -450,9 +542,18 @@ export async function startProduction( try { // Get sources from the DB const sources = await getSourcesByIds( - production.sources.map((source) => { - return source._id.toString(); - }) + production.sources + .filter( + (source) => + (source._id !== undefined && source.type !== 'html') || + source.type !== 'mediaplayer' + ) + .map((source) => { + if (source._id !== undefined) { + return source._id.toString(); + } + return ''; + }) ).catch((error) => { if (error === "Can't connect to Database") { throw "Can't connect to Database"; @@ -537,8 +638,8 @@ export async function startProduction( return pipeline.uuid; }) ); - streams = await connectIngestSources( + production.sources, production_settings, sources, usedPorts @@ -558,7 +659,7 @@ export async function startProduction( return { ok: false, value: [{ step: 'streams', success: false }], - error: 'Could not setup streams: Unexpected error occured' + error: `Could not setup streams: Unexpected error occured: ${e}` }; } return { @@ -647,8 +748,9 @@ export async function startProduction( ], error: e }; - } // Try to setup pipeline outputs end + } + // Try to setup pipeline outputs end // Try to setup multiviews start try { if (!production.production_settings.pipelines[0].multiviews) { @@ -698,7 +800,7 @@ export async function startProduction( { step: 'pipeline_outputs', success: false }, { step: 'multiviews', success: false } ], - error: 'Unknown error occured' + error: 'Could not start multiviews' }; } return { @@ -714,20 +816,108 @@ export async function startProduction( }; } // Try to setup multiviews end + // Create HTML and Media sources on each pipeline + const htmlSources = production.sources.filter( + (source) => source.type === 'html' + ); + const mediaSources = production.sources.filter( + (source) => source.type === 'mediaplayer' + ); + + if (htmlSources.length > 0) { + for (const htmlSource of htmlSources) { + if (htmlSource.html_data) { + const htmlData = { + ...htmlSource.html_data, + url: htmlSource.html_data.url || '', + input_slot: htmlSource.input_slot + }; + await createPipelineHtmlSource( + production, + htmlSource.input_slot, + htmlData, + htmlSource + ); + } + } + } + + if (mediaSources.length > 0) { + for (const mediaSource of mediaSources) { + const mediaData = { + filename: mediaSource.media_data?.filename || '', + input_slot: mediaSource.input_slot + }; + await createPipelineMediaSource( + production, + mediaSource.input_slot, + mediaData, + mediaSource + ); + } + } + try { + const sourceIds = production.sources + .filter( + (source) => source.type !== 'mediaplayer' && source.type !== 'html' + ) + .map((source) => source._id?.toString()) + .filter((id): id is string => id !== undefined); + + const sourcesWithId = sourceIds ? await getSourcesByIds(sourceIds) : []; + // Store updated production in database await putProduction(production._id.toString(), { ...production, sources: production.sources.map((source) => { const streamsForSource = streams?.filter( - (stream) => stream.source_id === source._id.toString() + (stream) => stream.source_id === source._id?.toString() ); return { ...source, - stream_uuids: streamsForSource?.map((s) => s.stream_uuid), - input_slot: streamsForSource[0].input_slot + stream_uuids: + streamsForSource?.map((s) => s.stream_uuid) || undefined, + input_slot: source.input_slot }; }), + production_settings: { + ...production.production_settings, + pipelines: await Promise.all( + production.production_settings.pipelines.map(async (pipeline) => { + const newSources = await Promise.all( + sourcesWithId.map(async (source) => { + const ingestUuid = await getUuidFromIngestName( + source.ingest_name + ); + const sourceId = await getSourceIdFromSourceName( + ingestUuid || '', + source.ingest_source_name + ); + + const currentSettings = pipeline.sources?.find( + (s) => + s.ingest_source_name === source.ingest_source_name && + s.ingest_name === source.ingest_name + )?.settings; + + return { + ingest_name: source.ingest_name, + ingest_source_name: source.ingest_source_name, + settings: { + alignment_ms: + currentSettings?.alignment_ms ?? pipeline.alignment_ms, + max_network_latency_ms: + currentSettings?.max_network_latency_ms ?? + pipeline.max_network_latency_ms + } + }; + }) + ); + return { ...pipeline, sources: newSources }; + }) + ) + }, isActive: true }).catch(async (error) => { Log().error(error); @@ -831,3 +1021,265 @@ export async function startProduction( }; } } + +export async function postMultiviewersOnRunningProduction( + production: Production, + additions: MultiviewSettings[] +) { + try { + const multiview = production.production_settings.pipelines[0].multiviews; + if (!multiview) { + Log().error( + `No multiview settings specified for production: ${production.name}` + ); + throw `No multiview settings specified for production: ${production.name}`; + } + + const productionSettings = { + ...production.production_settings, + pipelines: production.production_settings.pipelines.map((pipeline) => { + return { + ...pipeline, + multiviews: additions + }; + }) + }; + + const runtimeMultiviews = await createMultiviewForPipeline( + productionSettings, + production.sources + ).catch(async (error) => { + Log().error( + `Failed to create multiview for pipeline '${productionSettings.pipelines[0].pipeline_name}/${productionSettings.pipelines[0].pipeline_id}'`, + error + ); + throw `Failed to create multiview for pipeline '${productionSettings.pipelines[0].pipeline_name}/${productionSettings.pipelines[0].pipeline_id}': ${error}`; + }); + + const multiviewsWithUpdatedId: MultiviewSettings[] = [ + ...multiview.slice(0, multiview.length - runtimeMultiviews.length), + ...runtimeMultiviews.map((runtimeMultiview, index) => { + return { + ...multiview[multiview.length - runtimeMultiviews.length + index], + multiview_id: runtimeMultiview.id + }; + }) + ]; + + await putProduction(production._id.toString(), { + ...production, + production_settings: { + ...production.production_settings, + pipelines: production.production_settings.pipelines.map((pipeline) => { + return { + ...pipeline, + multiviews: multiviewsWithUpdatedId + }; + }) + } + }).catch(async (error) => { + Log().error( + `Failed to save multiviews for pipeline '${productionSettings.pipelines[0].pipeline_name}/${productionSettings.pipelines[0].pipeline_id}' to database`, + error + ); + throw error; + }); + + return { + ok: true, + value: { + success: true, + steps: [ + { + step: 'create_multiview', + success: true + } + ] + } + }; + } catch (e) { + Log().error('Could not create multiviews'); + Log().error(e); + if (typeof e !== 'string') { + return { + ok: false, + value: { + success: true, + steps: [ + { + step: 'create_multiview', + success: false + } + ] + }, + error: 'Could not create multiviews' + }; + } + return { + ok: false, + value: { + success: true, + steps: [ + { + step: 'create_multiview', + success: false, + message: e + } + ] + }, + error: e + }; + } +} + +export async function putMultiviewersOnRunningProduction( + production: Production, + updates: MultiviewSettings[] +) { + try { + updates.map(async (multiview) => { + const views = multiview.layout.views; + + if ( + multiview.multiview_id && + production.production_settings.pipelines[0].pipeline_id + ) { + await updateMultiviewForPipeline( + production.production_settings.pipelines[0].pipeline_id, + multiview.multiview_id, + views + ); + } + }); + + return { + ok: true, + value: { + success: true, + steps: [ + { + step: 'update_multiview', + success: true + } + ] + } + }; + } catch (e) { + Log().error('Could not update multiviews'); + Log().error(e); + if (typeof e !== 'string') { + return { + ok: false, + value: { + success: true, + steps: [ + { + step: 'update_multiview', + success: false + } + ] + }, + error: 'Could not update multiviews' + }; + } + return { + ok: false, + value: { + success: true, + steps: [ + { + step: 'update_multiview', + success: false, + message: e + } + ] + }, + error: e + }; + } +} + +export async function deleteMultiviewersOnRunningProduction( + production: Production, + removals: MultiviewSettings[] +) { + try { + const pipeline = production.production_settings.pipelines.find((p) => + p.multiviews ? p.multiviews?.length > 0 : undefined + ); + const multiviewIndexArray = pipeline?.multiviews + ? pipeline.multiviews.map((p) => p.for_pipeline_idx) + : undefined; + + const multiviewIndex = multiviewIndexArray?.find((p) => p !== undefined); + + const pipelineUUID = + multiviewIndex !== undefined + ? production.production_settings.pipelines[multiviewIndex].pipeline_id + : undefined; + + if (!pipelineUUID) return; + + await Promise.allSettled( + removals.map((multiview) => { + if (multiview.multiview_id) { + deleteMultiviewFromPipeline( + pipelineUUID, + multiview.multiview_id + ).catch((error) => { + Log().error( + `Failed to remove multiview '${multiview.multiview_id}' from pipeline '${pipelineUUID}'`, + error + ); + throw `Failed to remove multiview '${multiview.multiview_id}' from pipeline '${pipelineUUID}': ${error}`; + }); + } + }) + ); + + return { + ok: true, + value: { + success: true, + steps: [ + { + step: 'delete_multiview', + success: true + } + ] + } + }; + } catch (e) { + Log().error('Could not delete multiviews'); + Log().error(e); + if (typeof e !== 'string') { + return { + ok: false, + value: { + success: true, + steps: [ + { + step: 'delete_multiview', + success: false + } + ] + }, + error: 'Could not delete multiviews' + }; + } + return { + ok: false, + value: { + success: true, + steps: [ + { + step: 'delete_multiview', + success: false, + message: e + } + ] + }, + error: e + }; + } +} diff --git a/src/api/mongoClient/dbClient.ts b/src/api/mongoClient/dbClient.ts index ab1780ca..4989776c 100644 --- a/src/api/mongoClient/dbClient.ts +++ b/src/api/mongoClient/dbClient.ts @@ -47,10 +47,17 @@ async function bootstrapDbCollections(db: Db) { await migratePresets(db); } - const multiviews = await db.collection('multiviews').countDocuments(); + const multiviews = await db + .collection('multiview-presets') + .countDocuments(); if (multiviews === 0) { Log().info('Bootstrapping database with default multiview'); - await db.collection('multiviews').insertOne(defaultMultiview); + + await db.collection('multiview-presets').insertMany(defaultMultiview); + await db.collection('multiviews').insertOne({ + ...defaultMultiview[0], + name: 'Default - ' + defaultMultiview[0].name + }); } else { await migrateMultiviewPresets(db); } diff --git a/src/api/mongoClient/dbMigrate.ts b/src/api/mongoClient/dbMigrate.ts index 2c7578fa..7232a82e 100644 --- a/src/api/mongoClient/dbMigrate.ts +++ b/src/api/mongoClient/dbMigrate.ts @@ -102,7 +102,7 @@ export async function migratePresets(db: Db) { export async function migrateMultiviewPresets(db: Db) { const multiviewPresets = await db - .collection('multiviews') + .collection('multiview-presets') .find({}) .toArray(); for (const preset of multiviewPresets) { @@ -114,13 +114,15 @@ export async function migrateMultiviewPresets(db: Db) { `Upgrading multiview preset '${migratedPreset.name}' with new SRT output settings` ); const { _id, ...rest } = migratedPreset; - await db.collection('multiviews').findOneAndReplace( - { _id: new ObjectId(_id) }, - { - ...rest - }, - { returnDocument: 'after' } - ); + await db + .collection('multiview-presets') + .findOneAndReplace( + { _id: new ObjectId(_id) }, + { + ...rest + }, + { returnDocument: 'after' } + ); } } } diff --git a/src/api/mongoClient/dbOverrides.ts b/src/api/mongoClient/dbOverrides.ts index 57a5aef7..74f32fa3 100644 --- a/src/api/mongoClient/dbOverrides.ts +++ b/src/api/mongoClient/dbOverrides.ts @@ -100,7 +100,7 @@ async function overrideMultiviewPreset( break; } const { _id, ...rest } = multiviewPreset; - await db.collection('multiviews').findOneAndReplace( + await db.collection('multiview-presets').findOneAndReplace( { _id: new ObjectId(_id) }, { ...rest @@ -131,7 +131,7 @@ export async function applyPresetOverrides(db: Db, overrideString: string) { } } else if (override.type === 'multiview') { const multiviewPreset = await db - .collection('multiviews') + .collection('multiview-presets') .findOne({ name: override.name }); if (multiviewPreset) { await overrideMultiviewPreset(db, multiviewPreset, override); diff --git a/src/api/mongoClient/defaults/preset.ts b/src/api/mongoClient/defaults/preset.ts index 7c91e825..040d927b 100644 --- a/src/api/mongoClient/defaults/preset.ts +++ b/src/api/mongoClient/defaults/preset.ts @@ -181,125 +181,273 @@ export const ldOnlyPreset = { ] }; -export const defaultMultiview = { - _id: new ObjectId('6582ff61987fa290e66ba95c'), - name: '10 inputs HD', - layout: { - output_height: 1080, - output_width: 1920, - views: [ - { - input_slot: 1002, - x: 0, - y: 0, - height: 540, - width: 960, - label: 'Preview' - }, - { - input_slot: 1001, - x: 960, - y: 0, - height: 540, - width: 960, - label: 'Program' - }, - { - input_slot: 1, - x: 0, - y: 540, - height: 270, - width: 384, - label: 'Input 1' - }, - { - input_slot: 2, - x: 384, - y: 540, - height: 270, - width: 384, - label: 'Input 2' - }, - { - input_slot: 3, - x: 768, - y: 540, - height: 270, - width: 384, - label: 'Input 3' - }, - { - input_slot: 4, - x: 1152, - y: 540, - height: 270, - width: 384, - label: 'Input 4' - }, - { - input_slot: 5, - x: 1536, - y: 540, - height: 270, - width: 384, - label: 'Input 5' - }, - { - input_slot: 6, - x: 0, - y: 810, - height: 270, - width: 384, - label: 'Input 6' - }, - { - input_slot: 7, - x: 384, - y: 810, - height: 270, - width: 384, - label: 'Input 7' - }, - { - input_slot: 8, - x: 768, - y: 810, - height: 270, - width: 384, - label: 'Input 8' - }, - { - input_slot: 9, - x: 1152, - y: 810, - height: 270, - width: 384, - label: 'Input 9' - }, - { - input_slot: 10, - x: 1536, - y: 810, - height: 270, - width: 384, - label: 'Input 10' - } - ] +export const defaultMultiview = [ + { + _id: new ObjectId('6582ff61987fa290e66ba95c'), + name: '10 inputs HD', + layout: { + output_height: 1080, + output_width: 1920, + views: [ + { + input_slot: 1002, + x: 0, + y: 0, + height: 540, + width: 960, + label: 'Preview' + }, + { + input_slot: 1001, + x: 960, + y: 0, + height: 540, + width: 960, + label: 'Program' + }, + { + input_slot: 1, + x: 0, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 2, + x: 384, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 3, + x: 768, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 4, + x: 1152, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 5, + x: 1536, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 6, + x: 0, + y: 810, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 7, + x: 384, + y: 810, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 8, + x: 768, + y: 810, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 9, + x: 1152, + y: 810, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 10, + x: 1536, + y: 810, + height: 270, + width: 384, + label: '' + } + ] + }, + output: { + format: 'MPEG-TS-SRT', + frame_rate_d: 1, + frame_rate_n: 50, + local_ip: '0.0.0.0', + local_port: 1234, + remote_ip: '0.0.0.0', + remote_port: 1234, + srt_mode: 'listener', + srt_latency_ms: 120, + srt_passphrase: '', + video_format: 'AVC', + video_kilobit_rate: 5000, + speed_quality_balance: 'balanced', + pic_mode: 'pic_mode_ip' + } }, - output: { - format: 'MPEG-TS-SRT', - frame_rate_d: 1, - frame_rate_n: 50, - local_ip: '0.0.0.0', - local_port: 1234, - remote_ip: '0.0.0.0', - remote_port: 1234, - srt_mode: 'listener', - srt_latency_ms: 120, - srt_passphrase: '', - video_format: 'AVC', - video_kilobit_rate: 5000, - speed_quality_balance: 'balanced', - pic_mode: 'pic_mode_ip' + { + _id: new ObjectId('65cb266c00fecda4a1faf977'), + name: '13 inputs HD', + layout: { + output_height: 1080, + output_width: 1920, + views: [ + { + input_slot: 1002, + x: 0, + y: 0, + height: 540, + width: 960, + label: 'Preview' + }, + { + input_slot: 1001, + x: 960, + y: 0, + height: 540, + width: 960, + label: 'Program' + }, + { + input_slot: 1, + x: 0, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 2, + x: 384, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 3, + x: 768, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 4, + x: 1152, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 5, + x: 1536, + y: 540, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 6, + x: 0, + y: 810, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 7, + x: 384, + y: 810, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 8, + x: 768, + y: 810, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 9, + x: 1152, + y: 810, + height: 270, + width: 384, + label: '' + }, + { + input_slot: 10, + x: 1536, + y: 810, + height: 135, + width: 192, + label: '' + }, + { + input_slot: 11, + x: 1728, + y: 810, + height: 135, + width: 192, + label: '' + }, + { + input_slot: 12, + x: 1536, + y: 945, + height: 135, + width: 192, + label: '' + }, + { + input_slot: 13, + x: 1728, + y: 945, + height: 135, + width: 192, + label: '' + } + ] + }, + output: { + format: 'MPEG-TS-SRT', + frame_rate_d: 1, + frame_rate_n: 50, + local_ip: '0.0.0.0', + local_port: 4567, + remote_ip: '0.0.0.0', + remote_port: 1234, + srt_mode: 'listener', + srt_latency_ms: 60, + srt_passphrase: '', + video_format: 'AVC', + video_kilobit_rate: 5000, + speed_quality_balance: 'balanced', + pic_mode: 'pic_mode_ip' + } } -}; +]; diff --git a/src/app/api/manager/ingests/[uuid]/srt/route.ts b/src/app/api/manager/ingests/[uuid]/srt/route.ts new file mode 100644 index 00000000..ec4bc9fc --- /dev/null +++ b/src/app/api/manager/ingests/[uuid]/srt/route.ts @@ -0,0 +1,34 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../api/manager/auth'; +import { Log } from '../../../../../../api/logger'; +import { createSrtSource } from '../../../../../../api/ateliereLive/ingest'; + +type Params = { + uuid: string; +}; + +export async function POST( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const data = await request.json(); + + return await createSrtSource(params.uuid, data.srtPayload) + .then((response) => { + return new NextResponse(JSON.stringify(response)); + }) + .catch((error) => { + Log().error(error); + const errorResponse = { + ok: false, + error: error.message + }; + return new NextResponse(JSON.stringify(errorResponse), { status: 500 }); + }); +} diff --git a/src/app/api/manager/ingests/route.ts b/src/app/api/manager/ingests/route.ts new file mode 100644 index 00000000..9b9696d5 --- /dev/null +++ b/src/app/api/manager/ingests/route.ts @@ -0,0 +1,14 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../api/manager/auth'; +import { getIngests } from '../../../../api/ateliereLive/ingest'; + +export async function GET(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const ingests = await getIngests(); + return new NextResponse(JSON.stringify(ingests)); +} diff --git a/src/app/api/manager/ingests/source_id/[ingest_name]/[ingest_source_name]/route.ts b/src/app/api/manager/ingests/source_id/[ingest_name]/[ingest_source_name]/route.ts new file mode 100644 index 00000000..09b7226d --- /dev/null +++ b/src/app/api/manager/ingests/source_id/[ingest_name]/[ingest_source_name]/route.ts @@ -0,0 +1,38 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../../api/manager/auth'; +import { + getSourceIdFromSourceName, + getUuidFromIngestName +} from '../../../../../../../api/ateliereLive/ingest'; + +type Params = { + ingest_name: string; + ingest_source_name: string; +}; + +export async function GET( + request: NextRequest, + { params }: { params: Params } +) { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const ingestUuid = await getUuidFromIngestName(params.ingest_name, false); + const sourceId = ingestUuid + ? await getSourceIdFromSourceName( + ingestUuid, + params.ingest_source_name, + false + ) + : 0; + return new NextResponse(JSON.stringify(sourceId), { status: 200 }); + } catch (error) { + return new NextResponse(`Error getting streams for ingest: ${error}`, { + status: 500 + }); + } +} diff --git a/src/app/api/manager/ingests/sources/[ingest_name]/route.ts b/src/app/api/manager/ingests/sources/[ingest_name]/route.ts new file mode 100644 index 00000000..da9e9449 --- /dev/null +++ b/src/app/api/manager/ingests/sources/[ingest_name]/route.ts @@ -0,0 +1,31 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../api/manager/auth'; +import { + getIngestSources, + getUuidFromIngestName +} from '../../../../../../api/ateliereLive/ingest'; + +type Params = { + ingest_name: string; +}; + +export async function GET( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const ingestUuid = await getUuidFromIngestName(params.ingest_name); + const ingestSources = ingestUuid ? await getIngestSources(ingestUuid) : []; + return new NextResponse(JSON.stringify(ingestSources), { status: 200 }); + } catch (error) { + return new NextResponse(`Error getting sources for ingest: ${error}`, { + status: 500 + }); + } +} diff --git a/src/app/api/manager/ingests/streams/[ingest_name]/[ingest_source_name]/route.ts b/src/app/api/manager/ingests/streams/[ingest_name]/[ingest_source_name]/route.ts new file mode 100644 index 00000000..f308b18b --- /dev/null +++ b/src/app/api/manager/ingests/streams/[ingest_name]/[ingest_source_name]/route.ts @@ -0,0 +1,39 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../../api/manager/auth'; +import { + getUuidFromIngestName, + getIngestStreams, + getSourceIdFromSourceName +} from '../../../../../../../api/ateliereLive/ingest'; + +type Params = { + ingest_name: string; + ingest_source_name: string; +}; + +export async function GET( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const ingestUuid = await getUuidFromIngestName(params.ingest_name); + const sourceId = ingestUuid + ? await getSourceIdFromSourceName(ingestUuid, params.ingest_source_name) + : 0; + const ingestStreams = ingestUuid ? await getIngestStreams(ingestUuid) : []; + const sourceStreams = ingestStreams.filter( + (stream) => stream.source_id === sourceId + ); + return new NextResponse(JSON.stringify(sourceStreams), { status: 200 }); + } catch (error) { + return new NextResponse(`Error getting streams for ingest: ${error}`, { + status: 500 + }); + } +} diff --git a/src/app/api/manager/inventory/[_id]/database/route.ts b/src/app/api/manager/inventory/[_id]/database/route.ts new file mode 100644 index 00000000..08ac90a2 --- /dev/null +++ b/src/app/api/manager/inventory/[_id]/database/route.ts @@ -0,0 +1,28 @@ +import { Params } from 'next/dist/shared/lib/router/utils/route-matcher'; +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../api/manager/auth'; +import { removeInventorySource } from '../../../../../../api/manager/inventory'; + +export async function DELETE( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + try { + await removeInventorySource(params._id); + return new NextResponse(null, { + status: 200 + }); + } catch (error) { + return new NextResponse( + `Error occurred while posting to DB! Error: ${error}`, + { + status: 500 + } + ); + } +} diff --git a/src/app/api/manager/multiview-presets/[id]/route.ts b/src/app/api/manager/multiview-presets/[id]/route.ts new file mode 100644 index 00000000..a279d9e9 --- /dev/null +++ b/src/app/api/manager/multiview-presets/[id]/route.ts @@ -0,0 +1,22 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { Params } from 'next/dist/shared/lib/router/utils/route-matcher'; +import { getMultiviewPreset } from '../../../../../api/manager/multiview-presets'; +import { isAuthenticated } from '../../../../../api/manager/auth'; + +export async function GET( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + try { + return NextResponse.json(await getMultiviewPreset(params.id)); + } catch (e) { + return new NextResponse(JSON.stringify(e), { + status: 500 + }); + } +} diff --git a/src/app/api/manager/multiview-presets/route.ts b/src/app/api/manager/multiview-presets/route.ts new file mode 100644 index 00000000..f6d4d5d8 --- /dev/null +++ b/src/app/api/manager/multiview-presets/route.ts @@ -0,0 +1,19 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../api/manager/auth'; +import { getMultiviewPresets } from '../../../../api/manager/multiview-presets'; + +export async function GET(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + return NextResponse.json(await getMultiviewPresets()); + } catch (e) { + return new NextResponse(JSON.stringify(e), { + status: 500 + }); + } +} diff --git a/src/app/api/manager/multiviewersOnRunningProduction/[id]/route.ts b/src/app/api/manager/multiviewersOnRunningProduction/[id]/route.ts new file mode 100644 index 00000000..3869fabb --- /dev/null +++ b/src/app/api/manager/multiviewersOnRunningProduction/[id]/route.ts @@ -0,0 +1,51 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { Log } from '../../../../../api/logger'; +import { isAuthenticated } from '../../../../../api/manager/auth'; +import { + deleteMultiviewersOnRunningProduction, + putMultiviewersOnRunningProduction +} from '../../../../../api/manager/workflow'; + +export async function PUT(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const { production, updates } = await request.json(); + return putMultiviewersOnRunningProduction(production, updates) + .then((result) => { + return new NextResponse(JSON.stringify(result)); + }) + .catch((error) => { + Log().error(error); + const errorResponse = { + ok: false, + error: 'Could not update multiviewers' + }; + return new NextResponse(JSON.stringify(errorResponse), { status: 500 }); + }); +} + +export async function DELETE(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const { production, removals } = await request.json(); + return deleteMultiviewersOnRunningProduction(production, removals) + .then((result) => { + return new NextResponse(JSON.stringify(result)); + }) + .catch((error) => { + Log().error(error); + const errorResponse = { + ok: false, + error: 'Could not remove multiviewers' + }; + return new NextResponse(JSON.stringify(errorResponse), { status: 500 }); + }); +} diff --git a/src/app/api/manager/multiviewersOnRunningProduction/route.ts b/src/app/api/manager/multiviewersOnRunningProduction/route.ts new file mode 100644 index 00000000..cbc04968 --- /dev/null +++ b/src/app/api/manager/multiviewersOnRunningProduction/route.ts @@ -0,0 +1,26 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { Log } from '../../../../api/logger'; +import { isAuthenticated } from '../../../../api/manager/auth'; +import { postMultiviewersOnRunningProduction } from '../../../../api/manager/workflow'; + +export async function POST(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const { production, additions } = await request.json(); + return postMultiviewersOnRunningProduction(production, additions) + .then((result) => { + return new NextResponse(JSON.stringify(result)); + }) + .catch((error) => { + Log().error(error); + const errorResponse = { + ok: false, + error: 'Could not add multiviewers' + }; + return new NextResponse(JSON.stringify(errorResponse), { status: 500 }); + }); +} diff --git a/src/app/api/manager/multiviews/[id]/route.ts b/src/app/api/manager/multiviews/[id]/route.ts index 4390f374..56d8af54 100644 --- a/src/app/api/manager/multiviews/[id]/route.ts +++ b/src/app/api/manager/multiviews/[id]/route.ts @@ -1,9 +1,17 @@ import { NextRequest, NextResponse } from 'next/server'; import { isAuthenticated } from '../../../../../api/manager/auth'; -import { getMultiviewPreset } from '../../../../../api/manager/presets'; import { Params } from 'next/dist/shared/lib/router/utils/route-matcher'; import { updateMultiviewForPipeline } from '../../../../../api/ateliereLive/pipelines/multiviews/multiviews'; import { MultiviewViews } from '../../../../../interfaces/multiview'; +import { + deleteLayout, + getMultiviewLayout +} from '../../../../../api/manager/multiviews'; + +type PutMultiviewRequest = { + pipelineId: string; + multiviews: MultiviewViews[]; +}; export async function GET( request: NextRequest, @@ -15,17 +23,14 @@ export async function GET( }); } try { - return NextResponse.json(await getMultiviewPreset(params.id)); + return NextResponse.json(await getMultiviewLayout(params.id)); } catch (e) { return new NextResponse(JSON.stringify(e), { status: 500 }); } } -type PutMultiviewRequest = { - pipelineId: string; - multiviews: MultiviewViews[]; -}; + export async function PUT( request: NextRequest, { params }: { params: Params } @@ -50,3 +55,28 @@ export async function PUT( }); } } + +export async function DELETE( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + try { + await deleteLayout(params.id); + return new NextResponse(null, { + status: 200 + }); + } catch (error) { + console.log(error); + return new NextResponse( + `Error occurred while deleting from DB! Error: ${error}`, + { + status: 500 + } + ); + } +} diff --git a/src/app/api/manager/multiviews/route.tsx b/src/app/api/manager/multiviews/route.tsx index 10b543f4..4412e6c1 100644 --- a/src/app/api/manager/multiviews/route.tsx +++ b/src/app/api/manager/multiviews/route.tsx @@ -1,6 +1,12 @@ import { NextRequest, NextResponse } from 'next/server'; -import { getMultiviewPresets } from '../../../../api/manager/presets'; import { isAuthenticated } from '../../../../api/manager/auth'; +import { + deleteLayouts, + getMultiviewLayouts, + putMultiviewLayout +} from '../../../../api/manager/multiviews'; +import { MultiviewPreset } from '../../../../interfaces/preset'; +import { Log } from '../../../../api/logger'; export async function GET(request: NextRequest): Promise { if (!(await isAuthenticated())) { @@ -10,10 +16,54 @@ export async function GET(request: NextRequest): Promise { } try { - return NextResponse.json(await getMultiviewPresets()); + return NextResponse.json(await getMultiviewLayouts()); } catch (e) { return new NextResponse(JSON.stringify(e), { status: 500 }); } } + +export async function PUT(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const body = (await request.json()) as MultiviewPreset; + const newMultiviewLayout = await putMultiviewLayout(body); + return new NextResponse(JSON.stringify(newMultiviewLayout), { + status: 200 + }); + } catch (error) { + Log().warn('Could not update layout', error); + return new NextResponse(`Error searching DB! Error: ${error}`, { + status: 500 + }); + } +} + +export async function DELETE(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + try { + const productionId = await request.json(); + await deleteLayouts(productionId); + return new NextResponse(null, { + status: 200 + }); + } catch (error) { + console.log(error); + return new NextResponse( + `Error occurred while deleting from DB! Error: ${error}`, + { + status: 500 + } + ); + } +} diff --git a/src/app/api/manager/pipelines/[id]/rendering-engine/html/[input_slot]/[ld_pipeline_id]/route.ts b/src/app/api/manager/pipelines/[id]/rendering-engine/html/[input_slot]/[ld_pipeline_id]/route.ts new file mode 100644 index 00000000..980d5ab0 --- /dev/null +++ b/src/app/api/manager/pipelines/[id]/rendering-engine/html/[input_slot]/[ld_pipeline_id]/route.ts @@ -0,0 +1,70 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../../../../api/manager/auth'; +import { deleteHtmlFromPipeline } from '../../../../../../../../../api/ateliereLive/pipelines/renderingengine/renderingengine'; +import { DeleteRenderingEngineSourceStep } from '../../../../../../../../../interfaces/Source'; +import { Result } from '../../../../../../../../../interfaces/result'; +import { Log } from '../../../../../../../../../api/logger'; + +type Params = { + id: string; + input_slot: number; + ld_pipeline_id: string; +}; + +export async function DELETE( + request: NextRequest, + { params }: { params: Params } +) { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + await deleteHtmlFromPipeline(params.id, params.input_slot).catch((e) => { + Log().error(`Failed to delete html: ${params.id}: ${e.message}`); + throw `Failed to delete html: ${params.id}: ${e.message}`; + }); + return new NextResponse( + JSON.stringify({ + ok: true, + value: [ + { + step: 'delete_html', + success: true + } + ] + }) + ); + } catch (e) { + if (typeof e !== 'string') { + return new NextResponse( + JSON.stringify({ + ok: false, + value: [ + { + step: 'delete_html', + success: false, + message: `Failed to delete html` + } + ], + error: 'Delete html failed' + } satisfies Result) + ); + } + return new NextResponse( + JSON.stringify({ + ok: false, + value: [ + { + step: 'delete_html', + success: false, + message: `Failed to delete html: ${params.id}: ${e}` + } + ], + error: e + } satisfies Result) + ); + } +} diff --git a/src/app/api/manager/pipelines/[id]/rendering-engine/html/get/route.ts b/src/app/api/manager/pipelines/[id]/rendering-engine/html/get/route.ts new file mode 100644 index 00000000..c4d57cf6 --- /dev/null +++ b/src/app/api/manager/pipelines/[id]/rendering-engine/html/get/route.ts @@ -0,0 +1,38 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../../../api/manager/auth'; +import { getPipelineHtmlSources } from '../../../../../../../../api/ateliereLive/pipelines/renderingengine/renderingengine'; + +type Params = { + id: string; +}; + +export async function GET( + request: NextRequest, + { params }: { params: Params } +) { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const htmlSources = await getPipelineHtmlSources(params.id); + return new NextResponse( + JSON.stringify({ + htmlSources + }), + { + status: 200 + } + ); + } catch (error) { + console.log(error); + return new NextResponse( + `Error fetching pipeline html sources! Error: ${error}`, + { + status: 500 + } + ); + } +} diff --git a/src/app/api/manager/pipelines/[id]/rendering-engine/media/[input_slot]/[ld_pipeline_id]/route.ts b/src/app/api/manager/pipelines/[id]/rendering-engine/media/[input_slot]/[ld_pipeline_id]/route.ts new file mode 100644 index 00000000..8c1df499 --- /dev/null +++ b/src/app/api/manager/pipelines/[id]/rendering-engine/media/[input_slot]/[ld_pipeline_id]/route.ts @@ -0,0 +1,55 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../../../../api/manager/auth'; +import { deleteMediaFromPipeline } from '../../../../../../../../../api/ateliereLive/pipelines/renderingengine/renderingengine'; +import { DeleteRenderingEngineSourceStep } from '../../../../../../../../../interfaces/Source'; +import { Result } from '../../../../../../../../../interfaces/result'; +import { Log } from '../../../../../../../../../api/logger'; + +type Params = { + id: string; + input_slot: number; + ld_pipeline_id: string; +}; + +export async function DELETE( + request: NextRequest, + { params }: { params: Params } +) { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + await deleteMediaFromPipeline(params.id, params.input_slot).catch((e) => { + Log().error(`Failed to delete media: ${params.id}: ${e.message}`); + throw `Failed to delete media: ${params.id}: ${e.message}`; + }); + return new NextResponse( + JSON.stringify({ + ok: true, + value: [ + { + step: 'delete_media', + success: true + } + ] + }) + ); + } catch (e) { + return new NextResponse( + JSON.stringify({ + ok: false, + value: [ + { + step: 'delete_media', + success: false, + message: `Failed to delete media` + } + ], + error: typeof e === 'string' ? e : 'Failed to delete media' + } satisfies Result) + ); + } +} diff --git a/src/app/api/manager/pipelines/[id]/rendering-engine/media/get/route.ts b/src/app/api/manager/pipelines/[id]/rendering-engine/media/get/route.ts new file mode 100644 index 00000000..63dcd761 --- /dev/null +++ b/src/app/api/manager/pipelines/[id]/rendering-engine/media/get/route.ts @@ -0,0 +1,39 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../../../api/manager/auth'; +import { getPipelineMediaSources } from '../../../../../../../../api/ateliereLive/pipelines/renderingengine/renderingengine'; +import { Log } from '../../../../../../../../api/logger'; + +type Params = { + id: string; +}; + +export async function GET( + request: NextRequest, + { params }: { params: Params } +) { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const mediaSources = await getPipelineMediaSources(params.id); + return new NextResponse( + JSON.stringify({ + mediaSources + }), + { + status: 200 + } + ); + } catch (error) { + Log().error(error); + return new NextResponse( + `Error fetching pipeline media sources! Error: ${error}`, + { + status: 500 + } + ); + } +} diff --git a/src/app/api/manager/pipelines/[id]/rendering-engine/route.ts b/src/app/api/manager/pipelines/[id]/rendering-engine/route.ts new file mode 100644 index 00000000..ae80d825 --- /dev/null +++ b/src/app/api/manager/pipelines/[id]/rendering-engine/route.ts @@ -0,0 +1,39 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../api/manager/auth'; +import { getPipelineRenderingEngine } from '../../../../../../api/ateliereLive/pipelines/renderingengine/renderingengine'; +import { Log } from '../../../../../../api/logger'; + +type Params = { + id: string; +}; + +export async function GET( + request: NextRequest, + { params }: { params: Params } +) { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const renderingEngine = await getPipelineRenderingEngine(params.id); + return new NextResponse( + JSON.stringify({ + renderingEngine + }), + { + status: 200 + } + ); + } catch (error) { + Log().error(error); + return new NextResponse( + `Error getting rendering engine for pipeline! Error: ${error}`, + { + status: 500 + } + ); + } +} diff --git a/src/app/api/manager/productions/[id]/[pipeline_id]/[ingest_name]/[ingest_source_name]/route.ts b/src/app/api/manager/productions/[id]/[pipeline_id]/[ingest_name]/[ingest_source_name]/route.ts new file mode 100644 index 00000000..bb3585d5 --- /dev/null +++ b/src/app/api/manager/productions/[id]/[pipeline_id]/[ingest_name]/[ingest_source_name]/route.ts @@ -0,0 +1,119 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { + getProductionPipelineSourceAlignment, + setProductionPipelineSourceAlignment, + getProductionSourceLatency, + setProductionPipelineSourceLatency +} from '../../../../../../../../api/manager/productions'; +import { isAuthenticated } from '../../../../../../../../api/manager/auth'; +import { + getSourceIdFromSourceName, + getUuidFromIngestName +} from '../../../../../../../../api/ateliereLive/ingest'; + +type Params = { + id: string; + pipeline_id: string; + ingest_name: string; + ingest_source_name: string; +}; + +export async function GET( + request: NextRequest, + { params }: { params: Params } +) { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const ingestUuid = await getUuidFromIngestName(params.ingest_name); + + const sourceId = ingestUuid + ? await getSourceIdFromSourceName(ingestUuid, params.ingest_source_name) + : null; + + const alignment = + sourceId !== null && sourceId !== undefined + ? await getProductionPipelineSourceAlignment( + params.id, + params.pipeline_id, + params.ingest_source_name, + params.ingest_name + ) + : 0; + + const latency = + sourceId !== null && sourceId !== undefined + ? await getProductionSourceLatency( + params.id, + params.pipeline_id, + params.ingest_source_name, + params.ingest_name + ) + : 0; + + const result = { + alignment: alignment, + latency: latency + }; + + return new NextResponse(JSON.stringify(result), { status: 200 }); + } catch (error) { + console.log('Error:', error); + return new NextResponse(`Error searching DB! Error: ${error}`, { + status: 500 + }); + } +} + +export async function PUT( + request: NextRequest, + { params }: { params: Params } +) { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const body = await request.json(); + + const alignment = body.alignment_ms; + const latency = body.max_network_latency_ms; + + try { + const ingestUuid = await getUuidFromIngestName(params.ingest_name); + const sourceId = ingestUuid + ? await getSourceIdFromSourceName(ingestUuid, params.ingest_source_name) + : null; + + if (sourceId !== null && sourceId !== undefined) { + const alignmentResult = await setProductionPipelineSourceAlignment( + params.id, + params.pipeline_id, + params.ingest_name, + params.ingest_source_name, + alignment + ); + const latencyResult = await setProductionPipelineSourceLatency( + params.id, + params.pipeline_id, + params.ingest_name, + params.ingest_source_name, + latency + ); + + return new NextResponse( + JSON.stringify({ alignment: alignmentResult, latency: latencyResult }), + { status: 200 } + ); + } + } catch (error) { + return new NextResponse(`Error updating DB! Error: ${error}`, { + status: 500 + }); + } +} diff --git a/src/app/api/manager/productions/[id]/sources/[source_id]/route.ts b/src/app/api/manager/productions/[id]/sources/[source_id]/route.ts new file mode 100644 index 00000000..39d7e4a8 --- /dev/null +++ b/src/app/api/manager/productions/[id]/sources/[source_id]/route.ts @@ -0,0 +1,36 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../../api/manager/auth'; +import { replaceProductionSourceStreamIds } from '../../../../../../../api/manager/productions'; +import { Log } from '../../../../../../../api/logger'; + +type Params = { + id: string; + source_id: string; +}; + +export async function PUT( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + try { + const body = (await request.json()) as { stream_uuids: string[] }; + const prod = await replaceProductionSourceStreamIds( + params.id, + params.source_id, + body.stream_uuids + ); + return new NextResponse(JSON.stringify(prod), { status: 200 }); + } catch (error) { + Log().warn('Could not update production source stream ids', error); + + return new NextResponse(`Error searching DB! Error: ${error}`, { + status: 500 + }); + } +} diff --git a/src/app/api/manager/rendering-engine/html/route.ts b/src/app/api/manager/rendering-engine/html/route.ts new file mode 100644 index 00000000..e643fbbe --- /dev/null +++ b/src/app/api/manager/rendering-engine/html/route.ts @@ -0,0 +1,43 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../api/manager/auth'; +import { createPipelineHtmlSource } from '../../../../../api/ateliereLive/pipelines/renderingengine/renderingengine'; +import { Log } from '../../../../../api/logger'; +import { HTMLSource } from '../../../../../interfaces/renderingEngine'; +import { Production } from '../../../../../interfaces/production'; +import { SourceReference } from '../../../../../interfaces/Source'; + +export type CreateHtmlRequestBody = { + production: Production; + htmlBody: HTMLSource; + inputSlot: number; + source: SourceReference; +}; + +export async function POST(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const data = await request.json(); + const createHtmlRequest = data as CreateHtmlRequestBody; + + return await createPipelineHtmlSource( + createHtmlRequest.production, + createHtmlRequest.inputSlot, + createHtmlRequest.htmlBody, + createHtmlRequest.source + ) + .then((response) => { + return new NextResponse(JSON.stringify(response)); + }) + .catch((error) => { + Log().error(error); + const errorResponse = { + ok: false, + error: error + }; + return new NextResponse(JSON.stringify(errorResponse), { status: 500 }); + }); +} diff --git a/src/app/api/manager/rendering-engine/media/route.ts b/src/app/api/manager/rendering-engine/media/route.ts new file mode 100644 index 00000000..f21e1c5a --- /dev/null +++ b/src/app/api/manager/rendering-engine/media/route.ts @@ -0,0 +1,43 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../api/manager/auth'; +import { createPipelineMediaSource } from '../../../../../api/ateliereLive/pipelines/renderingengine/renderingengine'; +import { Log } from '../../../../../api/logger'; +import { MediaSource } from '../../../../../interfaces/renderingEngine'; +import { Production } from '../../../../../interfaces/production'; +import { SourceReference } from '../../../../../interfaces/Source'; + +export type CreateMediaRequestBody = { + production: Production; + mediaBody: MediaSource; + inputSlot: number; + source: SourceReference; +}; + +export async function POST(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const data = await request.json(); + const createMediaRequest = data as CreateMediaRequestBody; + + return await createPipelineMediaSource( + createMediaRequest.production, + createMediaRequest.inputSlot, + createMediaRequest.mediaBody, + createMediaRequest.source + ) + .then((response) => { + return new NextResponse(JSON.stringify(response)); + }) + .catch((error) => { + Log().error(error); + const errorResponse = { + ok: false, + error: error + }; + return new NextResponse(JSON.stringify(errorResponse), { status: 500 }); + }); +} diff --git a/src/app/api/manager/sources/[ingest_name]/[source_name]/thumbnail/route.ts b/src/app/api/manager/sources/[ingest_name]/[source_name]/thumbnail/route.ts index 926d3e33..fd76649e 100644 --- a/src/app/api/manager/sources/[ingest_name]/[source_name]/thumbnail/route.ts +++ b/src/app/api/manager/sources/[ingest_name]/[source_name]/thumbnail/route.ts @@ -5,6 +5,7 @@ import { getUuidFromIngestName } from '../../../../../../../api/ateliereLive/ingest'; import { isAuthenticated } from '../../../../../../../api/manager/auth'; +import { Log } from '../../../../../../../api/logger'; type Params = { ingest_name: string; @@ -23,16 +24,22 @@ export async function GET( try { const ingestUuid = await getUuidFromIngestName(params.ingest_name); - const sourceId = await getSourceIdFromSourceName( - ingestUuid, - params.source_name + const sourceId = ingestUuid + ? await getSourceIdFromSourceName(ingestUuid, params.source_name) + : 0; + const base64Image = await getSourceThumbnail( + ingestUuid || '', + sourceId || 0 ); - const base64Image = await getSourceThumbnail(ingestUuid, sourceId); if (!base64Image) { return new NextResponse('image not found', { status: 404 }); } return new NextResponse(Buffer.from(base64Image, 'base64')); } catch (e) { + Log().error( + `Error fetching thumbnail for '${params.source_name}' from ingest '${params.ingest_name}':`, + e + ); return new NextResponse(e?.toString(), { status: 404 }); } } diff --git a/src/app/api/manager/srt/[ingest_uuid]/[ingest_source_id]/route.ts b/src/app/api/manager/srt/[ingest_uuid]/[ingest_source_id]/route.ts new file mode 100644 index 00000000..20bd44a5 --- /dev/null +++ b/src/app/api/manager/srt/[ingest_uuid]/[ingest_source_id]/route.ts @@ -0,0 +1,33 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { isAuthenticated } from '../../../../../../api/manager/auth'; +import { deleteSrtSource } from '../../../../../../api/ateliereLive/ingest'; +import { Log } from '../../../../../../api/logger'; + +type Params = { + ingest_uuid: string; + ingest_source_id: number; +}; + +export async function DELETE( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + return await deleteSrtSource(params.ingest_uuid, params.ingest_source_id) + .then((response) => { + return new NextResponse(JSON.stringify(response)); + }) + .catch((error) => { + Log().error(error); + const errorResponse = { + ok: false, + error: 'Failed to delete SRT source' + }; + return new NextResponse(JSON.stringify(errorResponse), { status: 500 }); + }); +} diff --git a/src/app/api/manager/stop/route.ts b/src/app/api/manager/stop/route.ts index 3afc8889..eaa2400f 100644 --- a/src/app/api/manager/stop/route.ts +++ b/src/app/api/manager/stop/route.ts @@ -13,7 +13,6 @@ export async function POST(request: NextRequest): Promise { const { production } = await request.json(); return stopProduction(production) .then((result) => { - console.log(result); return new NextResponse(JSON.stringify(result)); }) .catch((error) => { diff --git a/src/app/api/manager/streams/[id]/route.ts b/src/app/api/manager/streams/[id]/route.ts index 00d44df3..84eb7595 100644 --- a/src/app/api/manager/streams/[id]/route.ts +++ b/src/app/api/manager/streams/[id]/route.ts @@ -1,13 +1,20 @@ import { Params } from 'next/dist/shared/lib/router/utils/route-matcher'; import { NextRequest, NextResponse } from 'next/server'; import { isAuthenticated } from '../../../../../api/manager/auth'; -import { deleteStream } from '../../../../../api/ateliereLive/pipelines/streams/streams'; +import { + deleteStream, + updateStream +} from '../../../../../api/ateliereLive/pipelines/streams/streams'; import { MultiviewSettings } from '../../../../../interfaces/multiview'; import { updateMultiviewForPipeline } from '../../../../../api/ateliereLive/pipelines/multiviews/multiviews'; import { DeleteSourceStep } from '../../../../../interfaces/Source'; import { Result } from '../../../../../interfaces/result'; import { Log } from '../../../../../api/logger'; +export type UpdateStreamRequestBody = { + alignment_ms: number; +}; + export async function DELETE( request: NextRequest, { params }: { params: Params } @@ -133,3 +140,34 @@ export async function DELETE( ); } } + +export async function PATCH( + request: NextRequest, + { params }: { params: Params } +): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const data = await request.json(); + const updateStreamRequest = data as UpdateStreamRequestBody; + + try { + const result = await updateStream( + params.id, + updateStreamRequest.alignment_ms + ); + return new NextResponse(JSON.stringify(result), { + status: 200 + }); + } catch (error) { + return new NextResponse( + JSON.stringify({ message: `Error updating stream! Error: ${error}` }), + { + status: 500 + } + ); + } +} diff --git a/src/app/api/manager/streams/route.ts b/src/app/api/manager/streams/route.ts index 44635884..689dd0f6 100644 --- a/src/app/api/manager/streams/route.ts +++ b/src/app/api/manager/streams/route.ts @@ -18,6 +18,7 @@ export async function POST(request: NextRequest): Promise { const data = await request.json(); const createStreamRequest = data as CreateStreamRequestBody; + return await createStream( createStreamRequest.source, createStreamRequest.production, diff --git a/src/app/api/manager/teardown/route.ts b/src/app/api/manager/teardown/route.ts new file mode 100644 index 00000000..224bb78c --- /dev/null +++ b/src/app/api/manager/teardown/route.ts @@ -0,0 +1,27 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { Log } from '../../../../api/logger'; +import { isAuthenticated } from '../../../../api/manager/auth'; +import { teardown } from '../../../../api/manager/teardown'; + +export async function POST(request: NextRequest): Promise { + if (!(await isAuthenticated())) { + return new NextResponse(`Not Authorized!`, { + status: 403 + }); + } + + const options = await request.json(); + + return teardown(options) + .then((result) => { + return new NextResponse(JSON.stringify(result)); + }) + .catch((error) => { + Log().error(error); + const errorResponse = { + ok: false, + error: 'unexpected' + }; + return new NextResponse(JSON.stringify(errorResponse), { status: 500 }); + }); +} diff --git a/src/app/html_input/page.tsx b/src/app/html_input/page.tsx new file mode 100644 index 00000000..81cfaa52 --- /dev/null +++ b/src/app/html_input/page.tsx @@ -0,0 +1,10 @@ +import { PageProps } from '../../../.next/types/app/html_input/page'; + +export default function HtmlInput({ searchParams: { input } }: PageProps) { + return ( +
+

HTML INPUT

+

{input}

+
+ ); +} diff --git a/src/app/inventory/page.tsx b/src/app/inventory/page.tsx index 0b851641..a762e9da 100644 --- a/src/app/inventory/page.tsx +++ b/src/app/inventory/page.tsx @@ -1,22 +1,5 @@ -import { Suspense } from 'react'; -import HeaderNavigation from '../../components/headerNavigation/HeaderNavigation'; -import { useTranslate } from '../../i18n/useTranslate'; -import { LoadingCover } from '../../components/loader/LoadingCover'; -import Inventory from '../../components/inventory/Inventory'; +import { InventoryPageContent } from '../../components/inventory/InventoryPageContent'; export default function Page() { - const t = useTranslate(); - - return ( - <> - -

- {t('inventory')} -

-
- }> - - - - ); + return ; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index eab1456f..915e472f 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,7 @@ import { Toaster } from 'react-hot-toast'; import DefaultLayout from '../components/layout/DefaultLayout'; import './globals.css'; +import { GlobalContextProvider } from '../contexts/GlobalContext'; export default async function RootLayout({ children @@ -21,7 +22,9 @@ export default async function RootLayout({ } }} /> - {children} + + {children} + ); diff --git a/src/app/page.tsx b/src/app/page.tsx index 30467a92..c1ff2f35 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,37 +1,10 @@ -import React, { Suspense } from 'react'; -import ProductionsList from '../components/productionsList/ProductionsList'; -import { CreateProduction } from '../components/createProduction/CreateProduction'; -import { LoadingCover } from '../components/loader/LoadingCover'; -import Link from 'next/link'; -import { Button } from '../components/button/Button'; -import { useTranslate } from '../i18n/useTranslate'; +import React from 'react'; +import { HomePageContent } from '../components/homePageContent/HomePageContent'; export const dynamic = 'force-dynamic'; function Home() { - const t = useTranslate(); - return ( - <> -
-
-
- - - -
-
-
- -
- }> - {/* @ts-expect-error Async Server Component: https://github.com/vercel/next.js/issues/42292 */} - - -
- - ); + return ; } export default Home; diff --git a/src/app/production/[id]/page.tsx b/src/app/production/[id]/page.tsx index be3112c7..93890279 100644 --- a/src/app/production/[id]/page.tsx +++ b/src/app/production/[id]/page.tsx @@ -1,10 +1,13 @@ 'use client'; -import React, { useEffect, useState, KeyboardEvent } from 'react'; +import React, { + useEffect, + useState, + KeyboardEvent, + useContext, + useMemo +} from 'react'; import { PageProps } from '../../../../.next/types/app/production/[id]/page'; -import SourceListItem from '../../../components/sourceListItem/SourceListItem'; -import FilterOptions from '../../../components/filter/FilterOptions'; -import { AddSource } from '../../../components/addSource/AddSource'; -import { IconX } from '@tabler/icons-react'; +import { AddInput } from '../../../components/addInput/AddInput'; import { useSources } from '../../../hooks/sources/useSources'; import { AddSourceStatus, @@ -12,14 +15,16 @@ import { SourceReference, SourceWithId } from '../../../interfaces/Source'; -import { useGetProduction, usePutProduction } from '../../../hooks/productions'; +import { + useGetProduction, + usePutProduction, + useReplaceProductionSourceStreamIds +} from '../../../hooks/productions'; import { Production } from '../../../interfaces/production'; import { updateSetupItem } from '../../../hooks/items/updateSetupItem'; import { removeSetupItem } from '../../../hooks/items/removeSetupItem'; import { addSetupItem } from '../../../hooks/items/addSetupItem'; import HeaderNavigation from '../../../components/headerNavigation/HeaderNavigation'; -import styles from './page.module.scss'; -import FilterProvider from '../../../components/inventory/FilterContext'; import { useGetPresets } from '../../../hooks/presets'; import { Preset } from '../../../interfaces/preset'; import SourceCards from '../../../components/sourceCards/SourceCards'; @@ -39,17 +44,40 @@ import { AddSourceModal } from '../../../components/modal/AddSourceModal'; import { RemoveSourceModal } from '../../../components/modal/RemoveSourceModal'; import { useDeleteStream, useCreateStream } from '../../../hooks/streams'; import { MonitoringButton } from '../../../components/button/MonitoringButton'; -import { useGetMultiviewPreset } from '../../../hooks/multiviewPreset'; -import { ISource } from '../../../hooks/useDragableItems'; +import { useGetMultiviewLayout } from '../../../hooks/multiviewLayout'; import { useMultiviews } from '../../../hooks/multiviews'; +import SourceList from '../../../components/sourceList/SourceList'; +import { GlobalContext } from '../../../contexts/GlobalContext'; +import { Select } from '../../../components/select/Select'; +import { useGetFirstEmptySlot } from '../../../hooks/useGetFirstEmptySlot'; +import { ConfigureMultiviewButton } from '../../../components/modal/configureMultiviewModal/ConfigureMultiviewButton'; +import { useUpdateSourceInputSlotOnMultiviewLayouts } from '../../../hooks/useUpdateSourceInputSlotOnMultiviewLayouts'; +import { useCheckProductionPipelines } from '../../../hooks/useCheckProductionPipelines'; +import { ISource } from '../../../hooks/useDragableItems'; +import { useUpdateStream } from '../../../hooks/streams'; +import { usePutProductionPipelineSourceAlignmentAndLatency } from '../../../hooks/productions'; +import { useIngestSourceId } from '../../../hooks/ingests'; +import cloneDeep from 'lodash.clonedeep'; +import { + useAddMultiviewersOnRunningProduction, + useRemoveMultiviewersOnRunningProduction, + useUpdateMultiviewersOnRunningProduction +} from '../../../hooks/workflow'; +import { MultiviewSettings } from '../../../interfaces/multiview'; +import { CreateHtmlModal } from '../../../components/modal/renderingEngineModals/CreateHtmlModal'; +import { CreateMediaModal } from '../../../components/modal/renderingEngineModals/CreateMediaModal'; +import { useDeleteHtmlSource } from '../../../hooks/renderingEngine/useDeleteHtmlSource'; +import { useDeleteMediaSource } from '../../../hooks/renderingEngine/useDeleteMediaSource'; +import { useCreateHtmlSource } from '../../../hooks/renderingEngine/useCreateHtmlSource'; +import { useCreateMediaSource } from '../../../hooks/renderingEngine/useCreateMediaSource'; export default function ProductionConfiguration({ params }: PageProps) { const t = useTranslate(); //SOURCES const [sources] = useSources(); - const [filteredSources, setFilteredSources] = useState( - new Map() + const [selectedValue, setSelectedValue] = useState( + t('production.add_other_source_type') ); const [addSourceModal, setAddSourceModal] = useState(false); const [removeSourceModal, setRemoveSourceModal] = useState(false); @@ -61,10 +89,13 @@ export default function ProductionConfiguration({ params }: PageProps) { >(); const [createStream, loadingCreateStream] = useCreateStream(); const [deleteStream, loadingDeleteStream] = useDeleteStream(); + //PRODUCTION const putProduction = usePutProduction(); const getPresets = useGetPresets(); const getProduction = useGetProduction(); + const replaceProductionSourceStreamIds = + useReplaceProductionSourceStreamIds(); const [configurationName, setConfigurationName] = useState(''); const [productionSetup, setProductionSetup] = useState(); const [presets, setPresets] = useState(); @@ -73,20 +104,63 @@ export default function ProductionConfiguration({ params }: PageProps) { productionSetup?.sources.map((prod) => prod._id) || []; //MULTIVIEWS - //TODO: move useGetMultiviewPreset into useMultiviews (refactor) - const getMultiviewPreset = useGetMultiviewPreset(); + const getMultiviewLayout = useGetMultiviewLayout(); const [updateMultiviewViews] = useMultiviews(); + const [updateSourceInputSlotOnMultiviewLayouts, updateMultiviewViewsLoading] = + useUpdateSourceInputSlotOnMultiviewLayouts(); + const [addMultiviewersOnRunningProduction] = + useAddMultiviewersOnRunningProduction(); + const [updateMultiviewersOnRunningProduction] = + useUpdateMultiviewersOnRunningProduction(); + const [removeMultiviewersOnRunningProduction] = + useRemoveMultiviewersOnRunningProduction(); //FROM LIVE API const [pipelines, loadingPipelines, , refreshPipelines] = usePipelines(); const [controlPanels, loadingControlPanels, , refreshControlPanels] = useControlPanels(); + //UI STATE const [inventoryVisible, setInventoryVisible] = useState(false); const [isPresetDropdownHidden, setIsPresetDropdownHidden] = useState(true); const [addSourceStatus, setAddSourceStatus] = useState(); const [deleteSourceStatus, setDeleteSourceStatus] = useState(); + const [isHtmlModalOpen, setIsHtmlModalOpen] = useState(false); + const [isMediaModalOpen, setIsMediaModalOpen] = useState(false); + + // Create source + const [firstEmptySlot] = useGetFirstEmptySlot(); + + const [updateStream, loading] = useUpdateStream(); + + const [getIngestSourceId] = useIngestSourceId(); + + const putProductionPipelineSourceAlignmentAndLatency = + usePutProductionPipelineSourceAlignmentAndLatency(); + + const [checkProductionPipelines] = useCheckProductionPipelines(); + + // Rendering engine + const [deleteHtmlSource, deleteHtmlLoading] = useDeleteHtmlSource(); + const [deleteMediaSource, deleteMediaLoading] = useDeleteMediaSource(); + const [createHtmlSource, createHtmlLoading] = useCreateHtmlSource(); + const [createMediaSource, createMediaLoading] = useCreateMediaSource(); + + const { locked } = useContext(GlobalContext); + + const memoizedProduction = useMemo(() => productionSetup, [productionSetup]); + + const pipelinesAreSelected = productionSetup?.production_settings + ? productionSetup?.production_settings.pipelines.some( + (pipeline) => pipeline.pipeline_id === undefined + ) === false + : false; + + const isAddButtonDisabled = + (selectedValue !== 'HTML' && selectedValue !== 'Media Player') || + locked || + !pipelinesAreSelected; useEffect(() => { refreshPipelines(); @@ -118,14 +192,27 @@ export default function ProductionConfiguration({ params }: PageProps) { }; }); }; + const setSelectedPipelineName = ( pipelineIndex: number, - pipelineName?: string + pipelineName?: string, + id?: string ) => { + const selectedPresetCopy = cloneDeep(selectedPreset); + const foundPipeline = selectedPresetCopy?.pipelines[pipelineIndex]; + if (foundPipeline) { + foundPipeline.outputs = foundPipeline.outputs || []; + foundPipeline.pipeline_name = pipelineName; + foundPipeline.pipeline_id = id; + } + setSelectedPreset(selectedPresetCopy); setProductionSetup((prevState) => { const updatedPipelines = prevState?.production_settings.pipelines; if (!updatedPipelines) return; updatedPipelines[pipelineIndex].pipeline_name = pipelineName; + updatedPipelines[pipelineIndex].pipeline_id = id; + updatedPipelines[pipelineIndex].outputs = + prevState.production_settings.pipelines[pipelineIndex].outputs || []; putProduction(prevState._id, { ...prevState, production_settings: { @@ -143,41 +230,12 @@ export default function ProductionConfiguration({ params }: PageProps) { }); }; - const checkProductionPipelinesAndControlPanels = (production: Production) => { - if (!production.production_settings) return production; - const productionPipelines = production.production_settings?.pipelines; - - const activePipelinesForProduction = pipelines?.filter((pipeline) => - productionPipelines.some( - (productionPipeline) => - productionPipeline.pipeline_name === pipeline.name - ) - ); - const availablePipelines = productionPipelines.map((productionPipeline) => { - const activePipeForProduction = activePipelinesForProduction?.find( - (p) => p.name === productionPipeline.pipeline_name - ); - if (activePipeForProduction?.streams.length === 0) { - return productionPipeline; - } - return productionPipeline; - }); - - return { - ...production, - production_settings: { - ...production.production_settings, - pipelines: availablePipelines - } - }; - }; - const refreshProduction = () => { getProduction(params.id).then((config) => { // check if production has pipelines in use or control panels in use, if so update production const production = config.isActive ? config - : checkProductionPipelinesAndControlPanels(config); + : checkProductionPipelines(config, pipelines); putProduction(production._id, production); setProductionSetup(production); @@ -204,24 +262,19 @@ export default function ProductionConfiguration({ params }: PageProps) { }, []); useEffect(() => { - if (productionSetup && sources) { - const hasMissingSource = productionSetup?.sources.find( - (productionSource) => - !Array.from(sources.values()).find( - (source) => source._id.toString() === productionSource._id - ) - ); - if (hasMissingSource) { - toast.error(t('error.missing_sources_in_db')); - } + if (selectedValue === t('production.source')) { + setInventoryVisible(true); } - - setFilteredSources(sources); - }, [sources]); + }, [selectedValue]); const updatePreset = (preset: Preset) => { if (!productionSetup?._id) return; - putProduction(productionSetup?._id.toString(), { + + const presetMultiviews = preset.pipelines[0].multiviews; + const productionMultiviews = + productionSetup.production_settings.pipelines[0].multiviews; + + const updatedPreset = { ...productionSetup, production_settings: { ...preset, @@ -239,7 +292,56 @@ export default function ProductionConfiguration({ params }: PageProps) { }; }) } - }).then(() => { + }; + + if (productionSetup.isActive && presetMultiviews && productionMultiviews) { + const productionMultiviewsMap = new Map( + productionMultiviews.map((item) => [item.multiview_id, item]) + ); + const presetMultiviewsMap = new Map( + presetMultiviews.map((item) => [item.multiview_id, item]) + ); + + const additions: MultiviewSettings[] = []; + const updates: MultiviewSettings[] = []; + + presetMultiviews.forEach((newItem) => { + const oldItem = productionMultiviewsMap.get(newItem.multiview_id); + + if (!oldItem) { + additions.push(newItem); + } else if (JSON.stringify(oldItem) !== JSON.stringify(newItem)) { + updates.push(newItem); + } + }); + + const removals = productionMultiviews.filter( + (oldItem) => !presetMultiviewsMap.has(oldItem.multiview_id) + ); + + if (additions.length > 0) { + addMultiviewersOnRunningProduction( + (productionSetup?._id.toString(), updatedPreset), + additions + ); + } + + if (updates.length > 0) { + updateMultiviewersOnRunningProduction( + (productionSetup?._id.toString(), updatedPreset), + updates + ); + } + + if (removals.length > 0) { + removeMultiviewersOnRunningProduction( + (productionSetup?._id.toString(), updatedPreset), + removals + ); + } + } + + putProduction(productionSetup?._id.toString(), updatedPreset).then(() => { refreshProduction(); }); }; @@ -249,13 +351,105 @@ export default function ProductionConfiguration({ params }: PageProps) { putProduction(id, productionSetup); }; - const updateSource = ( + const handleSetPipelineSourceSettings = async ( + source: ISource, + data: { + pipeline_uuid: string; + stream_uuid: string; + alignment: number; + latency: number; + }[], + shouldRestart?: boolean, + streamUuids?: string[] + ) => { + if ( + productionSetup?._id && + source?.ingest_name && + source?.ingest_source_name + ) { + data.forEach(({ pipeline_uuid, stream_uuid, alignment, latency }) => { + putProductionPipelineSourceAlignmentAndLatency( + productionSetup._id, + pipeline_uuid, + source.ingest_name, + source.ingest_source_name, + alignment, + latency + ).then(() => refreshProduction()); + + if (productionSetup.isActive) { + updateStream(stream_uuid, alignment); + } + + const updatedProduction = { + ...productionSetup, + productionSettings: { + ...productionSetup.production_settings, + pipelines: productionSetup.production_settings.pipelines.map( + (pipeline) => { + if (pipeline.pipeline_id === pipeline_uuid) { + pipeline.sources?.map(async (s) => { + if ( + s.ingest_source_name === source.ingest_source_name && + s.ingest_name === source.ingest_name + ) { + s.settings.alignment_ms = alignment; + s.settings.max_network_latency_ms = latency; + } + }); + } + return pipeline; + } + ) + } + }; + + setProductionSetup(updatedProduction); + }); + } + + if (shouldRestart && productionSetup && streamUuids) { + const sourceToDeleteFrom = productionSetup.sources.find((source) => + source.stream_uuids?.includes(streamUuids[0]) + ); + const sourceId = await getIngestSourceId( + source.ingest_name, + source.ingest_source_name + ); + deleteStream(streamUuids, productionSetup, sourceId) + .then(() => { + delete sourceToDeleteFrom?.stream_uuids; + }) + .then(() => + setTimeout(async () => { + const result = await createStream( + source, + productionSetup, + source.input_slot + ); + if (result.ok) { + if (result.value.success) { + const newStreamUuids = result.value.streams.map( + (r) => r.stream_uuid + ); + if (sourceToDeleteFrom?._id) { + replaceProductionSourceStreamIds( + productionSetup._id, + sourceToDeleteFrom?._id, + newStreamUuids + ).then(() => refreshProduction()); + } + } + } + }, 1500) + ); + } + }; + + const updateMultiview = ( source: SourceReference, - productionSetup: Production + updatedSetup: Production ) => { - const updatedSetup = updateSetupItem(source, productionSetup); - setProductionSetup(updatedSetup); - putProduction(updatedSetup._id.toString(), updatedSetup); const pipeline = updatedSetup.production_settings.pipelines[0]; pipeline.multiviews?.map((singleMultiview) => { @@ -274,6 +468,16 @@ export default function ProductionConfiguration({ params }: PageProps) { }); }; + const updateSource = ( + source: SourceReference, + productionSetup: Production + ) => { + const updatedSetup = updateSetupItem(source, productionSetup); + setProductionSetup(updatedSetup); + putProduction(updatedSetup._id.toString(), updatedSetup); + updateMultiview(source, updatedSetup); + }; + const updateConfigName = (nameChange: string) => { if (productionSetup?.name === nameChange) { return; @@ -299,7 +503,7 @@ export default function ProductionConfiguration({ params }: PageProps) { toast.error(t('production.missing_multiview')); return; } - const defaultMultiview = await getMultiviewPreset( + const defaultMultiview = await getMultiviewLayout( preset?.default_multiview_reference ); setSelectedPreset(preset); @@ -345,7 +549,7 @@ export default function ProductionConfiguration({ params }: PageProps) { const id = `${preset.name}-${index}-id`; return (
  • { updateSelectedPreset(preset); @@ -362,65 +566,173 @@ export default function ProductionConfiguration({ params }: PageProps) {
  • ); } - function getSourcesToDisplay( - filteredSources: Map - ): React.ReactNode[] { - return Array.from(filteredSources.values()).map((source, index) => { - return ( - { - if (productionSetup && productionSetup.isActive) { - setSelectedSource(source); - setAddSourceModal(true); - } else if (productionSetup) { - const updatedSetup = addSetupItem( - { - _id: source._id.toString(), - label: source.ingest_source_name, - input_slot: getFirstEmptySlot() - }, - productionSetup - ); - if (!updatedSetup) return; - setProductionSetup(updatedSetup); - putProduction(updatedSetup._id.toString(), updatedSetup).then( - () => { - setAddSourceModal(false); - setSelectedSource(undefined); - } - ); - } - }} - /> - ); - }); - } - const getFirstEmptySlot = () => { - if (!productionSetup) throw 'no_production'; - let firstEmptySlot = productionSetup.sources.length + 1; - if (productionSetup.sources.length === 0) { - return firstEmptySlot; + const addSourceAction = async (source: SourceWithId) => { + if (productionSetup && productionSetup.isActive) { + setSelectedSource(source); + setAddSourceModal(true); + } else if (productionSetup) { + const input: SourceReference = { + _id: source._id.toString(), + type: 'ingest_source', + label: source.name || source.ingest_source_name, + input_slot: firstEmptySlot(productionSetup) + }; + + let updatedSetup = addSetupItem(input, productionSetup); + + if (!updatedSetup) return; + + updatedSetup = await updatePipelinesWithSource(updatedSetup, source); + + setProductionSetup(updatedSetup); + await putProduction(updatedSetup._id.toString(), updatedSetup); + + setAddSourceModal(false); + setSelectedSource(undefined); } - for ( - let i = 0; - i < - productionSetup.sources[productionSetup.sources.length - 1].input_slot; - i++ - ) { - if ( - !productionSetup.sources.some((source) => source.input_slot === i + 1) - ) { - firstEmptySlot = i + 1; - break; + }; + + const updatePipelinesWithSource = async ( + productionSetup: Production, + source: SourceWithId + ): Promise => { + const updatedPipelines = await Promise.all( + productionSetup.production_settings.pipelines.map((pipeline) => { + const newSource = { + ingest_name: source.ingest_name, + ingest_source_name: source.ingest_source_name, + settings: { + alignment_ms: pipeline.alignment_ms, + max_network_latency_ms: pipeline.max_network_latency_ms + } + }; + + const exists = pipeline.sources?.some((s) => { + s.ingest_name === newSource.ingest_name && + s.ingest_source_name === newSource.ingest_source_name; + }); + + const updatedSources = exists + ? pipeline.sources + : [...(pipeline.sources || []), newSource]; + + return { + ...pipeline, + sources: updatedSources + }; + }) + ); + + return { + ...productionSetup, + production_settings: { + ...productionSetup.production_settings, + pipelines: updatedPipelines + } + }; + }; + + const addHtmlSource = (height: number, width: number, url: string) => { + if (productionSetup) { + const sourceToAdd: SourceReference = { + type: 'html', + label: `HTML ${firstEmptySlot(productionSetup)}`, + input_slot: firstEmptySlot(productionSetup), + html_data: { + height: height, + url: url, + width: width + } + }; + const updatedSetup = addSetupItem(sourceToAdd, productionSetup); + if (!updatedSetup) return; + setProductionSetup(updatedSetup); + putProduction(updatedSetup._id.toString(), updatedSetup).then(() => { + refreshProduction(); + }); + + if (productionSetup?.isActive && sourceToAdd.html_data) { + createHtmlSource( + productionSetup, + sourceToAdd.input_slot, + sourceToAdd.html_data, + sourceToAdd + ); + } + } + }; + + const addMediaSource = (filename: string) => { + if (productionSetup) { + const sourceToAdd: SourceReference = { + type: 'mediaplayer', + label: `Media Player ${firstEmptySlot(productionSetup)}`, + input_slot: firstEmptySlot(productionSetup), + media_data: { + filename: filename + } + }; + const updatedSetup = addSetupItem(sourceToAdd, productionSetup); + if (!updatedSetup) return; + setProductionSetup(updatedSetup); + putProduction(updatedSetup._id.toString(), updatedSetup).then(() => { + refreshProduction(); + }); + + if (productionSetup?.isActive && sourceToAdd.media_data) { + createMediaSource( + productionSetup, + sourceToAdd.input_slot, + sourceToAdd.media_data, + sourceToAdd + ); } } - return firstEmptySlot; }; + const isDisabledFunction = (source: SourceWithId): boolean => { + return selectedProductionItems?.includes(source._id.toString()); + }; + + const handleOpenModal = (type: 'html' | 'media') => { + if (type === 'html') { + setIsHtmlModalOpen(true); + } else if (type === 'media') { + setIsMediaModalOpen(true); + } + }; + + async function fetchWithRetry( + updatedSetup: Production, + config: { + retries: number; + retryDelay: number; + } + ): Promise { + const { retries, retryDelay } = config; + + for (let attempt = 0; attempt <= retries; attempt++) { + try { + const response = await updateSourceInputSlotOnMultiviewLayouts( + updatedSetup + ); + + if (response) return response; + + throw new Error(`Request failed`); + } catch (error) { + if (attempt === retries) throw error; + + await new Promise((resolve) => + setTimeout(resolve, retryDelay * 2 ** attempt) + ); + } + } + + throw new Error('Max retries reached'); + } + const handleAddSource = async () => { setAddSourceStatus(undefined); if ( @@ -435,17 +747,57 @@ export default function ProductionConfiguration({ params }: PageProps) { ) : false) ) { - const firstEmptySlot = getFirstEmptySlot(); + let updatedSetup = productionSetup; + + for ( + let i = 0; + i < productionSetup.production_settings.pipelines.length; + i++ + ) { + const pipeline = productionSetup.production_settings.pipelines[i]; + + if (!pipeline.sources) { + pipeline.sources = []; + } + + const newSource = { + ingest_name: selectedSource.ingest_name, + ingest_source_name: selectedSource.ingest_source_name, + settings: { + alignment_ms: pipeline.alignment_ms, + max_network_latency_ms: pipeline.max_network_latency_ms + } + }; + + updatedSetup = { + ...productionSetup, + production_settings: { + ...productionSetup.production_settings, + pipelines: productionSetup.production_settings.pipelines.map( + (p, index) => { + if (index === i) { + if (!p.sources) { + p.sources = []; + } + p.sources.push(newSource); + } + return p; + } + ) + } + } as Production; + } + const result = await createStream( selectedSource, - productionSetup, - firstEmptySlot ? firstEmptySlot : productionSetup.sources.length + 1 + updatedSetup, + firstEmptySlot(productionSetup) ); if (!result.ok) { if (!result.value) { setAddSourceStatus({ success: false, - steps: [{ step: 'unexpected', success: false }] + steps: [{ step: 'add_stream', success: false }] }); } else { setAddSourceStatus({ @@ -456,21 +808,32 @@ export default function ProductionConfiguration({ params }: PageProps) { } if (result.ok) { if (result.value.success) { - const sourceToAdd = { + const sourceToAdd: SourceReference = { _id: result.value.streams[0].source_id, + type: 'ingest_source', label: selectedSource.name, stream_uuids: result.value.streams.map((r) => r.stream_uuid), - input_slot: getFirstEmptySlot() + input_slot: firstEmptySlot(productionSetup) }; const updatedSetup = addSetupItem(sourceToAdd, productionSetup); if (!updatedSetup) return; - setProductionSetup(updatedSetup); - putProduction(updatedSetup._id.toString(), updatedSetup).then(() => { + try { + const databaseResult = await fetchWithRetry(updatedSetup, { + retries: 10, + retryDelay: 1000 + }); + setProductionSetup(databaseResult); + updateMultiview(sourceToAdd, databaseResult); refreshProduction(); setAddSourceModal(false); setSelectedSource(undefined); - }); - setAddSourceStatus(undefined); + setAddSourceStatus({ success: true, steps: result.value.steps }); + } catch (error) { + console.error( + 'Failed to update the database after retries:', + error + ); + } } else { setAddSourceStatus({ success: false, steps: result.value.steps }); } @@ -478,13 +841,8 @@ export default function ProductionConfiguration({ params }: PageProps) { } }; - const handleRemoveSource = async () => { - if ( - productionSetup && - productionSetup.isActive && - selectedSourceRef && - selectedSourceRef.stream_uuids - ) { + const handleRemoveSource = async (ingestSource?: SourceWithId) => { + if (productionSetup && productionSetup.isActive && selectedSourceRef) { const multiviews = productionSetup.production_settings.pipelines[0].multiviews; @@ -496,9 +854,75 @@ export default function ProductionConfiguration({ params }: PageProps) { ) ); - if (!viewToUpdate) { - if (!productionSetup.production_settings.pipelines[0].pipeline_id) + if ( + selectedSourceRef.stream_uuids && + selectedSourceRef.stream_uuids.length > 0 + ) { + if (!viewToUpdate) { + if (!productionSetup.production_settings.pipelines[0].pipeline_id) + return; + + const result = await deleteStream( + selectedSourceRef.stream_uuids, + productionSetup, + selectedSourceRef.input_slot + ); + + if (!result.ok) { + if (!result.value) { + setDeleteSourceStatus({ + success: false, + steps: [{ step: 'unexpected', success: false }] + }); + } else { + setDeleteSourceStatus({ success: false, steps: result.value }); + const didDeleteStream = result.value.some( + (step) => step.step === 'delete_stream' && step.success + ); + if (didDeleteStream) { + const updatedSetup = removeSetupItem( + selectedSourceRef, + productionSetup + ); + if (!updatedSetup) return; + try { + const databaseResult = await fetchWithRetry(updatedSetup, { + retries: 10, + retryDelay: 1000 + }); + setProductionSetup(updatedSetup); + updateMultiview(selectedSourceRef, databaseResult); + setSelectedSourceRef(undefined); + } catch (error) { + console.error( + 'Failed to update the database after retries:', + error + ); + } + return; + } + } + return; + } + + const updatedSetup = removeSetupItem( + selectedSourceRef, + productionSetup + ); + + if (!updatedSetup) return; + + updateSourceInputSlotOnMultiviewLayouts(updatedSetup).then( + (result) => { + if (!result) return; + setProductionSetup(updatedSetup); + updateMultiview(selectedSourceRef, result); + setRemoveSourceModal(false); + setSelectedSourceRef(undefined); + } + ); return; + } const result = await deleteStream( selectedSourceRef.stream_uuids, @@ -523,10 +947,11 @@ export default function ProductionConfiguration({ params }: PageProps) { productionSetup ); if (!updatedSetup) return; - setProductionSetup(updatedSetup); - putProduction(updatedSetup._id.toString(), updatedSetup).then( - () => { - setSelectedSourceRef(undefined); + updateSourceInputSlotOnMultiviewLayouts(updatedSetup).then( + (result) => { + if (!result) return; + setProductionSetup(result); + updateMultiview(selectedSourceRef, result); } ); return; @@ -534,58 +959,68 @@ export default function ProductionConfiguration({ params }: PageProps) { } return; } + } - const updatedSetup = removeSetupItem( - selectedSourceRef, - productionSetup - ); - - if (!updatedSetup) return; - - setProductionSetup(updatedSetup); - putProduction(updatedSetup._id.toString(), updatedSetup).then(() => { - setRemoveSourceModal(false); - setSelectedSourceRef(undefined); - }); - return; + if ( + selectedSourceRef.type === 'html' || + selectedSourceRef.type === 'mediaplayer' + ) { + for ( + let i = 0; + i < productionSetup.production_settings.pipelines.length; + i++ + ) { + const pipelineId = + productionSetup.production_settings.pipelines[i].pipeline_id; + if (pipelineId) { + if ( + selectedSourceRef.type === 'html' && + productionSetup.production_settings.pipelines[0].pipeline_id && + productionSetup.production_settings.pipelines[0].pipeline_id !== + '' + ) { + await deleteHtmlSource( + pipelineId, + selectedSourceRef.input_slot, + productionSetup.production_settings.pipelines[0].pipeline_id + ); + } else if ( + selectedSourceRef.type === 'mediaplayer' && + productionSetup.production_settings.pipelines[0].pipeline_id && + productionSetup.production_settings.pipelines[0].pipeline_id !== + '' + ) { + await deleteMediaSource( + pipelineId, + selectedSourceRef.input_slot, + productionSetup.production_settings.pipelines[0].pipeline_id + ); + } + } + } } - const result = await deleteStream( - selectedSourceRef.stream_uuids, + const updatedSetup = removeSetupItem( + selectedSourceRef, productionSetup, - selectedSourceRef.input_slot + ingestSource?.ingest_source_name, + ingestSource?.ingest_name ); - if (!result.ok) { - if (!result.value) { - setDeleteSourceStatus({ - success: false, - steps: [{ step: 'unexpected', success: false }] - }); - } else { - setDeleteSourceStatus({ success: false, steps: result.value }); - const didDeleteStream = result.value.some( - (step) => step.step === 'delete_stream' && step.success - ); - if (didDeleteStream) { - const updatedSetup = removeSetupItem( - selectedSourceRef, - productionSetup - ); - if (!updatedSetup) return; - setProductionSetup(updatedSetup); - putProduction(updatedSetup._id.toString(), updatedSetup); - return; - } - } - return; - } - const updatedSetup = removeSetupItem(selectedSourceRef, productionSetup); if (!updatedSetup) return; - setProductionSetup(updatedSetup); - putProduction(updatedSetup._id.toString(), updatedSetup).then(() => { + updateSourceInputSlotOnMultiviewLayouts(updatedSetup).then((result) => { + if (!result) return; + setProductionSetup(result); + updateMultiview( + { + ...selectedSourceRef, + label: '' + }, + result + ); setRemoveSourceModal(false); setSelectedSourceRef(undefined); + setSelectedSource(undefined); }); } }; @@ -601,11 +1036,21 @@ export default function ProductionConfiguration({ params }: PageProps) { setSelectedSource(undefined); setDeleteSourceStatus(undefined); }; + + const hasSelectedPipelines = () => { + if (!productionSetup?.production_settings?.pipelines?.length) return false; + let allPipesHaveName = true; + productionSetup.production_settings.pipelines.forEach((p) => { + if (!p.pipeline_name) allPipesHaveName = false; + }); + return allPipesHaveName; + }; + return ( <> { @@ -617,6 +1062,7 @@ export default function ProductionConfiguration({ params }: PageProps) { } }} onBlur={() => updateConfigName(configurationName)} + disabled={locked} />
    + { + refreshProduction(); + }} production={productionSetup} - disabled={!selectedPreset ? true : false} + disabled={ + (!selectedPreset ? true : false) || + locked || + !hasSelectedPipelines() + } />
    -
    -
    - -
    -
    - - ) => { - setFilteredSources(new Map(filtered)); - }} - /> - -
    -
      - {getSourcesToDisplay(filteredSources)} - {addSourceModal && selectedSource && ( - - )} -
    -
    + setInventoryVisible(false)} + isDisabledFunc={isDisabledFunction} + locked={locked} + /> + {addSourceModal && (selectedSource || selectedSourceRef) && ( + + )}
    -
    +
    0 && ( { updateProduction(productionSetup._id, updated); }} - onSourceUpdate={( - source: SourceReference, - sourceItem: ISource - ) => { - sourceItem.label = source.label; + onSourceUpdate={(source: SourceReference) => { updateSource(source, productionSetup); }} - onSourceRemoval={(source: SourceReference) => { + onSourceRemoval={async ( + source: SourceReference, + ingestSource?: ISource + ) => { if (productionSetup && productionSetup.isActive) { + setSelectedSource(ingestSource); setSelectedSourceRef(source); setRemoveSourceModal(true); } else if (productionSetup) { const updatedSetup = removeSetupItem( { _id: source._id, + type: source.type, label: source.label, input_slot: source.input_slot }, - productionSetup + productionSetup, + ingestSource?.ingest_source_name, + ingestSource?.ingest_name ); if (!updatedSetup) return; setProductionSetup(updatedSetup); @@ -735,28 +1191,67 @@ export default function ProductionConfiguration({ params }: PageProps) { }); } }} + loading={loading} /> {removeSourceModal && selectedSourceRef && ( handleRemoveSource(selectedSource)} status={deleteSourceStatus} - loading={loadingDeleteStream} + loading={ + loadingDeleteStream || + deleteHtmlLoading || + deleteMediaLoading || + updateMultiviewViewsLoading + } /> )} )} - { - setInventoryVisible(true); - }} - /> +
    + setInventoryVisible(true)} + disabled={ + productionSetup?.production_settings === undefined || + productionSetup.production_settings === null || + locked + } + /> +
    + +
    + + + ); +}; + +export default CustomSwitch; diff --git a/src/components/dragElement/DragItem.tsx b/src/components/dragElement/DragItem.tsx index fdc7cde7..7f7364f8 100644 --- a/src/components/dragElement/DragItem.tsx +++ b/src/components/dragElement/DragItem.tsx @@ -5,7 +5,7 @@ import { ObjectId } from 'mongodb'; import { Production } from '../../interfaces/production'; interface IDrag { - id: ObjectId; + id: ObjectId | string; selectingText: boolean; onMoveItem: (currentId: string, nextId: string) => void; children: ReactElement; @@ -14,11 +14,10 @@ interface IDrag { updateProduction: (updated: Production) => void; productionSetup: Production; } - const DragItem: React.FC = memo( ({ id, - selectingText: selectingText, + selectingText, onMoveItem, children, previousOrder, @@ -28,34 +27,41 @@ const DragItem: React.FC = memo( }) => { const ref = useRef(null); + const oid = typeof id === 'string' ? id : id.toString(); + const [{ isDragging }, connectDrag] = useDrag({ canDrag: !selectingText, type: 'Card', - item: { id }, - collect: (monitor) => { - return { - isDragging: monitor.isDragging() - }; - } + item: { oid }, + collect: (monitor) => ({ + isDragging: monitor.isDragging() + }) }); const [, connectDrop] = useDrop({ accept: 'Card', - hover(hoveredOverItem: { id: ObjectId }) { - if (hoveredOverItem.id !== id && id) { - onMoveItem(hoveredOverItem.id.toString(), id.toString()); + hover(hoveredOverItem: { oid: string }) { + if (hoveredOverItem.oid !== oid) { + onMoveItem(hoveredOverItem.oid.toString(), oid.toString()); } }, drop() { - const isSame = previousOrder.every( - (item, index) => item._id === currentOrder[index]._id - ); + const isSameLength = previousOrder.length === currentOrder.length; + const isSame = isSameLength + ? previousOrder.every( + (item, index) => item._id === currentOrder[index]?._id + ) + : false; + if (!isSame) { - console.log('ORDER CHANGED'); const updatedProduction = { ...productionSetup, - sources: currentOrder + sources: currentOrder.map((source) => ({ + ...source, + _id: source._id || undefined + })) }; + updateProduction(updatedProduction); } } @@ -68,12 +74,12 @@ const DragItem: React.FC = memo( return ( <> - {React.Children.map(children, (child) => { - return React.cloneElement(child, { + {React.Children.map(children, (child) => + React.cloneElement(child, { forwardedRef: ref, style: containerStyle - }); - })} + }) + )} ); } diff --git a/src/components/dropDown/ControlPanelDropDown.tsx b/src/components/dropDown/ControlPanelDropDown.tsx index 5decaa7a..70d3f8f8 100644 --- a/src/components/dropDown/ControlPanelDropDown.tsx +++ b/src/components/dropDown/ControlPanelDropDown.tsx @@ -33,7 +33,7 @@ export default function ControlPanelDropDown({ } else { setSelected(initial); } - }, [initial]); + }, []); const handleAddSelectedControlPanel = (option: string) => { setSelected((prevState) => { diff --git a/src/components/dropDown/PipelineNameDropDown.tsx b/src/components/dropDown/PipelineNameDropDown.tsx index 42c7549c..98d5f704 100644 --- a/src/components/dropDown/PipelineNameDropDown.tsx +++ b/src/components/dropDown/PipelineNameDropDown.tsx @@ -3,11 +3,12 @@ import DropDown from './DropDown'; type PipelineNamesDropDownProps = { label: string; - options?: { option: string; available: boolean }[]; + options?: { option: string; available: boolean; id: string }[]; initial?: string; setSelectedPipelineName: ( pipelineIndex: number, - pipelineName?: string + pipelineName?: string, + id?: string ) => void; pipelineIndex: number; disabled: boolean; @@ -22,7 +23,10 @@ export default function PipelineNamesDropDown({ }: PipelineNamesDropDownProps) { const [selected, setSelected] = useState(initial); useEffect(() => { - setSelectedPipelineName(pipelineIndex, selected); + const id = options?.find((o) => o.option === selected)?.id; + if (selected && id && id !== '') { + setSelectedPipelineName(pipelineIndex, selected, id); + } }, [selected]); const handleSetSelected = (option: string) => { diff --git a/src/components/filter/FilterDropdown.tsx b/src/components/filter/FilterDropdown.tsx index 5e765835..5e049e13 100644 --- a/src/components/filter/FilterDropdown.tsx +++ b/src/components/filter/FilterDropdown.tsx @@ -1,6 +1,6 @@ import React, { ChangeEvent, useEffect, useState } from 'react'; import { useTranslate } from '../../i18n/useTranslate'; -import { SortSelect } from './SortSelect'; +import { Select } from '../select/Select'; import { IconArrowsSort } from '@tabler/icons-react'; function FilterDropdown({ @@ -283,7 +283,8 @@ function FilterDropdown({ {t('inventory_list.sort_by')} - { setSelectedValue(e.target.value); diff --git a/src/components/filter/FilterOptions.tsx b/src/components/filter/FilterOptions.tsx index 23b2bd77..dd874de2 100644 --- a/src/components/filter/FilterOptions.tsx +++ b/src/components/filter/FilterOptions.tsx @@ -4,7 +4,7 @@ import SearchBar from './SearchBar'; import FilterDropdown from './FilterDropdown'; import { ClickAwayListener } from '@mui/base'; import { SourceWithId } from '../../interfaces/Source'; -import { FilterContext } from '../inventory/FilterContext'; +import { FilterContext } from '../../contexts/FilterContext'; type FilterOptionsProps = { onFilteredSources: (sources: Map) => void; @@ -93,7 +93,6 @@ function FilterOptions({ onFilteredSources }: FilterOptionsProps) { } } }; - const filterSources = (tempSet: Map) => { const isFilteringByType = showNdiType || showBmdType || showSrtType || showMediaSourceGeneratorType; diff --git a/src/components/filter/SortSelect.tsx b/src/components/filter/SortSelect.tsx deleted file mode 100644 index beeefa03..00000000 --- a/src/components/filter/SortSelect.tsx +++ /dev/null @@ -1,21 +0,0 @@ -type SortSelectProps = { - value: string; - onChange: (e: React.ChangeEvent) => void; - options: readonly string[]; -}; - -export const SortSelect = ({ value, onChange, options }: SortSelectProps) => { - return ( - - ); -}; diff --git a/src/components/flowSteps/FlowSteps.tsx b/src/components/flowSteps/FlowSteps.tsx new file mode 100644 index 00000000..87a38461 --- /dev/null +++ b/src/components/flowSteps/FlowSteps.tsx @@ -0,0 +1,64 @@ +import { IconCheck, IconInfoCircle, IconX } from '@tabler/icons-react'; +import { FlowStep } from '../../interfaces/production'; +import StatusTooltip from '../tooltip/StatusTooltip'; + +interface FlowStepsProps { + steps: FlowStep[]; +} + +const FlowSteps: React.FC = (props) => { + const { steps } = props; + + return ( +
    + {steps.map((step, i) => { + return ( +
    + {!step.success ? ( +
    +
    +
    + {step.success ? : } +
    + {i + 1 < steps.length && ( +
    + )} +
    +

    {step.step}

    + + + + +
    + ) : ( +
    +
    +
    + {step.success ? : } +
    + {i + 1 < steps.length && ( +
    + )} +
    +

    {step.step}

    +
    + )} +
    + ); + })} +
    + ); +}; + +export default FlowSteps; diff --git a/src/components/footer/AppStatus.tsx b/src/components/footer/AppStatus.tsx deleted file mode 100644 index 59f6dd84..00000000 --- a/src/components/footer/AppStatus.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useTranslate } from '../../i18n/useTranslate'; - -interface IAppStatus { - message?: string; - version?: string; -} - -export default function AppStatus({ version, message }: IAppStatus) { - const t = useTranslate(); - - return ( -

    - {t('application')}:{' '} - - {version ? `${version}` : ''} ({message}) - -

    - ); -} diff --git a/src/components/footer/Footer.tsx b/src/components/footer/Footer.tsx deleted file mode 100644 index 60b628e0..00000000 --- a/src/components/footer/Footer.tsx +++ /dev/null @@ -1,92 +0,0 @@ -'use client'; -import { useEffect, useState, useCallback } from 'react'; -import { useTranslate } from '../../i18n/useTranslate'; -import checkApiConnections from '../../utils/checkApiConnections'; -import checkAppHealth from '../../utils/checkAppHealth'; -import Status from './Status'; -import AppStatus from './AppStatus'; - -interface IObject { - connected?: boolean; - url?: string; -} - -interface IConnection { - database?: IObject; - liveApi?: IObject; - message?: string; -} - -interface IApp { - message?: string; - version?: string; -} - -export default function Footer() { - const [loading, setLoading] = useState(true); - const [connection, setConnection] = useState({}); - const [appHealth, setAppHealth] = useState({}); - - const t = useTranslate(); - - const checkConnections = useCallback(() => { - checkApiConnections() - .then((data) => { - if (JSON.stringify(data) !== JSON.stringify(connection)) - setConnection(data); - if (loading) setLoading(() => false); - }) - .catch(() => { - if (loading) setLoading(false); - setConnection({}); - }); - }, []); - - const checkApp = useCallback(() => { - checkAppHealth() - .then((data) => { - if (JSON.stringify(data) !== JSON.stringify(appHealth)) - setAppHealth(data); - }) - .catch(() => { - setAppHealth({}); - }); - }, []); - - useEffect(() => { - const interval = setInterval(() => { - checkConnections(); - }, 60000); - checkConnections(); - checkApp(); - return () => clearInterval(interval); - }, []); - - const settingsDb = connection?.database; - const settingsLive = connection?.liveApi; - - return ( -
    - {loading ? ( -

    {t('setting_up')}

    - ) : ( - <> - - - - - )} -
    - ); -} diff --git a/src/components/footer/Status.tsx b/src/components/footer/Status.tsx deleted file mode 100644 index 4908e33f..00000000 --- a/src/components/footer/Status.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { useTranslate } from '../../i18n/useTranslate'; - -interface IStatus { - adress: string | undefined; - connected?: boolean; - name: 'system_controller' | 'database'; -} - -export default function Status({ adress, connected, name }: IStatus) { - const t = useTranslate(); - - return ( -

    - {t(name)}:{' '} - {connected ? ( - {t('online')} - ) : ( - - {adress ? t('server_error', { string: adress }) : t('offline')} - - )} -

    - ); -} diff --git a/src/components/headerNavigation/HeaderNavigation.tsx b/src/components/headerNavigation/HeaderNavigation.tsx index 68500db6..5e30abaa 100644 --- a/src/components/headerNavigation/HeaderNavigation.tsx +++ b/src/components/headerNavigation/HeaderNavigation.tsx @@ -1,23 +1,9 @@ -import Link from 'next/link'; -import { useTranslate } from '../../i18n/useTranslate'; +'use client'; export default function HeaderNavigation({ children }: { children: React.ReactNode; }) { - const t = useTranslate(); - return ( -
    -
    - - {t('homepage')} - -
    - {children} -
    - ); + return
    {children}
    ; } diff --git a/src/components/homePageContent/HomePageContent.tsx b/src/components/homePageContent/HomePageContent.tsx new file mode 100644 index 00000000..50a75433 --- /dev/null +++ b/src/components/homePageContent/HomePageContent.tsx @@ -0,0 +1,17 @@ +import { CreateProduction } from '../createProduction/CreateProduction'; +import { Suspense } from 'react'; +import { LoadingCover } from '../loader/LoadingCover'; +import ProductionsList from '../productionsList/ProductionsList'; + +export const HomePageContent = () => { + return ( +
    + +
    + }> + + +
    +
    + ); +}; diff --git a/src/components/icons/Icons.tsx b/src/components/icons/Icons.tsx index 2d1c1404..d35ef391 100644 --- a/src/components/icons/Icons.tsx +++ b/src/components/icons/Icons.tsx @@ -1,7 +1,6 @@ import { IconVideo, IconMicrophone2, - IconArrowRight, IconVideoOff, IconVector, IconVectorOff, @@ -12,7 +11,22 @@ import { IconDeviceTv, IconBrandVlc, IconAppWindow, - IconCast + IconCast, + IconMenuDeep, + IconMenu2, + IconLogout2, + IconBuildingFactory2, + IconListDetails, + IconUserCircle, + IconLock, + IconLockOpen, + IconPlugConnected, + IconPlugConnectedX, + IconRefresh, + IconAlertTriangle, + IconAlertOctagon, + IconPencil, + IconPlus } from '@tabler/icons-react'; interface IClassName { @@ -36,9 +50,6 @@ const pickIcon = { IconVectorOff: ({ className }: IClassName) => ( ), - IconArrowRight: ({ className }: IClassName) => ( - - ), IconCopy: ({ className }: IClassName) => , IconCopyOff: ({ className }: IClassName) => ( @@ -55,11 +66,52 @@ const pickIcon = { IconAppWindow: ({ className }: IClassName) => ( ), - IconCast: ({ className }: IClassName) => + IconCast: ({ className }: IClassName) => , + IconMenuDeep: ({ className }: IClassName) => ( + + ), + IconMenu2: ({ className }: IClassName) => , + IconLogout2: ({ className }: IClassName) => ( + + ), + IconBuildingFactory2: ({ className }: IClassName) => ( + + ), + IconListDetails: ({ className }: IClassName) => ( + + ), + IconUserCircle: ({ className }: IClassName) => ( + + ), + IconLock: ({ className }: IClassName) => , + IconLockOpen: ({ className }: IClassName) => ( + + ), + IconPlugConnected: ({ className }: IClassName) => ( + + ), + IconPlugConnectedX: ({ className }: IClassName) => ( + + ), + IconRefresh: ({ className }: IClassName) => ( + + ), + IconAlertTriangle: ({ className }: IClassName) => ( + + ), + IconAlertOctagon: ({ className }: IClassName) => ( + + ), + IconPencil: ({ className }: IClassName) => ( + + ), + IconPlus: ({ className }: IClassName) => }; +export type PickIconNames = keyof typeof pickIcon; + interface IIcons { - name: keyof typeof pickIcon; + name: PickIconNames; className: string; } diff --git a/src/components/image/ImageComponent.tsx b/src/components/image/ImageComponent.tsx new file mode 100644 index 00000000..7f60e127 --- /dev/null +++ b/src/components/image/ImageComponent.tsx @@ -0,0 +1,112 @@ +'use client'; + +import { + PropsWithChildren, + SyntheticEvent, + useContext, + useEffect, + useRef, + useState +} from 'react'; +import Image from 'next/image'; +import { IconExclamationCircle } from '@tabler/icons-react'; +import { Loader } from '../loader/Loader'; +import { GlobalContext } from '../../contexts/GlobalContext'; +import { Type } from '../../interfaces/Source'; +interface ImageComponentProps extends PropsWithChildren { + src?: string; + alt?: string; + type?: Type; + className?: string; +} + +const ImageComponent: React.FC = (props) => { + const { src, alt = 'Image', children, type, className } = props; + const { imageRefetchKey } = useContext(GlobalContext); + const [imgSrc, setImgSrc] = useState(); + const [loaded, setLoaded] = useState(false); + const [loading, setLoading] = useState(false); + const [error, setError] = useState>(); + const timeout = useRef>(); + + const refetchImage = () => { + setImgSrc(`${src}?${imageRefetchKey}`); + setError(undefined); + setLoading(true); + clearTimeout(timeout.current); + timeout.current = setTimeout(() => setLoading(false), 500); + }; + useEffect(() => { + refetchImage(); + }, [imageRefetchKey]); + + useEffect(() => { + setError(undefined); + setImgSrc(`${src}?${imageRefetchKey}`); + }, [src]); + + useEffect(() => { + return () => { + clearTimeout(timeout.current); + }; + }, []); + return ( + <> + {(!type || type === 'ingest_source') && src && ( +
    + {((!imgSrc || error) && ( + + )) || ( + <> + {alt} { + setError(undefined); + setLoaded(false); + }} + onLoadingComplete={() => { + setLoaded(true); + }} + onError={(err) => { + setError(err); + }} + placeholder="empty" + width={0} + height={0} + sizes="20vh" + style={{ + width: 'auto', + height: '100%' + }} + /> + + + )} + {children} +
    + )} + {(type === 'html' || type === 'mediaplayer') && ( + +

    + {type === 'html' ? 'HTML' : 'Media Player'} +

    +
    + )} + + ); +}; +export default ImageComponent; diff --git a/src/components/input/Input.tsx b/src/components/input/Input.tsx new file mode 100644 index 00000000..9c3431e9 --- /dev/null +++ b/src/components/input/Input.tsx @@ -0,0 +1,37 @@ +interface InputProps { + className?: string; + type?: 'text' | 'number'; + value: string | number | undefined; + name?: string; + placeholder?: string; + error?: boolean; + disabled?: boolean; + onChange: (e: React.ChangeEvent) => void; +} + +const Input = ({ + className, + type, + value, + name, + placeholder, + error, + disabled, + onChange +}: InputProps) => { + return ( + + ); +}; + +export default Input; diff --git a/src/components/inventory/EditViewContext.tsx b/src/components/inventory/EditViewContext.tsx index 182f2eac..9657b9c3 100644 --- a/src/components/inventory/EditViewContext.tsx +++ b/src/components/inventory/EditViewContext.tsx @@ -17,6 +17,7 @@ import { } from '../../interfaces/Source'; import { API_SECRET_KEY } from '../../utils/constants'; import { zeroBased } from './editView/AudioChannels/utils'; +import { ResourcesSrt } from '../../../types/ateliere-live'; export interface IInput { name: string; @@ -39,6 +40,7 @@ interface IContext { videoStream: VideoStream; audioStream: AudioStream; sourceMetadata: IReadOnlyMetadata; + srtMetaData?: ResourcesSrt; } export const EditViewContext = createContext({ @@ -51,7 +53,8 @@ export const EditViewContext = createContext({ isSame: true, videoStream: {}, audioStream: {}, - sourceMetadata: {} + sourceMetadata: {}, + srtMetaData: undefined }); export default function Context({ @@ -139,6 +142,7 @@ export default function Context({ ingestServer: source.ingest_name, originalName: source.ingest_source_name }; + const srtMetaData = source.srt; return (
    { e.preventDefault(); diff --git a/src/components/inventory/Inventory.tsx b/src/components/inventory/Inventory.tsx index dacb2623..8e31e653 100644 --- a/src/components/inventory/Inventory.tsx +++ b/src/components/inventory/Inventory.tsx @@ -1,26 +1,39 @@ 'use client'; import { useEffect, useState } from 'react'; -import { useSources } from '../../hooks/sources/useSources'; +import { useSources, useUpdateSources } from '../../hooks/sources/useSources'; import { useSetSourceToPurge } from '../../hooks/sources/useSetSourceToPurge'; -import FilterOptions from '../../components/filter/FilterOptions'; -import SourceListItem from '../../components/sourceListItem/SourceListItem'; -import { SourceWithId } from '../../interfaces/Source'; +import { SourceWithId, SrtSource } from '../../interfaces/Source'; import EditView from './editView/EditView'; -import FilterContext from './FilterContext'; -import styles from './Inventory.module.scss'; +import SourceList from '../sourceList/SourceList'; +import { useTranslate } from '../../i18n/useTranslate'; +import { useRemoveInventorySourceItem } from '../../hooks/sources/useRemoveInventorySource'; +import { useCreateSrtSource } from '../../hooks/sources/useCreateSrtSource'; +import { AddSrtModal } from '../modal/addSrtModal/AddSrtModal'; +import { IconReload } from '@tabler/icons-react'; +import { Loader } from '../loader/Loader'; -export default function Inventory() { - const [removeInventorySource, reloadList] = useSetSourceToPurge(); +export default function Inventory({ locked }: { locked: boolean }) { + const [showSrtModal, setShowSrtModal] = useState(false); const [updatedSource, setUpdatedSource] = useState< SourceWithId | undefined >(); - const [sources] = useSources(reloadList, updatedSource); + const [refreshKey, setRefreshKey] = useState(0); const [currentSource, setCurrentSource] = useState(); - const [filteredSources, setFilteredSources] = - useState>(sources); + const [isCreateSuccessful, setIsCreateSuccessful] = useState(false); - const inventoryVisible = true; + const [purgeInventorySource, reloadList] = useSetSourceToPurge(); + const [removeInventorySourceItem, reloadInventoryList] = + useRemoveInventorySourceItem(); + const [createSrtSource, reloadSourceList] = useCreateSrtSource(); + const [sources, loadingSources] = useSources( + reloadList || reloadInventoryList || reloadSourceList || !!refreshKey, + updatedSource, + refreshKey + ); + const [updateSources, updateSourcesLoading] = useUpdateSources(); + const [createSrtError, setCreateSrtError] = useState(''); + const t = useTranslate(); useEffect(() => { if (updatedSource && typeof updatedSource !== 'boolean') { @@ -29,77 +42,109 @@ export default function Inventory() { }, [updatedSource]); useEffect(() => { - if (reloadList) { + if (reloadList || reloadInventoryList) { setCurrentSource(null); } - }, [reloadList]); + }, [reloadList, reloadInventoryList]); const editSource = (source: SourceWithId) => { - setCurrentSource(() => source); + setCurrentSource(source); }; - function getSourcesToDisplay( - filteredSources: Map - ): React.ReactNode { - return Array.from(filteredSources.values()).map((source, index) => { - if (source.status !== 'purge') { - return ( - { - editSource(source); - }} - /> + const handleCreateSrtSource = ( + ingestUuid: string, + srtPayload: SrtSource, + callback: () => void + ) => { + setCreateSrtError(''); + createSrtSource(ingestUuid, srtPayload) + .then(() => { + callback(); + }) + .finally(() => { + setIsCreateSuccessful(true); + }) + .catch((e) => { + const errorMessageString = String(e.message); + const isErrorMessageStringUndefined = + errorMessageString === 'undefined'; + setCreateSrtError( + t('inventory_list.generic_error') + + (isErrorMessageStringUndefined ? '' : ': ' + errorMessageString) ); - } - }); - } + }); + }; + + const handleRefreshInventory = async () => { + await updateSources(); + setRefreshKey((prev) => prev + 1); + }; + + const handleCloseModal = () => { + setCreateSrtError(''); + setShowSrtModal(false); + }; return ( - -
    -
    +
    + + +
    +
    + {currentSource ? ( -
    +
    setUpdatedSource(source)} close={() => setCurrentSource(null)} - removeInventorySource={(source) => removeInventorySource(source)} + purgeInventorySource={purgeInventorySource} + removeInventorySourceItem={removeInventorySourceItem} />
    ) : null}
    - + + ); } diff --git a/src/components/inventory/InventoryPageContent.tsx b/src/components/inventory/InventoryPageContent.tsx new file mode 100644 index 00000000..90fa6f40 --- /dev/null +++ b/src/components/inventory/InventoryPageContent.tsx @@ -0,0 +1,29 @@ +'use client'; + +import { Suspense, useContext } from 'react'; +import { useTranslate } from '../../i18n/useTranslate'; +import Inventory from './Inventory'; +import { GlobalContext } from '../../contexts/GlobalContext'; +import HeaderNavigation from '../headerNavigation/HeaderNavigation'; + +export const InventoryPageContent = () => { + const t = useTranslate(); + const { locked } = useContext(GlobalContext); + + return ( + <> + +
    +
    +

    + {t('inventory')} +

    +
    +
    +
    + + + + + ); +}; diff --git a/src/components/inventory/editView/AudioChannels/AudioChannels.tsx b/src/components/inventory/editView/AudioChannels/AudioChannels.tsx index afe3b8d3..45cbeffa 100644 --- a/src/components/inventory/editView/AudioChannels/AudioChannels.tsx +++ b/src/components/inventory/editView/AudioChannels/AudioChannels.tsx @@ -11,9 +11,15 @@ import { channel, mapAudio } from '../../../../utils/audioMapping'; interface IAudioChannels { source: Source; + locked: boolean; + setDuplicateAudioValues: (values: boolean) => void; } -export default function AudioChannels({ source }: IAudioChannels) { +export default function AudioChannels({ + source, + locked, + setDuplicateAudioValues +}: IAudioChannels) { type TOutputs = 'audio_mapping.outL' | 'audio_mapping.outR'; const t = useTranslate(); const outputs: TOutputs[] = ['audio_mapping.outL', 'audio_mapping.outR']; @@ -24,6 +30,7 @@ export default function AudioChannels({ source }: IAudioChannels) { } = useContext(EditViewContext); const [error, setError] = useState(''); + const [duplicateError, setDuplicateError] = useState(''); const [outputRows, setRows] = useState<{ id: string; value: string }[][]>([]); const { number_of_channels: numberOfChannels = 0 } = audioStream || {}; @@ -33,6 +40,22 @@ export default function AudioChannels({ source }: IAudioChannels) { channelsInArray.push(i); } + useEffect(() => { + const isDuplicate = outputRows.some((row, rowIndex) => + row.some((cell, cellIndex) => + outputRows.some((innerRow, innerRowIndex) => + innerRow.some((innerCell, innerCellIndex) => + rowIndex !== innerRowIndex || cellIndex !== innerCellIndex + ? cell.value === innerCell.value && cell.value !== '' + : false + ) + ) + ) + ); + + setDuplicateAudioValues(isDuplicate); + }, [outputRows, setDuplicateAudioValues]); + useEffect(() => { let array = [channelsInArray.map(() => channel(''))]; @@ -188,9 +211,11 @@ export default function AudioChannels({ source }: IAudioChannels) { }); if (isAlreadyUsed) { - return setError(() => + setDuplicateError(() => t('audio_mapping.alreadyUsed', { value: e.target.value }) ); + } else { + setDuplicateError(''); } if (Number(e.target.value) > max) { @@ -227,9 +252,11 @@ export default function AudioChannels({ source }: IAudioChannels) { rowIndex={rowIndex} max={max} updateRows={updateRows} + locked={locked} />
    ))} +
    ); diff --git a/src/components/inventory/editView/AudioChannels/NumberInput.tsx b/src/components/inventory/editView/AudioChannels/NumberInput.tsx index 66910169..001a782b 100644 --- a/src/components/inventory/editView/AudioChannels/NumberInput.tsx +++ b/src/components/inventory/editView/AudioChannels/NumberInput.tsx @@ -9,6 +9,7 @@ interface IInputRow { value: string; max: number; isDisabled: boolean; + duplicateError: boolean; updateRows: (e: IEvent) => void; } @@ -16,6 +17,7 @@ export default function InputRow({ value, max, isDisabled, + duplicateError, updateRows }: IInputRow) { return ( @@ -26,9 +28,11 @@ export default function InputRow({ max={max} onChange={updateRows} disabled={isDisabled} - className={`w-[100%] h-[100%] appearance-none text-black text-center ${ - styles.numberInput - } ${isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'}`} + className={`w-[100%] h-[100%] appearance-none text-black text-center ${ + duplicateError ? 'bg-red-400' : '' + } ${styles.numberInput} ${ + isDisabled ? 'cursor-not-allowed' : 'cursor-pointer' + }`} /> ); } diff --git a/src/components/inventory/editView/AudioChannels/Outputs.tsx b/src/components/inventory/editView/AudioChannels/Outputs.tsx index 335afed5..5730b035 100644 --- a/src/components/inventory/editView/AudioChannels/Outputs.tsx +++ b/src/components/inventory/editView/AudioChannels/Outputs.tsx @@ -17,8 +17,9 @@ interface IOutput { outputRows: IContents[][]; rowIndex: number; max: number; - updateRows?: (e: IEvent, rowIndex: number, index: number, id: string) => void; small?: boolean; + locked: boolean; + updateRows?: (e: IEvent, rowIndex: number, index: number, id: string) => void; } export default function Outputs({ @@ -26,9 +27,37 @@ export default function Outputs({ outputRows, rowIndex, max, - updateRows, - small = false + small = false, + locked, + updateRows }: IOutput) { + const findDuplicateValues = () => { + const duplicateOutputIndices: number[] = []; + const seenOutputs = new Set(); + + contents.forEach((output, index) => { + if (seenOutputs.has(output.value) && output.value !== '') { + duplicateOutputIndices.push(index); + + // Also include the first occurrence if it's not already included + const firstIndex = contents.findIndex( + (item) => item.value === output.value + ); + if (!duplicateOutputIndices.includes(firstIndex)) { + duplicateOutputIndices.push(firstIndex); + } + } else if (output.value !== '') { + seenOutputs.add(output.value); + } + }); + + return { + duplicateOutputIndices + }; + }; + + const { duplicateOutputIndices } = findDuplicateValues(); + return (
    {contents.map(({ value, id }, index) => { @@ -50,9 +79,10 @@ export default function Outputs({ } relative ${styles.checkbox}`} > updateRows && updateRows(e, rowIndex, index, id) } diff --git a/src/components/inventory/editView/EditView.tsx b/src/components/inventory/editView/EditView.tsx index 80d498a3..3a995ad1 100644 --- a/src/components/inventory/editView/EditView.tsx +++ b/src/components/inventory/editView/EditView.tsx @@ -1,61 +1,52 @@ -import Image from 'next/image'; import { getSourceThumbnail } from '../../../utils/source'; -import { useMemo, useState } from 'react'; import EditViewContext from '../EditViewContext'; import GeneralSettings from './GeneralSettings'; import { SourceWithId } from '../../../interfaces/Source'; import UpdateButtons from './UpdateButtons'; import AudioChannels from './AudioChannels/AudioChannels'; -import { IconExclamationCircle } from '@tabler/icons-react'; +import ImageComponent from '../../image/ImageComponent'; +import { useState } from 'react'; export default function EditView({ source, updateSource, close, - removeInventorySource + purgeInventorySource, + removeInventorySourceItem, + locked }: { source: SourceWithId; updateSource: (source: SourceWithId) => void; close: () => void; - removeInventorySource: (source: SourceWithId) => void; + purgeInventorySource: (source: SourceWithId) => void; + removeInventorySourceItem: (id: string) => Promise; + locked: boolean; }) { - const [loaded, setLoaded] = useState(false); - const src = useMemo(() => getSourceThumbnail(source), [source]); + const [duplicateAudioValues, setDuplicateAudioValues] = useState(false); return ( -
    - {source.status === 'gone' ? ( -
    - -
    - ) : ( - Preview Thumbnail setLoaded(true)} - placeholder="empty" - width={300} - height={0} - style={{ - objectFit: 'contain' - }} - /> - )} - - +
    +
    + +
    +
    -
    - +
    +
    ); diff --git a/src/components/inventory/editView/GeneralSettings.tsx b/src/components/inventory/editView/GeneralSettings.tsx index ae2c4c66..30ee82ec 100644 --- a/src/components/inventory/editView/GeneralSettings.tsx +++ b/src/components/inventory/editView/GeneralSettings.tsx @@ -1,18 +1,23 @@ import { useContext } from 'react'; import { EditViewContext, IInput } from '../EditViewContext'; -import { FilterContext } from '../FilterContext'; +import { FilterContext } from '../../../contexts/FilterContext'; import { useTranslate } from '../../../i18n/useTranslate'; import SelectOptions from './SelectOptions'; import { getHertz } from '../../../utils/stream'; import videoSettings from '../../../utils/videoSettings'; -export default function GeneralSettings() { +type GeneralSettingsProps = { + locked: boolean; +}; + +export default function GeneralSettings({ locked }: GeneralSettingsProps) { const { input: [input, setInput], saved: [saved, setSaved], videoStream, audioStream, - sourceMetadata + sourceMetadata, + srtMetaData } = useContext(EditViewContext); const t = useTranslate(); @@ -29,6 +34,12 @@ export default function GeneralSettings() { })); }; + const getValueWithBackup = (value?: string | number) => { + const backedUpValue = value || t('missing'); + const classNameString = value ? '' : 'italic text-light'; + return

    {backedUpValue}

    ; + }; + const { height, width, frame_rate: frameRate } = videoStream; const { sample_rate: sampleRate } = audioStream; const { ingestServer, originalName } = sourceMetadata; @@ -41,7 +52,12 @@ export default function GeneralSettings() { type="text" value={input.name} onChange={(e) => onChange('name', e.target.value)} - className="cursor-pointer ml-5 border justify-center text-sm rounded-lg w-full pl-2 pt-1 pb-1 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:ring-blue-500 focus:border-blue-500" + className={`${ + locked + ? 'pointer-events-none bg-gray-700/50 border-gray-600/50 placeholder-gray-400/50 text-p/50' + : 'pointer-events-auto bg-gray-700 border-gray-600 placeholder-gray-400 text-p' + } 'cursor-pointer ml-5 border justify-center text-sm rounded-lg w-full pl-2 pt-1 pb-1 focus:ring-blue-500 focus:border-blue-500'`} + disabled={locked} />
    @@ -50,15 +66,17 @@ export default function GeneralSettings() { name="type" options={types} selected={input.type} + disabled={locked} onChange={(e) => onChange('type', e.target.value.toLowerCase())} />
    onChange('location', e.target.value.toLowerCase())} - selected={input.location} options={locations} + selected={input.location} + disabled={locked} + onChange={(e) => onChange('location', e.target.value.toLowerCase())} />
    @@ -79,19 +97,67 @@ export default function GeneralSettings() {
    - {height && width && ( + {height !== undefined && width !== undefined && (

    {t('video')}

    {videoSettings(width, height, frameRate)}

    )} - {sampleRate && ( + {sampleRate !== undefined && (

    {t('audio')}

    {getHertz(sampleRate)}

    )} + + {srtMetaData && ( + <> +

    + {t('inventory_list.srt_metadata')} +

    +
    +

    {t('preset.mode')}

    + {getValueWithBackup(srtMetaData.srt_mode)} +
    +
    +

    + {t('preset.video_format')} +

    + {getValueWithBackup(srtMetaData.video_format)} +
    +
    +

    + {t('inventory_list.latency')} +

    + {getValueWithBackup(srtMetaData.latency_ms)} +
    +
    +

    + {srtMetaData.srt_mode === 'listener' + ? t('inventory_list.local_ip') + : t('inventory_list.remote_ip')} +

    + {getValueWithBackup( + srtMetaData.srt_mode === 'listener' + ? srtMetaData.local_ip + : srtMetaData.remote_ip + )} +
    +
    +

    + {srtMetaData.srt_mode === 'listener' + ? t('inventory_list.local_port') + : t('inventory_list.remote_port')} +

    + {getValueWithBackup( + srtMetaData.srt_mode === 'listener' + ? srtMetaData.local_port + : srtMetaData.remote_port + )} +
    + + )}
    ); } diff --git a/src/components/inventory/editView/SelectOptions.tsx b/src/components/inventory/editView/SelectOptions.tsx index 05f31b98..e8384f9d 100644 --- a/src/components/inventory/editView/SelectOptions.tsx +++ b/src/components/inventory/editView/SelectOptions.tsx @@ -3,28 +3,37 @@ import { useTranslate } from '../../../i18n/useTranslate'; import { en } from '../../../i18n/locales/en'; import capitalize from '../../../utils/capitalize'; -export default function SelectOptions({ - name, - onChange, - options, - selected -}: { +export type SelectOptionsProps = { name: keyof typeof en; - onChange: (e: { target: { value: string } }) => void; options: string[]; selected: string; -}) { + disabled?: boolean; + onChange: (e: { target: { value: string } }) => void; +}; + +export default function SelectOptions({ + name, + options, + selected, + disabled, + onChange +}: SelectOptionsProps) { const t = useTranslate(); return ( <>

    {t(name)}

    {options.map((option: string, index: number) => ( diff --git a/src/components/inventory/editView/UpdateButtons.tsx b/src/components/inventory/editView/UpdateButtons.tsx index 1f3309da..16c2b94d 100644 --- a/src/components/inventory/editView/UpdateButtons.tsx +++ b/src/components/inventory/editView/UpdateButtons.tsx @@ -6,16 +6,25 @@ import styles from './animation.module.scss'; import { Loader } from '../../loader/Loader'; import { SourceWithId } from '../../../interfaces/Source'; import { IconTrash } from '@tabler/icons-react'; +import { useDeleteSrtSource } from '../../../hooks/sources/useDeleteSource'; + +type UpdateButtonsProps = { + source: SourceWithId; + purgeInventorySource: (source: SourceWithId) => void; + removeInventorySourceItem: (id: string) => Promise; + close: () => void; + locked: boolean; + duplicateAudioValues: boolean; +}; export default function UpdateButtons({ + source, close, - removeInventorySource, - source -}: { - close: () => void; - removeInventorySource: (source: SourceWithId) => void; - source: SourceWithId; -}) { + purgeInventorySource, + removeInventorySourceItem, + locked, + duplicateAudioValues +}: UpdateButtonsProps) { const t = useTranslate(); const { saved: [saved], @@ -23,9 +32,20 @@ export default function UpdateButtons({ loading } = useContext(EditViewContext); + const [deleteSrtSource, deleteSrtLoading] = useDeleteSrtSource(); + + const handleRemoveSource = () => { + if (source.ingest_type.toUpperCase() === 'SRT') { + deleteSrtSource(source.ingest_name, source.ingest_source_name); + removeInventorySourceItem(source._id.toString()); + } else { + purgeInventorySource(source); + } + }; + return (
    -
    +
    {t('saved')}
    @@ -35,16 +55,34 @@ export default function UpdateButtons({ - + ); +}; diff --git a/src/components/modal/AddSourceModal.tsx b/src/components/modal/AddSourceModal.tsx index bacb9656..f7ed7e2d 100644 --- a/src/components/modal/AddSourceModal.tsx +++ b/src/components/modal/AddSourceModal.tsx @@ -12,6 +12,7 @@ type AddSourceModalProps = { onConfirm: () => void; status?: AddSourceStatus; loading: boolean; + locked: boolean; }; export function AddSourceModal({ @@ -20,14 +21,14 @@ export function AddSourceModal({ onAbort, onConfirm, status, - loading + loading, + locked }: AddSourceModalProps) { const t = useTranslate(); return (

    {t('workflow.add_source_modal', { name })}

    -
    {status && }
    + +
    +
    + handleSaveAlignmentAndLatency(true)} + /> +
    + ); +} diff --git a/src/components/modal/Modal.tsx b/src/components/modal/Modal.tsx index 8b3122d3..a06d2a5c 100644 --- a/src/components/modal/Modal.tsx +++ b/src/components/modal/Modal.tsx @@ -6,35 +6,41 @@ import { Modal as BaseModal } from '@mui/base'; type BaseModalProps = { open: boolean; forwardRef?: LegacyRef | null; - outsideClick: () => void; + outsideClick?: () => void; + className?: string; }; export type ModalProps = BaseModalProps & PropsWithChildren; -export function Modal({ open, children, outsideClick }: ModalProps) { +export function Modal({ open, children, outsideClick, className }: ModalProps) { const element = useRef(null); useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (element.current && !element.current.contains(event.target as Node)) { - outsideClick(); - } - }; + if (outsideClick) { + const handleClickOutside = (event: MouseEvent) => { + if ( + element.current && + !element.current.contains(event.target as Node) + ) { + outsideClick(); + } + }; - document.addEventListener('click', handleClickOutside, true); - return () => { - document.removeEventListener('click', handleClickOutside, true); - }; + document.addEventListener('click', handleClickOutside, true); + return () => { + document.removeEventListener('click', handleClickOutside, true); + }; + } }, [outsideClick]); return (
    {children}
    diff --git a/src/components/modal/RestartStreamModal.tsx b/src/components/modal/RestartStreamModal.tsx new file mode 100644 index 00000000..bda39b9e --- /dev/null +++ b/src/components/modal/RestartStreamModal.tsx @@ -0,0 +1,51 @@ +import { Modal } from './Modal'; +import { useTranslate } from '../../i18n/useTranslate'; +import { Loader } from '../loader/Loader'; +import { Button } from '../button/Button'; + +type RestartStreamModalProps = { + open: boolean; + loading: boolean; + onAbort: () => void; + onConfirm: () => void; +}; + +export function RestartStreamModal({ + open, + loading, + onAbort, + onConfirm +}: RestartStreamModalProps) { + const t = useTranslate(); + + const handleRestart = () => { + onConfirm(); + onAbort(); + }; + + return ( + +

    + {t('configure_alignment_latency.restart_stream_info')} +

    +
    + + +
    +
    + ); +} diff --git a/src/components/modal/UpdateMultiviewersModal.tsx b/src/components/modal/UpdateMultiviewersModal.tsx new file mode 100644 index 00000000..526b12b2 --- /dev/null +++ b/src/components/modal/UpdateMultiviewersModal.tsx @@ -0,0 +1,37 @@ +import { useTranslate } from '../../i18n/useTranslate'; +import { Button } from '../button/Button'; +import { Modal } from './Modal'; + +type UpdateMultiviewersModalProps = { + open: boolean; + onAbort: () => void; + onConfirm: () => void; +}; + +export function UpdateMultiviewersModal({ + open, + onAbort, + onConfirm +}: UpdateMultiviewersModalProps) { + const t = useTranslate(); + return ( + +
    +

    {t('preset.confirm_update_multiviewers')}

    + +
    + + + +
    +
    +
    + ); +} diff --git a/src/components/modal/addSrtModal/AddSrtModal.tsx b/src/components/modal/addSrtModal/AddSrtModal.tsx new file mode 100644 index 00000000..e07d160d --- /dev/null +++ b/src/components/modal/addSrtModal/AddSrtModal.tsx @@ -0,0 +1,550 @@ +'use client'; +import { useTranslate } from '../../../i18n/useTranslate'; +import { Modal } from '../../modal/Modal'; +import { Select } from '../../select/Select'; +import { useState, useEffect } from 'react'; +import { Button } from '../../button/Button'; +import { SrtSource } from '../../../interfaces/Source'; +import { Loader } from '../../loader/Loader'; +import { useIngests } from '../../../hooks/ingests'; +import Input from '../../input/Input'; +import { ResourcesSourceResponse } from '../../../../types/ateliere-live'; +import { useIngestSources } from '../../../hooks/ingests'; +import toast from 'react-hot-toast'; + +type AddSrtModalProps = { + open: boolean; + loading?: boolean; + onAbort: () => void; + onConfirm: ( + ingestUuid: string, + srtPayload: SrtSource, + callback: () => void + ) => void; + createSrtError?: string | null; + createSrtSuccessful?: boolean; +}; + +type SelectOptions = 'Caller' | 'Listener'; + +export function AddSrtModal({ + open, + loading, + onAbort, + onConfirm, + createSrtError, + createSrtSuccessful +}: AddSrtModalProps) { + const ingests = useIngests(); + const t = useTranslate(); + + const [ingestUuid, setIngestUuid] = useState(''); + const [ingestName, setIngestName] = useState(''); + const [mode, setMode] = useState('Listener'); + const [localIp, setLocalIp] = useState('0.0.0.0'); + const [localPort, setLocalPort] = useState(1234); + const [remoteIp, setRemoteIp] = useState('127.0.0.1'); + const [remotePort, setRemotePort] = useState(1234); + const [latency, setLatency] = useState(120); + const [name, setName] = useState('My SRT source'); + const [passphrase, setPassphrase] = useState(''); + const [isPassphraseError, setIsPassphraseError] = useState(false); + const [isNameError, setIsNameError] = useState(false); + const [isIngestNameError, setIsIngestNameError] = useState(false); + const [isLocalPortError, setIsLocalPortError] = useState(false); + const [isRemotePortError, setIsRemotePortError] = useState(false); + const [isLocalIpError, setIsLocalIpError] = useState(false); + const [isRemoteIpError, setIsRemoteIpError] = useState(false); + const [isDuplicateNameError, setIsDuplicateNameError] = + useState(false); + const [getIngestSources, ingestSourcesLoading] = useIngestSources(); + + const [isPortAlreadyInUseError, setIsPortAlreadyInUseError] = + useState(false); + const [srtPayload, setSrtPayload] = useState({ + latency_ms: latency, + local_ip: localIp, + local_port: localPort, + mode: 'listener', + name: name, + passphrase: passphrase, + remote_ip: remoteIp, + remote_port: remotePort + }); + + useEffect(() => { + if (ingestName !== '') { + const selectedIngest = ingests.find( + (ingest) => + ingest.name.toLowerCase() === ingestName.toLowerCase().trim() + ); + + if (selectedIngest) { + setIngestUuid(selectedIngest.uuid); + } + } + }, [ingestName, ingests]); + + useEffect(() => { + if (createSrtError && createSrtError !== '') { + toast.error(createSrtError || ''); + } + }, [createSrtError]); + + useEffect(() => { + fetchIngestSources(); + }, [ingests]); + + useEffect(() => { + setSrtPayload({ + latency_ms: latency || undefined, + local_ip: localIp || undefined, + local_port: localPort || undefined, + mode: mode === 'Listener' ? 'listener' : 'caller', + name: name, + passphrase: passphrase || undefined, + remote_ip: mode === 'Caller' ? remoteIp : undefined, + remote_port: mode === 'Caller' ? remotePort : undefined + }); + }, [ + mode, + localIp, + localPort, + remoteIp, + remotePort, + latency, + name, + passphrase + ]); + + useEffect(() => { + if (isNameError) { + if (name.length !== 0 || name !== '') { + setIsNameError(false); + } + } + }, [isNameError, name]); + + useEffect(() => { + if (mode === 'Caller' && isPortAlreadyInUseError) { + setIsPortAlreadyInUseError(false); + } + }, [mode, isPortAlreadyInUseError]); + + useEffect(() => { + if (localIp) { + setIsLocalIpError(false); + } + }, [localIp]); + + useEffect(() => { + if (localPort) { + setIsLocalPortError(false); + } + }, [localPort]); + + useEffect(() => { + if (remoteIp) { + setIsRemoteIpError(false); + } + }, [remoteIp]); + + useEffect(() => { + if (createSrtSuccessful) { + handleCancel(); + } + }, [createSrtSuccessful]); + + useEffect(() => { + if (remotePort) { + setIsRemotePortError(false); + } + }, [remotePort]); + + useEffect(() => { + if (passphrase || passphrase === '') { + setIsPassphraseError(false); + } + }, [passphrase]); + + useEffect(() => { + if (ingestName) { + setIsIngestNameError(false); + setIsDuplicateNameError(false); + setIsPortAlreadyInUseError(false); + } + }, [ingestName]); + + const handleCloseModal = () => { + setIsNameError(false); + setIsIngestNameError(false); + setIsLocalIpError(false); + setIsLocalPortError(false); + setIsRemoteIpError(false); + setIsRemotePortError(false); + setIsPortAlreadyInUseError(false); + setIsPassphraseError(false); + setIsDuplicateNameError(false); + onAbort(); + }; + + const handleCancel = () => { + // Reset all fields + setIngestName(''); + setName('My SRT source'); + setLocalIp('0.0.0.0'); + setLocalPort(1234); + setRemoteIp('127.0.0.1'); + setRemotePort(1234); + setLatency(120); + setPassphrase(''); + + // Reset all errors + setIsNameError(false); + setIsIngestNameError(false); + setIsLocalIpError(false); + setIsLocalPortError(false); + setIsRemoteIpError(false); + setIsRemotePortError(false); + setIsPortAlreadyInUseError(false); + setIsPassphraseError(false); + setIsDuplicateNameError(false); + + onAbort(); + }; + + const fetchIngestSources = async (): Promise => { + if (!ingestName || ingests.length === 0) { + return []; + } + + const sources = []; + try { + const res = await getIngestSources(ingestName); + sources.push(...res); + } catch (error) { + console.error(`Failed to fetch ingest sources for ${ingestName}:`, error); + } + + const srtSources = sources.filter((source) => source.type === 'SRT'); + + return srtSources; + }; + + const handleCreateSrtSource = async () => { + let hasError = false; + + if (!name) { + setIsNameError(true); + hasError = true; + } + if (ingestName === '') { + setIsIngestNameError(true); + hasError = true; + } + if (!localIp) { + setIsLocalIpError(true); + hasError = true; + } + if (!localPort && mode === 'Listener') { + setIsLocalPortError(true); + hasError = true; + } + if (!remoteIp && mode === 'Caller') { + setIsRemoteIpError(true); + hasError = true; + } + if (!remotePort && mode === 'Caller') { + setIsRemotePortError(true); + hasError = true; + } + + if ( + passphrase && + passphrase !== '' && + (passphrase.length < 10 || passphrase.length > 79) + ) { + setIsPassphraseError(true); + hasError = true; + } + + const srtSources = await fetchIngestSources(); + + if (srtSources.length > 0) { + const duplicateNames = srtSources.some((srt) => srt.name === name); + if (duplicateNames) { + setIsDuplicateNameError(true); + hasError = true; + } + } + + if (hasError) { + return; + } + + if (srtSources.length > 0 && mode === 'Listener') { + const usedPorts: number[] = []; + srtSources.forEach((s) => { + if (s.srt?.local_port) { + usedPorts.push(s.srt.local_port); + } + }); + + if (usedPorts.includes(Number(localPort))) { + setIsPortAlreadyInUseError(true); + return; + } else { + setIsPortAlreadyInUseError(false); + + onConfirm(ingestUuid, srtPayload, () => fetchIngestSources()); + } + } else { + onConfirm(ingestUuid, srtPayload, () => fetchIngestSources()); + } + }; + + const handleModeChange = () => { + setMode((prevMode) => (prevMode === 'Listener' ? 'Caller' : 'Listener')); + setIsIngestNameError(false); + }; + + useEffect(() => { + if (![t('inventory_list.select_ingest'), ''].includes(ingestName)) { + setIsIngestNameError(false); + } + }, [ingestName]); + + const handleIngestNameChange = (e: React.ChangeEvent) => { + setIngestName(e.target.value); + }; + + const handleInputChange = ( + setter: React.Dispatch>, + errorSetter?: React.Dispatch>, + secondErrorSetter?: React.Dispatch> + ) => { + return (e: React.ChangeEvent) => { + setter(e.target.value); + if (errorSetter) { + errorSetter(false); + } + if (secondErrorSetter) { + secondErrorSetter(false); + } + }; + }; + + return ( + +

    + {t('inventory_list.create_srt_source')} +

    +
    + + + {ingests.map((ingest) => ( + + ))} + + {isIngestNameError && ( +

    + {t('inventory_list.no_ingest_selected')} +

    + )} + + + +

    + {t('inventory_list.name')}: +

    + + + {isNameError && ( +

    + {t('inventory_list.no_name')} +

    + )} + {isDuplicateNameError && ( +

    + {t('inventory_list.duplicate_name_error')} +

    + )} +
    +
    + +

    + {t('inventory_list.local_ip')}: +

    + + + {isLocalIpError && ( +

    + {t('inventory_list.no_local_ip')} +

    + )} +
    +
    + +

    + {t('inventory_list.local_port')}: +

    + + + {isLocalPortError && ( +

    + {t('inventory_list.no_local_port')} +

    + )} + {mode === 'Listener' && isPortAlreadyInUseError && ( +

    + {t('inventory_list.port_already_in_use_error')} +

    + )} +
    +
    + {mode === 'Caller' && ( +
    + +

    + {t('inventory_list.remote_ip')}: +

    + + + {isRemoteIpError && ( +

    + {t('inventory_list.no_remote_ip')} +

    + )} +
    +
    + +

    + {t('inventory_list.remote_port')}: +

    + + + {isRemotePortError && ( +

    + {t('inventory_list.no_remote_port')} +

    + )} +
    +
    +
    + )} + +

    + {t('inventory_list.latency')}: +

    + + + +
    + +

    + {t('inventory_list.passphrase')}: +

    + + + {isPassphraseError && ( +

    + {t('inventory_list.passphrase_error')} +

    + )} +
    +
    +
    +
    + <> + + {loading ? ( + + ) : ( + + )} + +
    +
    + ); +} diff --git a/src/components/modal/configureMultiviewModal/ConfigureMultiviewButton.tsx b/src/components/modal/configureMultiviewModal/ConfigureMultiviewButton.tsx new file mode 100644 index 00000000..09122907 --- /dev/null +++ b/src/components/modal/configureMultiviewModal/ConfigureMultiviewButton.tsx @@ -0,0 +1,61 @@ +'use client'; + +import { useState } from 'react'; +import { IconSettings } from '@tabler/icons-react'; +import { Preset } from '../../../interfaces/preset'; +import { useTranslate } from '../../../i18n/useTranslate'; +import { Button } from '../../button/Button'; +import { ConfigureMultiviewModal } from './ConfigureMultiviewModal'; +import { Production } from '../../../interfaces/production'; + +type ConfigureMultiviewButtonProps = { + preset?: Preset; + disabled?: boolean; + updatePreset: (preset: Preset) => void; + production?: Production; +}; + +export function ConfigureMultiviewButton({ + preset, + updatePreset, + disabled, + production +}: ConfigureMultiviewButtonProps) { + const [modalOpen, setModalOpen] = useState(false); + const toggleConfigModal = () => { + if (preset) { + setModalOpen((state) => !state); + } + }; + const t = useTranslate(); + return ( + <> + + {preset && ( + + )} + + ); +} diff --git a/src/components/modal/configureMultiviewModal/ConfigureMultiviewModal.tsx b/src/components/modal/configureMultiviewModal/ConfigureMultiviewModal.tsx new file mode 100644 index 00000000..11b2fac3 --- /dev/null +++ b/src/components/modal/configureMultiviewModal/ConfigureMultiviewModal.tsx @@ -0,0 +1,359 @@ +import { TMultiviewLayout, Preset } from '../../../interfaces/preset'; +import { Modal } from '../Modal'; +import { useEffect, useState } from 'react'; +import { useTranslate } from '../../../i18n/useTranslate'; +import toast from 'react-hot-toast'; +import { MultiviewSettings } from '../../../interfaces/multiview'; +import { IconPlus, IconTrash } from '@tabler/icons-react'; +import { Production } from '../../../interfaces/production'; +import deepclone from 'lodash.clonedeep'; +import MultiviewSettingsConfig from './MultiviewSettings'; +import { usePutMultiviewLayout } from '../../../hooks/multiviewLayout'; +import Decision from '../configureOutputModal/Decision'; +import MultiviewLayoutSettings from './MultiviewLayoutSettings/MultiviewLayoutSettings'; +import { IconSettings } from '@tabler/icons-react'; +import { UpdateMultiviewersModal } from '../UpdateMultiviewersModal'; +import { Button } from '../../button/Button'; + +type ConfigureMultiviewModalProps = { + open: boolean; + preset: Preset; + onClose: () => void; + updatePreset: (preset: Preset) => void; + production?: Production; +}; + +export function ConfigureMultiviewModal({ + open, + preset, + onClose, + updatePreset, + production +}: ConfigureMultiviewModalProps) { + const [multiviews, setMultiviews] = useState([]); + const [portDuplicateIndexes, setPortDuplicateIndexes] = useState( + [] + ); + const [streamIdDuplicateIndexes, setStreamIdDuplicateIndexes] = useState< + number[] + >([]); + const [layoutModalOpen, setLayoutModalOpen] = useState(false); + const [refresh, setRefresh] = useState(false); + const [confirmUpdateModalOpen, setConfirmUpdateModalOpen] = useState(false); + const [newMultiviewLayout, setNewMultiviewLayout] = + useState(null); + const addNewLayout = usePutMultiviewLayout(); + const t = useTranslate(); + + useEffect(() => { + setRefresh(open); + }, [open]); + + useEffect(() => { + if (preset.pipelines[0].multiviews) { + if (!Array.isArray(preset.pipelines[0].multiviews)) { + setMultiviews([preset.pipelines[0].multiviews]); + } else { + setMultiviews(preset.pipelines[0].multiviews); + } + } + }, [preset.pipelines]); + + const clearInputs = () => { + setLayoutModalOpen(false); + setMultiviews(preset.pipelines[0].multiviews || []); + onClose(); + }; + + useEffect(() => { + runDuplicateCheck(multiviews); + }, [multiviews]); + + const onSave = () => { + if (production?.isActive && !confirmUpdateModalOpen) { + setConfirmUpdateModalOpen(true); + return; + } + + if (production?.isActive && confirmUpdateModalOpen) { + setConfirmUpdateModalOpen(false); + } + + const presetToUpdate = deepclone(preset); + const ipMissing = multiviews.some( + (multiview) => + multiview.output.local_ip === '' || !multiview.output.local_ip + ); + const portMissing = multiviews.some( + (multiview) => !multiview.output.local_port + ); + const videoKilobitRateMissing = multiviews.some( + (multiview) => !multiview.output.video_kilobit_rate + ); + + if (!multiviews) { + toast.error(t('preset.no_multiview_selected')); + return; + } + + if (ipMissing) { + toast.error(t('preset.no_ip_selected')); + return; + } + + if (videoKilobitRateMissing) { + toast.error(t('preset.no_rate_selected')); + return; + } + + if (portDuplicateIndexes.length > 0 || portMissing) { + toast.error(t('preset.no_port_selected')); + return; + } + + if (streamIdDuplicateIndexes.length > 0) { + toast.error(t('preset.unique_stream_id')); + return; + } + + presetToUpdate.pipelines[0].multiviews = multiviews.map( + (singleMultiview) => { + return { ...singleMultiview }; + } + ); + + updatePreset(presetToUpdate); + onClose(); + }; + + const onUpdateLayoutPreset = async () => { + if (newMultiviewLayout?.name === '') { + toast.error(t('preset.layout_name_missing')); + return; + } + + if (!newMultiviewLayout) { + toast.error(t('preset.no_updated_layout')); + return; + } + + await addNewLayout(newMultiviewLayout); + setNewMultiviewLayout(null); + setLayoutModalOpen(false); + setRefresh(true); + }; + + const closeLayoutModal = () => { + setNewMultiviewLayout(null); + setLayoutModalOpen(false); + setRefresh(true); + }; + + const findDuplicateValues = (mvs: MultiviewSettings[]) => { + const ports = mvs.map( + (item: MultiviewSettings) => + item.output.local_ip + ':' + item.output.local_port.toString() + ); + const streamIds = mvs.map( + (item: MultiviewSettings) => item.output.srt_stream_id + ); + const duplicatePortIndices: number[] = []; + const duplicateStreamIdIndices: number[] = []; + const seenPorts = new Set(); + const seenIds = new Set(); + + ports.forEach((port, index) => { + if (seenPorts.has(port)) { + duplicatePortIndices.push(index); + + // Also include the first occurrence if it's not already included + const firstIndex = ports.indexOf(port); + if (!duplicatePortIndices.includes(firstIndex)) { + duplicatePortIndices.push(firstIndex); + } + } else { + seenPorts.add(port); + } + }); + + streamIds.forEach((streamId, index) => { + if (streamId === '' || !streamId) { + return; + } + + if (seenIds.has(streamId)) { + duplicateStreamIdIndices.push(index); + + // Also include the first occurrence if it's not already included + const firstIndex = streamIds.indexOf(streamId); + if (!duplicateStreamIdIndices.includes(firstIndex)) { + duplicateStreamIdIndices.push(firstIndex); + } + } else { + seenIds.add(streamId); + } + }); + + return { + hasDuplicatePort: duplicatePortIndices, + hasDuplicateStreamId: duplicateStreamIdIndices + }; + }; + + const runDuplicateCheck = (mvs: MultiviewSettings[]) => { + const { hasDuplicatePort, hasDuplicateStreamId } = findDuplicateValues(mvs); + + if (hasDuplicatePort.length > 0) { + setPortDuplicateIndexes(hasDuplicatePort); + } + + if (hasDuplicateStreamId.length > 0) { + setStreamIdDuplicateIndexes(hasDuplicateStreamId); + } + + if (hasDuplicatePort.length === 0) { + setPortDuplicateIndexes([]); + } + + if (hasDuplicateStreamId.length === 0) { + setStreamIdDuplicateIndexes([]); + } + }; + + const handleUpdateMultiview = ( + multiview: MultiviewSettings, + index: number + ) => { + const updatedMultiviews = multiviews.map((item, i) => + i === index ? { ...item, ...multiview } : item + ); + + runDuplicateCheck(multiviews); + + setMultiviews(updatedMultiviews); + }; + + const addNewMultiview = (newMultiview: MultiviewSettings) => { + // Remove _id from newMultiview to avoid conflicts with existing multiviews + delete newMultiview._id; + + setMultiviews((prevMultiviews) => + prevMultiviews ? [...prevMultiviews, newMultiview] : [newMultiview] + ); + }; + + const removeNewMultiview = (index: number) => { + const newMultiviews = multiviews.filter((_, i) => i !== index); + setMultiviews(newMultiviews); + }; + + return ( + + {!layoutModalOpen && ( +
    + {multiviews && + multiviews.length > 0 && + multiviews.map((singleItem, index) => { + return ( +
    + {index !== 0 && ( +
    + )} +
    + + handleUpdateMultiview(input, index) + } + portDuplicateError={ + portDuplicateIndexes.length > 0 + ? portDuplicateIndexes.includes(index) + : false + } + streamIdDuplicateError={ + streamIdDuplicateIndexes.length > 0 + ? streamIdDuplicateIndexes.includes(index) + : false + } + refresh={refresh} + /> +
    1 + ? 'justify-between' + : 'justify-end' + }`} + > + {multiviews.length > 1 && ( + + )} + {multiviews.length === index + 1 && ( + + )} +
    +
    +
    + ); + })} +
    + )} + {layoutModalOpen && ( + + )} +
    + {!layoutModalOpen && ( + + )} + (layoutModalOpen ? closeLayoutModal() : clearInputs())} + onSave={() => (layoutModalOpen ? onUpdateLayoutPreset() : onSave())} + /> +
    + + {confirmUpdateModalOpen && ( + setConfirmUpdateModalOpen(false)} + onConfirm={() => onSave()} + /> + )} +
    + ); +} diff --git a/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/Checkbox.tsx b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/Checkbox.tsx new file mode 100644 index 00000000..d68a343d --- /dev/null +++ b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/Checkbox.tsx @@ -0,0 +1,27 @@ +import { useTranslate } from '../../../../i18n/useTranslate'; + +type RemoveLayoutButtonProps = { + handleCheckboxChange: () => void; + isChecked: boolean; +}; + +export default function Checkbox({ + handleCheckboxChange, + isChecked +}: RemoveLayoutButtonProps) { + const t = useTranslate(); + return ( +
    + + +
    + ); +} diff --git a/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx new file mode 100644 index 00000000..c039ec51 --- /dev/null +++ b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx @@ -0,0 +1,63 @@ +import { TListSource } from '../../../../interfaces/multiview'; +import { MultiviewPreset } from '../../../../interfaces/preset'; +import Options from '../../configureOutputModal/Options'; + +export default function MultiviewLayout({ + multiviewPresetLayout, + inputList, + handleChange +}: { + multiviewPresetLayout: MultiviewPreset; + inputList: TListSource[] | undefined; + handleChange: (id: string, value: string) => void; +}) { + return ( +
    + {multiviewPresetLayout.layout.views.map((singleView) => { + const { x, y, width, height, label, id } = singleView; + const previewView = singleView.input_slot === 1002 && y === 0; + const programView = singleView.input_slot === 1001 && y === 0; + + const sourceId = inputList?.find( + (source) => source.label === label + )?.id; + + return ( +
    + {inputList && (previewView || programView) && ( +

    {label}

    + )} + {inputList && !previewView && !programView && ( + ({ + id: singleSource.id, + label: singleSource.label + }))} + value={sourceId ? sourceId : 'empty'} + update={(value) => handleChange(id || '', value)} + columnStyle + emptyFirstOption + /> + )} +
    + ); + })} +
    + ); +} diff --git a/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx new file mode 100644 index 00000000..f5e6db13 --- /dev/null +++ b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx @@ -0,0 +1,269 @@ +import { useEffect, useState } from 'react'; +import { MultiviewPreset } from '../../../../interfaces/preset'; +import { useTranslate } from '../../../../i18n/useTranslate'; +import { useSetupMultiviewLayout } from '../../../../hooks/useSetupMultiviewLayout'; +import { + useDeleteMultiviewLayout, + useMultiviewLayouts +} from '../../../../hooks/multiviewLayout'; +import { useConfigureMultiviewLayout } from '../../../../hooks/useConfigureMultiviewLayout'; +import { Production } from '../../../../interfaces/production'; +import { TMultiviewLayout } from '../../../../interfaces/preset'; +import { useCreateInputArray } from '../../../../hooks/useCreateInputArray'; +import { TListSource } from '../../../../interfaces/multiview'; +import Options from '../../configureOutputModal/Options'; +import Input from '../../configureOutputModal/Input'; +import MultiviewLayout from './MultiviewLayout'; +import toast from 'react-hot-toast'; +import RemoveLayoutButton from './RemoveLayoutButton'; +import { useMultiviewDefaultPresets } from '../../../../hooks/useMultiviewDefaultPresets'; +import Checkbox from './Checkbox'; + +type ChangeLayout = { + defaultLabel?: string; + source?: TListSource; + viewId: string; +}; + +export default function MultiviewLayoutSettings({ + production, + setNewMultiviewPreset, + layoutModalOpen +}: { + production: Production | undefined; + setNewMultiviewPreset: (preset: TMultiviewLayout | null) => void; + layoutModalOpen: boolean; +}) { + const [selectedMultiviewPreset, setSelectedMultiviewPreset] = + useState(null); + const [presetName, setPresetName] = useState(''); + const [refresh, setRefresh] = useState(true); + const [isChecked, setIsChecked] = useState(false); + const [changedLayout, setChangedLayout] = useState(null); + const [newPresetName, setNewPresetName] = useState(null); + const { inputList } = useCreateInputArray(production); + const [multiviewLayouts] = useMultiviewLayouts(refresh); + const sourceList = production ? production.sources : []; + const { multiviewDefaultPresets } = useMultiviewDefaultPresets({ + sourceList, + isChecked + }); + const { multiviewPresetLayout } = useSetupMultiviewLayout( + selectedMultiviewPreset + ); + const { multiviewLayout } = useConfigureMultiviewLayout( + production?._id, + selectedMultiviewPreset, + changedLayout?.defaultLabel, + changedLayout?.source, + changedLayout?.viewId, + newPresetName + ); + + const deleteLayout = useDeleteMultiviewLayout(); + const t = useTranslate(); + + const multiviewPresetNames = multiviewDefaultPresets?.map( + (preset) => preset.name + ) + ? multiviewDefaultPresets?.map((preset) => preset.name) + : []; + + const availableMultiviewLayouts = + multiviewLayouts?.filter( + (layout) => layout.productionId === production?._id + ) || []; + + const multiviewLayoutNames = + availableMultiviewLayouts?.map((layout) => layout.name) || []; + const layoutNameAlreadyExist = availableMultiviewLayouts?.find( + (singlePreset) => singlePreset.name === newPresetName + )?.name; + + // This useEffect is used to set the drawn layout of the multiviewer on start, + // if this fails then the modal will be empty + useEffect(() => { + const selectedLayout = multiviewLayouts?.find((layout) => { + return layout.name === selectedMultiviewPreset?.name; + }); + const loadedPreset = multiviewDefaultPresets?.find((preset) => { + return preset.name === selectedMultiviewPreset?.name; + }); + + if (selectedLayout) { + setSelectedMultiviewPreset(selectedLayout); + } else if (loadedPreset && !selectedLayout) { + setSelectedMultiviewPreset(loadedPreset); + } else if (multiviewDefaultPresets && multiviewDefaultPresets[0]) { + setPresetName(multiviewDefaultPresets[0].name); + setSelectedMultiviewPreset(multiviewDefaultPresets[0]); + } + }, [multiviewDefaultPresets, multiviewLayouts]); + + // Refresh the layout list when a layout is deleted + useEffect(() => { + setRefresh(layoutModalOpen); + }, [layoutModalOpen]); + + useEffect(() => { + if (multiviewLayouts) { + setRefresh(false); + } + }, [multiviewLayouts]); + + useEffect(() => { + if (newPresetName && selectedMultiviewPreset && production) { + setNewMultiviewPreset({ + ...selectedMultiviewPreset, + name: newPresetName, + productionId: production._id + }); + } + }, [ + newPresetName, + selectedMultiviewPreset, + setNewMultiviewPreset, + production + ]); + + useEffect(() => { + if (multiviewLayout) { + setSelectedMultiviewPreset(multiviewLayout); + } + }, [multiviewLayout]); + + const handleLayoutUpdate = (name: string, type: string) => { + const chosenLayout = availableMultiviewLayouts?.find( + (singleLayout) => singleLayout.name === name + ); + const addBasePreset = multiviewDefaultPresets?.find( + (singlePreset) => singlePreset.name === name + ); + + switch (type) { + case 'layout': + setNewPresetName(name || ''); + if (chosenLayout) { + setIsChecked(false); + setPresetName(''); + setSelectedMultiviewPreset(chosenLayout); + } + break; + case 'preset': + if (addBasePreset) { + setNewPresetName(''); + setPresetName(addBasePreset.name); + setSelectedMultiviewPreset(addBasePreset); + } + break; + } + }; + + const handleChange = (viewId: string, value: string) => { + if (inputList && availableMultiviewLayouts) { + const emptyView = { + id: '', + input_slot: 0, + label: '' + }; + + inputList.map((source) => { + if (value === '') { + setChangedLayout({ source: emptyView, viewId }); + } + if (source.id === value) { + setChangedLayout({ source, viewId }); + } + }); + } + }; + + const removeMultiviewLayout = () => { + const layoutToRemove = availableMultiviewLayouts.find( + (layout) => layout.name === newPresetName + ); + + if (layoutToRemove && !layoutToRemove._id) { + toast.error(t('preset.could_not_delete_layout')); + return; + } + if (layoutToRemove && layoutToRemove._id) { + deleteLayout(layoutToRemove._id.toString()).then(() => { + setRefresh(true); + if (multiviewDefaultPresets?.[0]) { + setSelectedMultiviewPreset(multiviewDefaultPresets[0]); + } + setNewPresetName(''); + toast.success(t('preset.layout_deleted')); + }); + } + }; + + return ( + <> + {selectedMultiviewPreset && ( +
    +
    +
    + 0 + ? multiviewLayoutNames.map((singleItem) => ({ + label: singleItem + })) + : [{ label: 'No layouts available' }] + } + value={selectedMultiviewPreset?.name || ''} + update={(value) => handleLayoutUpdate(value, 'layout')} + emptyFirstOption + /> +
    +
    + ({ + label: singleItem + }))} + value={presetName} + update={(value) => handleLayoutUpdate(value, 'preset')} + emptyFirstOption + /> + setIsChecked((prev) => !prev)} + isChecked={isChecked} + /> +
    +
    + + {multiviewPresetLayout && ( + + )} +
    + setNewPresetName(value)} + placeholder={t('preset.new_preset_name')} + /> + {layoutNameAlreadyExist && newPresetName !== '' && ( +

    + {t('preset.layout_already_exist', { layoutNameAlreadyExist })} +

    + )} +
    +
    + )} + + ); +} diff --git a/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/RemoveLayoutButton.tsx b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/RemoveLayoutButton.tsx new file mode 100644 index 00000000..3688584d --- /dev/null +++ b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/RemoveLayoutButton.tsx @@ -0,0 +1,47 @@ +import { IconTrash } from '@tabler/icons-react'; + +type RemoveLayoutButtonProps = { + removeMultiviewLayout: () => void; + deleteDisabled: boolean; + title: string; + hidden: boolean; +}; + +export default function RemoveLayoutButton({ + removeMultiviewLayout, + deleteDisabled, + title, + hidden +}: RemoveLayoutButtonProps) { + const setIconStyling = () => { + if (deleteDisabled && !hidden) { + return 'pointer-events-none text-zinc-400'; + } + if (!deleteDisabled && !hidden) { + return 'text-button-delete hover:text-red-400'; + } + if (hidden) { + return 'pointer-events-none text-gray-800'; + } + }; + + return ( + + ); +} diff --git a/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx b/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx new file mode 100644 index 00000000..9daedef4 --- /dev/null +++ b/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx @@ -0,0 +1,264 @@ +import { useEffect, useState } from 'react'; +import { useTranslate } from '../../../i18n/useTranslate'; +import { MultiviewSettings } from '../../../interfaces/multiview'; +import { TMultiviewLayout } from '../../../interfaces/preset'; +import Input from '../configureOutputModal/Input'; +import Options from '../configureOutputModal/Options'; +import toast from 'react-hot-toast'; +import { useMultiviewLayouts } from '../../../hooks/multiviewLayout'; + +type MultiviewSettingsProps = { + lastItem: boolean; + multiview?: MultiviewSettings; + handleUpdateMultiview: (multiview: MultiviewSettings) => void; + portDuplicateError: boolean; + streamIdDuplicateError: boolean; + newMultiviewLayout: TMultiviewLayout | null; + productionId: string | undefined; + refresh: boolean; +}; + +export default function MultiviewSettingsConfig({ + lastItem, + multiview, + handleUpdateMultiview, + portDuplicateError, + streamIdDuplicateError, + newMultiviewLayout, + productionId, + refresh +}: MultiviewSettingsProps) { + const t = useTranslate(); + const [selectedMultiviewLayout, setSelectedMultiviewLayout] = useState< + TMultiviewLayout | undefined + >(); + const [multiviewLayouts] = useMultiviewLayouts(refresh); + + const currentValue = multiview || selectedMultiviewLayout; + const avaliableMultiviewLayouts = multiviewLayouts?.filter( + (layout) => layout.productionId === productionId || !layout.productionId + ); + const multiviewLayoutNames = + avaliableMultiviewLayouts?.map((layout) => layout.name) || []; + + useEffect(() => { + if ( + refresh && + multiview && + multiviewLayouts && + multiviewLayouts.length > 0 + ) { + handleSetSelectedMultiviewLayout(multiview.name); + } + }, [refresh, multiviewLayouts]); + + useEffect(() => { + if (multiview) { + setSelectedMultiviewLayout(multiview); + return; + } + if (multiviewLayouts) { + const defaultMultiview = multiviewLayouts.find( + (m) => m.productionId !== undefined + ); + if (defaultMultiview) { + setSelectedMultiviewLayout(defaultMultiview); + } + } + }, [lastItem, multiview, multiviewLayouts, newMultiviewLayout]); + + if (!multiview) { + if (!multiviewLayouts || multiviewLayouts.length === 0) { + return null; + } + handleUpdateMultiview({ + ...multiviewLayouts[0], + _id: multiviewLayouts[0]._id?.toString(), + for_pipeline_idx: 0 + }); + } + + const handleSetSelectedMultiviewLayout = (name: string) => { + const selected = multiviewLayouts?.find((m) => m.name === name); + if (!selected) { + toast.error(t('preset.no_multiview_found')); + return; + } + const updatedMultiview = { + ...selected, + name, + layout: { + ...selected.layout + }, + output: multiview?.output || selected.output + }; + setSelectedMultiviewLayout(updatedMultiview); + handleUpdateMultiview({ + ...updatedMultiview, + _id: updatedMultiview._id?.toString(), + for_pipeline_idx: 0 + }); + }; + + const getNumber = (val: string, prev: number) => { + if ( + val === '' || + (!isNaN(Number(val)) && Number.isInteger(parseFloat(val))) + ) { + return parseInt(val); + } else { + return prev; + } + }; + + const handleChange = (key: string, value: string) => { + if (!multiview) return; + if (key === 'videoFormat') { + const updatedMultiview = { + ...multiview, + output: { + ...multiview.output, + video_format: value + }, + for_pipeline_idx: 0 + }; + handleUpdateMultiview(updatedMultiview); + } + if (key === 'videoKiloBit') { + const updatedMultiview = { + ...multiview, + output: { + ...multiview.output, + video_kilobit_rate: getNumber( + value, + multiview.output.video_kilobit_rate + ) + }, + for_pipeline_idx: 0 + }; + handleUpdateMultiview(updatedMultiview); + } + if (key === 'srtMode') { + const updatedMultiview = { + ...multiview, + output: { + ...multiview.output, + srt_mode: value + }, + for_pipeline_idx: 0 + }; + handleUpdateMultiview(updatedMultiview); + } + if (key === 'port') { + const updatedMultiview = { + ...multiview, + output: { + ...multiview.output, + local_port: getNumber(value, multiview.output.local_port), + remote_port: getNumber(value, multiview.output.remote_port) + }, + for_pipeline_idx: 0 + }; + handleUpdateMultiview(updatedMultiview); + } + if (key === 'ip') { + const updatedMultiview = { + ...multiview, + output: { + ...multiview.output, + local_ip: value, + remote_ip: value + }, + for_pipeline_idx: 0 + }; + handleUpdateMultiview(updatedMultiview); + } + if (key === 'srtStreamId') { + const updatedMultiview = { + ...multiview, + output: { + ...multiview.output, + srt_stream_id: value + }, + for_pipeline_idx: 0 + }; + handleUpdateMultiview(updatedMultiview); + } + if (key === 'srtPassphrase') { + const updatedMultiview = { + ...multiview, + output: { + ...multiview.output, + srt_passphrase: value + }, + for_pipeline_idx: 0 + }; + handleUpdateMultiview(updatedMultiview); + } + }; + + return ( +
    +
    +

    {t('preset.multiview_output_settings')}

    +
    +
    + ({ + label: singleItem + }))} + value={selectedMultiviewLayout?.name || ''} + update={(value) => handleSetSelectedMultiviewLayout(value)} + /> +
    +
    + handleChange('videoFormat', value)} + /> + handleChange('videoKiloBit', value)} + /> + handleChange('srtMode', value)} + /> + handleChange('port', value)} + /> + handleChange('ip', value)} + /> + handleChange('srtStreamId', value)} + /> + handleChange('srtPassphrase', value)} + /> +
    +
    + ); +} diff --git a/src/components/modal/configureOutputModal/ConfigureOutputModal.tsx b/src/components/modal/configureOutputModal/ConfigureOutputModal.tsx index c0489473..169b9d93 100644 --- a/src/components/modal/configureOutputModal/ConfigureOutputModal.tsx +++ b/src/components/modal/configureOutputModal/ConfigureOutputModal.tsx @@ -1,19 +1,21 @@ import { Preset } from '../../../interfaces/preset'; -import { ProgramOutput, PipelineSettings } from '../../../interfaces/pipeline'; import { Modal } from '../Modal'; import Decision from './Decision'; +import PipelineOutputConfig from './PipelineOutputConfig'; import { useEffect, useState } from 'react'; -import { useTranslate } from '../../../i18n/useTranslate'; -import { v4 as uuidv4 } from 'uuid'; -import toast from 'react-hot-toast'; -import { MultiviewSettings } from '../../../interfaces/multiview'; -import MultiviewSettingsConfig from './MultiviewSettings'; -import PipelineSettingsConfig from './PipelineSettings'; -import { IconPlus, IconTrash } from '@tabler/icons-react'; +import { PipelineOutput, PipelineSettings } from '../../../interfaces/pipeline'; +import { usePipelines } from '../../../hooks/pipelines'; +import cloneDeep from 'lodash.clonedeep'; + +type ConfigureOutputModalProps = { + open: boolean; + preset: Preset; + onClose: () => void; + updatePreset: (preset: Preset) => void; +}; export interface OutputStream { name: string; - id: string; pipelineIndex: number; ip: string; srtMode: string; @@ -22,14 +24,10 @@ export interface OutputStream { videoFormat: string; videoBit: number; videoKiloBit: number; + srt_stream_id: string; } -type ConfigureOutputModalProps = { - open: boolean; - preset: Preset; - onClose: () => void; - updatePreset: (preset: Preset) => void; -}; +const DEFAULT_PORT_MUMBER = 9900; export function ConfigureOutputModal({ open, @@ -37,301 +35,93 @@ export function ConfigureOutputModal({ onClose, updatePreset }: ConfigureOutputModalProps) { - const defaultState = (pipelines: PipelineSettings[]) => { - const streamsPerPipe = pipelines.map((pipe, i) => { - return pipe.program_output.map((output) => ({ - name: ``, - id: uuidv4(), - pipelineIndex: i, - ip: - output?.srt_mode === 'listener' - ? output.local_ip - : output?.remote_ip || '0.0.0.0', - srtMode: output?.srt_mode, - srtPassphrase: output?.srt_passphrase || '', - port: output?.port, - videoFormat: output?.video_format, - videoBit: output?.video_bit_depth, - videoKiloBit: output?.video_kilobit_rate - })); - }); - - return streamsPerPipe.flatMap((streams) => { - return streams.map((stream, i) => { - return { ...stream, name: `Stream ${i + 1}` }; - }); - }) satisfies OutputStream[]; - }; - - const [outputstreams, setOutputStreams] = useState( - defaultState(preset.pipelines) - ); - const [multiviews, setMultiviews] = useState([]); - const [portDuplicateIndexes, setPortDuplicateIndexes] = useState( - [] + const [pipelines, setPipelines] = useState( + preset.pipelines || [] ); - const t = useTranslate(); + const [currentError, setCurrentError] = useState(''); + const [currentPortNumber, setCurrentPortNumber] = + useState(DEFAULT_PORT_MUMBER); - useEffect(() => { - if (preset.pipelines[0].multiviews) { - if (!Array.isArray(preset.pipelines[0].multiviews)) { - setMultiviews([preset.pipelines[0].multiviews]); - } else { - setMultiviews(preset.pipelines[0].multiviews); - } - } - }, [preset.pipelines]); + const [pipes] = usePipelines(); useEffect(() => { - setOutputStreams(defaultState(preset.pipelines)); + setPipelines(preset.pipelines || []); }, [preset]); - const clearInputs = () => { - setMultiviews(preset.pipelines[0].multiviews || []); - setOutputStreams(defaultState(preset.pipelines)); - onClose(); - }; - - useEffect(() => { - runDuplicateCheck(multiviews); - }, [multiviews]); - const onSave = () => { - const presetToUpdate = { - ...preset, - pipelines: preset.pipelines.map((pipeline, i) => { - return { - ...pipeline, - program_output: streamsToProgramOutputs( - i, - outputstreams.filter((o) => o.pipelineIndex === i) - ) - }; - }) - }; - - if (!multiviews) { - toast.error(t('preset.no_multiview_selected')); - return; + const locations = pipelines + .map((p) => + p.outputs?.map((o) => + o.streams.map((s) => `${s.local_ip}:${s.local_port}`) + ) + ) + .flat(2); + function findDuplicates(array: any[]) { + return array.filter( + (currentValue, currentIndex) => + array.indexOf(currentValue) !== currentIndex + ); } - - if (portDuplicateIndexes.length > 0) { - toast.error(t('preset.no_port_selected')); + const duplicates = findDuplicates(locations); + if (duplicates.length) { + setCurrentError('Same : used for multiple streams'); return; } - - presetToUpdate.pipelines[0].multiviews = multiviews.map( - (singleMultiview) => { - return { ...singleMultiview }; - } - ); - - updatePreset(presetToUpdate); + updatePreset({ ...preset, pipelines: pipelines }); onClose(); }; - const streamsToProgramOutputs = ( - pipelineIndex: number, - outputStreams?: OutputStream[] + const updatePipelineOutputFunc = ( + pipeline: PipelineSettings, + outputs: PipelineOutput[] ) => { - if (!outputStreams) return []; - return outputStreams.map((stream) => ({ - ...preset.pipelines[pipelineIndex].program_output[0], - port: stream.port, - [stream.srtMode === 'listener' ? 'local_ip' : 'remote_ip']: stream.ip, - srt_mode: stream.srtMode, - video_bit_depth: stream.videoBit, - video_format: stream.videoFormat, - video_kilobit_rate: stream.videoKiloBit, - srt_passphrase: stream.srtPassphrase - })) satisfies ProgramOutput[]; - }; - - const addStream = (stream: OutputStream) => { - const streams = outputstreams.filter( - (o) => o.pipelineIndex === stream.pipelineIndex + setCurrentError(''); + const pipelineIndex = pipelines.findIndex( + (p) => p.pipeline_name === pipeline.pipeline_name ); - if (streams.length > 4) return; - setOutputStreams([ - ...outputstreams, - { - ...stream, - name: `${t('preset.stream_name')} ${streams.length + 1}`, - port: streams[streams.length - 1].port + 1 - } - ]); - }; - - const updateStream = (updatedStream: OutputStream) => { - setOutputStreams( - [ - ...outputstreams.filter((o) => o.id !== updatedStream.id), - updatedStream - ].sort((a, b) => a.name.localeCompare(b.name)) - ); - }; - - const updateStreams = (updatedStreams: OutputStream[]) => { - const streams = outputstreams.filter( - (o) => !updatedStreams.some((u) => u.id === o.id) - ); - setOutputStreams( - [...streams, ...updatedStreams].sort((a, b) => - a.name.localeCompare(b.name) - ) - ); - }; - - const setNames = (outputstreams: OutputStream[], index: number) => { - const streamsForPipe = outputstreams.filter( - (o) => o.pipelineIndex === index - ); - const rest = outputstreams.filter((o) => o.pipelineIndex !== index); - return [ - ...streamsForPipe.map((s, i) => ({ ...s, name: `Stream ${i + 1}` })), - ...rest - ]; - }; - - const deleteStream = (id: string, index: number) => { - setOutputStreams( - setNames( - outputstreams.filter((o) => o.id !== id), - index - ) - ); - }; - - const findDuplicateValues = (mvs: MultiviewSettings[]) => { - const ports = mvs.map( - (item: MultiviewSettings) => - item.output.local_ip + ':' + item.output.local_port.toString() - ); - const duplicateIndices: number[] = []; - const seenPorts = new Set(); - - ports.forEach((port, index) => { - if (seenPorts.has(port)) { - duplicateIndices.push(index); - // Also include the first occurrence if it's not already included - const firstIndex = ports.indexOf(port); - if (!duplicateIndices.includes(firstIndex)) { - duplicateIndices.push(firstIndex); - } - } else { - seenPorts.add(port); - } - }); - - return duplicateIndices; - }; - - const runDuplicateCheck = (mvs: MultiviewSettings[]) => { - const hasDuplicates = findDuplicateValues(mvs); - - if (hasDuplicates.length > 0) { - setPortDuplicateIndexes(hasDuplicates); - } - - if (hasDuplicates.length === 0) { - setPortDuplicateIndexes([]); + if (pipelineIndex >= 0) { + const newPipelines = cloneDeep(pipelines); + newPipelines.splice(pipelineIndex, 1, { ...pipeline, outputs: outputs }); + setPipelines(newPipelines); } }; - const handleUpdateMultiview = ( - multiview: MultiviewSettings, - index: number - ) => { - const updatedMultiviews = multiviews.map((item, i) => - i === index ? { ...item, ...multiview } : item - ); - - runDuplicateCheck(multiviews); - - setMultiviews(updatedMultiviews); - }; - - const addNewMultiview = (newMultiview: MultiviewSettings) => { - setMultiviews((prevMultiviews) => - prevMultiviews ? [...prevMultiviews, newMultiview] : [newMultiview] - ); - }; - - const removeNewMultiview = (index: number) => { - const newMultiviews = multiviews.filter((_, i) => i !== index); - setMultiviews(newMultiviews); + const getPortNumber = () => { + setCurrentPortNumber(currentPortNumber + 1); + return currentPortNumber; }; return ( - clearInputs()}> -
    - {preset.pipelines.map((pipeline, i) => { - return ( - o.pipelineIndex === i)} - addStream={addStream} - updateStream={updateStream} - updateStreams={updateStreams} - deleteStream={deleteStream} - /> - ); - })} - {multiviews && - multiviews.length > 0 && - multiviews.map((singleItem, index) => { + { + onClose(); + }} + > +
    +
    + {pipelines.map((pipeline, i) => { return ( -
    -
    -
    - - handleUpdateMultiview(input, index) - } - portDuplicateError={ - portDuplicateIndexes.length > 0 - ? portDuplicateIndexes.includes(index) - : false - } - /> -
    1 ? 'justify-between' : 'justify-end' - }`} - > - {multiviews.length > 1 && ( - - )} - {multiviews.length === index + 1 && ( - - )} -
    -
    -
    + p.name === pipeline.pipeline_name) + ?.outputs || [] + } + pipelineOutputs={pipeline.outputs || []} + updatePipelineOutputs={(outputs: PipelineOutput[]) => + updatePipelineOutputFunc(pipeline, outputs) + } + pipeline={pipeline} + getPortNumber={getPortNumber} + /> ); })} +
    +
    {currentError}
    + onClose()} onSave={onSave} />
    - clearInputs()} onSave={onSave} />
    ); } diff --git a/src/components/modal/configureOutputModal/Decision.tsx b/src/components/modal/configureOutputModal/Decision.tsx index 440a6537..426e173f 100644 --- a/src/components/modal/configureOutputModal/Decision.tsx +++ b/src/components/modal/configureOutputModal/Decision.tsx @@ -4,13 +4,18 @@ import { Button } from '../../button/Button'; interface IDecision { onClose: () => void; onSave: () => void; + className?: string; } -export default function Decision({ onClose, onSave }: IDecision) { +export default function Decision({ onClose, onSave, className }: IDecision) { const t = useTranslate(); return ( -
    +
    diff --git a/src/components/modal/configureOutputModal/Input.tsx b/src/components/modal/configureOutputModal/Input.tsx index 25c09543..a2b01553 100644 --- a/src/components/modal/configureOutputModal/Input.tsx +++ b/src/components/modal/configureOutputModal/Input.tsx @@ -8,6 +8,7 @@ interface IInput { onKeyDown?: (e: KeyboardEvent) => void; size?: 'small' | 'large'; inputError?: boolean; + placeholder?: string; } export default function Input({ @@ -17,7 +18,8 @@ export default function Input({ type = 'text', onKeyDown, size = 'small', - inputError + inputError, + placeholder }: IInput) { const errorCss = 'border-red-500 focus:border-red-500 focus:outline'; @@ -34,6 +36,7 @@ export default function Input({ } pl-2 pt-1 pb-1 bg-gray-700 border-gray-600 placeholder-gray-400 text-white focus:border-gray-400 focus:outline-none ${ inputError ? errorCss : '' }`} + placeholder={placeholder ? placeholder : ''} />
    ); diff --git a/src/components/modal/configureOutputModal/MultiviewSettings.tsx b/src/components/modal/configureOutputModal/MultiviewSettings.tsx deleted file mode 100644 index e7420053..00000000 --- a/src/components/modal/configureOutputModal/MultiviewSettings.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useMultiviewPresets } from '../../../hooks/multiviewPreset'; -import { useTranslate } from '../../../i18n/useTranslate'; -import { MultiviewSettings } from '../../../interfaces/multiview'; -import { MultiviewPreset } from '../../../interfaces/preset'; -import Input from './Input'; -import Options from './Options'; -import toast from 'react-hot-toast'; - -type MultiviewSettingsProps = { - multiview?: MultiviewSettings; - handleUpdateMultiview: (multiview: MultiviewSettings) => void; - portDuplicateError: boolean; -}; - -export default function MultiviewSettingsConfig({ - multiview, - handleUpdateMultiview, - portDuplicateError -}: MultiviewSettingsProps) { - const t = useTranslate(); - const [multiviewPresets, loading] = useMultiviewPresets(); - const [selectedMultiviewPreset, setSelectedMultiviewPreset] = useState< - MultiviewPreset | undefined - >(multiview); - - useEffect(() => { - if (multiview) { - setSelectedMultiviewPreset(multiview); - return; - } - if (multiviewPresets && multiviewPresets[0]) { - setSelectedMultiviewPreset(multiviewPresets[0]); - } - }, [multiviewPresets, multiview]); - - if (!multiview) { - if (!multiviewPresets || multiviewPresets.length === 0) { - return null; - } - handleUpdateMultiview({ - ...multiviewPresets[0], - for_pipeline_idx: 0 - }); - } - - const handleSetSelectedMultiviewPreset = (name: string) => { - const selected = multiviewPresets?.find((m) => m.name === name); - if (!selected) { - toast.error(t('preset.no_multiview_found')); - return; - } - setSelectedMultiviewPreset(selected); - handleUpdateMultiview({ ...selected, for_pipeline_idx: 0 }); - }; - - const getNumber = (val: string, prev: number) => { - if (Number.isNaN(parseInt(val))) { - return prev; - } - return parseInt(val); - }; - - const handleChange = (key: string, value: string) => { - if (!multiview) return; - if (key === 'videoFormat') { - const updatedMultiview = { - ...multiview, - output: { - ...multiview.output, - video_format: value - }, - for_pipeline_idx: 0 - }; - handleUpdateMultiview(updatedMultiview); - } - if (key === 'videoKiloBit') { - const updatedMultiview = { - ...multiview, - output: { - ...multiview.output, - video_kilobit_rate: getNumber( - value, - multiview.output.video_kilobit_rate - ) - }, - for_pipeline_idx: 0 - }; - handleUpdateMultiview(updatedMultiview); - } - if (key === 'srtMode') { - const updatedMultiview = { - ...multiview, - output: { - ...multiview.output, - srt_mode: value - }, - for_pipeline_idx: 0 - }; - handleUpdateMultiview(updatedMultiview); - } - if (key === 'port') { - const updatedMultiview = { - ...multiview, - output: { - ...multiview.output, - local_port: getNumber(value, multiview.output.local_port), - remote_port: getNumber(value, multiview.output.remote_port) - }, - for_pipeline_idx: 0 - }; - handleUpdateMultiview(updatedMultiview); - } - if (key === 'ip') { - const updatedMultiview = { - ...multiview, - output: { - ...multiview.output, - local_ip: value, - remote_ip: value - }, - for_pipeline_idx: 0 - }; - handleUpdateMultiview(updatedMultiview); - } - if (key === 'srtPassphrase') { - const updatedMultiview = { - ...multiview, - output: { - ...multiview.output, - srt_passphrase: value - }, - for_pipeline_idx: 0 - }; - handleUpdateMultiview(updatedMultiview); - } - }; - const multiviewPresetNames = multiviewPresets?.map((preset) => preset.name) - ? multiviewPresets?.map((preset) => preset.name) - : []; - - const multiviewOrPreset = multiview ? multiview : selectedMultiviewPreset; - - return ( -
    -
    -

    {t('preset.multiview_output_settings')}

    -
    - handleSetSelectedMultiviewPreset(value)} - /> -
    - handleChange('videoFormat', value)} - /> - handleChange('videoKiloBit', value)} - /> - handleChange('srtMode', value)} - /> - handleChange('port', value)} - /> - handleChange('ip', value)} - /> - handleChange('srtPassphrase', value)} - /> -
    -
    - ); -} diff --git a/src/components/modal/configureOutputModal/Options.tsx b/src/components/modal/configureOutputModal/Options.tsx index 099da8fb..a5f52369 100644 --- a/src/components/modal/configureOutputModal/Options.tsx +++ b/src/components/modal/configureOutputModal/Options.tsx @@ -1,15 +1,34 @@ -type optionTypes = string; +import { useTranslate } from '../../../i18n/useTranslate'; -interface IOPtions { +type optionTypes = { id?: string; label: string }; + +interface IOptions { label: string; options: optionTypes[]; value: string; update: (value: string) => void; + columnStyle?: boolean; + emptyFirstOption?: boolean; } -export default function Options({ label, options, value, update }: IOPtions) { +export default function Options({ + label, + options, + value, + update, + columnStyle, + emptyFirstOption +}: IOptions) { + const t = useTranslate(); return ( -
    +
    diff --git a/src/components/modal/configureOutputModal/PipelineOutputConfig.tsx b/src/components/modal/configureOutputModal/PipelineOutputConfig.tsx new file mode 100644 index 00000000..d481ff1f --- /dev/null +++ b/src/components/modal/configureOutputModal/PipelineOutputConfig.tsx @@ -0,0 +1,293 @@ +import { useEffect, useState, KeyboardEvent } from 'react'; +import { ResourcesNameAndUUIDResponse } from '../../../../types/ateliere-live'; +import { useTranslate } from '../../../i18n/useTranslate'; +import Input from './Input'; +import { + PipelineOutput, + PipelineOutputEncoderSettings, + PipelineOutputSettings, + PipelineSettings +} from '../../../interfaces/pipeline'; +import StreamAccordion from './StreamAccordion'; +import { OutputStream } from './ConfigureOutputModal'; +import Options from './Options'; +import cloneDeep from 'lodash.clonedeep'; + +export type PipelineTypes = 'LD' | 'HQ'; + +interface PipelineOutputConfigProps { + title: string; + outputs: ResourcesNameAndUUIDResponse[]; + pipelineOutputs: PipelineOutput[]; + updatePipelineOutputs: (outputs: PipelineOutput[]) => void; + getPortNumber: () => number; + pipeline: PipelineSettings; +} + +const getStreamEncoderSettings = (pipeline: PipelineSettings) => { + return { + video_bit_depth: pipeline.bit_depth, + video_format: pipeline.format, + video_kilobit_rate: pipeline.video_kilobit_rate + }; +}; + +const createNewStream = (portNumber: number, pipeline: PipelineSettings) => { + return { + audio_format: 'ADTS', + audio_kilobit_rate: 128, + format: 'MPEG-TS-SRT', + local_ip: '0.0.0.0', + local_port: portNumber, + remote_ip: '0.0.0.0', + remote_port: portNumber, + srt_latency_ms: 120, + srt_mode: 'listener', + srt_passphrase: '', + video_gop_length: 50, + srt_stream_id: '', + ...getStreamEncoderSettings(pipeline) + }; +}; + +const PipelineOutputConfig: React.FC = (props) => { + const { + title, + outputs, + pipelineOutputs, + updatePipelineOutputs, + pipeline, + getPortNumber + } = props; + const [updatedOutputs, setUpdatedOutputs] = + useState(pipelineOutputs); + const t = useTranslate(); + + useEffect(() => { + updatePipelineOutputs(updatedOutputs); + }, [updatedOutputs]); + + const handleAddStream = (outputId: string) => { + const newOutputs: PipelineOutput[] = cloneDeep(updatedOutputs); + const foundOutput = newOutputs.find((o) => o.uuid === outputId); + const newStream = createNewStream(getPortNumber(), pipeline); + if (foundOutput) { + foundOutput?.streams.push(newStream); + } else { + newOutputs.push({ + uuid: outputId, + settings: getStreamEncoderSettings(pipeline), + streams: [newStream] + }); + } + setUpdatedOutputs(newOutputs); + }; + + const handleUpdateStream = ( + outputId: string, + index: number, + field: string, + value: string + ) => { + const foundOutputIndex = updatedOutputs.findIndex( + (o) => o.uuid === outputId + ); + if (foundOutputIndex >= 0) { + const getInt = (val: string) => { + if (Number.isNaN(parseInt(value))) { + return 0; + } + return parseInt(val); + }; + const newOutputs: PipelineOutput[] = cloneDeep(updatedOutputs); + const newStream = newOutputs[foundOutputIndex].streams[index]; + switch (field) { + default: + case 'port': + newStream.local_port = getInt(value); + newStream.remote_port = getInt(value); + break; + case 'srtMode': + newStream.srt_mode = value; + break; + case 'ip': + newStream.local_ip = value; + newStream.remote_ip = value; + break; + case 'srtPassphrase': + newStream.srt_passphrase = value; + break; + case 'srt_stream_id': + newStream.srt_stream_id = value; + break; + } + setUpdatedOutputs(newOutputs); + } + }; + + const handleDeleteStream = (outputId: string, index: number) => { + const foundOutputIndex = updatedOutputs.findIndex( + (o) => o.uuid === outputId + ); + if (foundOutputIndex >= 0) { + const newOutputs = cloneDeep(updatedOutputs); + newOutputs[foundOutputIndex].streams.splice(index, 1); + setUpdatedOutputs(newOutputs); + } + }; + + const getOutputStreams = (outputId: string) => { + const outputStreams = + updatedOutputs.find((o) => o.uuid === outputId)?.streams || []; + if (!outputStreams.length) return; + + const convertStream = ( + stream: PipelineOutputSettings, + index: number + ): OutputStream => { + return { + name: `Stream ${index + 1}`, + pipelineIndex: 0, + ip: stream.local_ip, + srtMode: stream.srt_mode, + srtPassphrase: stream.srt_passphrase, + port: stream.local_port, + videoFormat: stream.video_format, + videoBit: stream.video_bit_depth, + videoKiloBit: stream.video_kilobit_rate, + srt_stream_id: stream.srt_stream_id + }; + }; + return outputStreams.map((stream, index) => { + return ( + + handleUpdateStream(outputId, index, field, value) + } + onDelete={() => handleDeleteStream(outputId, index)} + /> + ); + }); + }; + + const preventCharacters = (evt: KeyboardEvent) => { + ['e', 'E', '+', '-'].includes(evt.key) && evt.preventDefault(); + }; + + const handleUpdateOutputSetting = ( + key: keyof PipelineOutputEncoderSettings, + value: string | number, + outputId: string + ) => { + const newOutputs: PipelineOutput[] = cloneDeep(updatedOutputs); + let foundOutputIndex = newOutputs.findIndex((o) => o.uuid === outputId); + if (foundOutputIndex < 0) { + newOutputs.push({ + uuid: outputId, + settings: getStreamEncoderSettings(pipeline), + streams: [] + }); + foundOutputIndex = newOutputs.findIndex((o) => o.uuid === outputId); + } + + if (!newOutputs[foundOutputIndex].settings) { + newOutputs[foundOutputIndex].settings = + getStreamEncoderSettings(pipeline); + } + + // Simple workaround to set these values as numbers + if (['video_bit_depth', 'video_kilobit_rate'].includes(key)) { + newOutputs[foundOutputIndex].settings[key] = Number(value) as never; + } else { + newOutputs[foundOutputIndex].settings[key] = value as never; + } + setUpdatedOutputs(newOutputs); + }; + + const getOutputFields = (outputId: string) => { + const foundOutput = updatedOutputs.find((p) => p.uuid === outputId); + + return ( +
    + + handleUpdateOutputSetting('video_format', value, outputId) + } + /> + + handleUpdateOutputSetting('video_bit_depth', value, outputId) + } + /> + + + handleUpdateOutputSetting('video_kilobit_rate', value, outputId) + } + /> +
    + ); + }; + + return ( +
    +

    {title}

    +
    + {outputs?.map((output, index) => ( +
    +

    {output.name}

    + {getOutputFields(output.uuid)} +
    + {getOutputStreams(output.uuid)} +
    + +
    + ))} +
    +
    + ); +}; + +export default PipelineOutputConfig; diff --git a/src/components/modal/configureOutputModal/PipelineSettings.tsx b/src/components/modal/configureOutputModal/PipelineSettings.tsx deleted file mode 100644 index ef4c924b..00000000 --- a/src/components/modal/configureOutputModal/PipelineSettings.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { useTranslate } from '../../../i18n/useTranslate'; -import Input from './Input'; -import Options from './Options'; -import { KeyboardEvent } from 'react'; -import StreamAccordion from './StreamAccordion'; -import { OutputStream } from './ConfigureOutputModal'; -import { v4 as uuidv4 } from 'uuid'; - -interface PipelineSettingsProps { - title: string; - streams?: OutputStream[]; - addStream: (stream: OutputStream) => void; - updateStream: (stream: OutputStream) => void; - updateStreams: (streams: OutputStream[]) => void; - deleteStream: (id: string, index: number) => void; -} -export default function PipelineSettings({ - title, - streams, - addStream, - updateStream, - updateStreams, - deleteStream -}: PipelineSettingsProps) { - const t = useTranslate(); - - const preventCharachters = (evt: KeyboardEvent) => - ['e', 'E', '+', '-'].includes(evt.key) && evt.preventDefault(); - if (!streams) return null; - - const handleAddStream = (stream: OutputStream) => { - addStream(stream); - }; - - const handleUpdateStream = (key: string, value: string, id: string) => { - const getInt = (val: string) => { - if (Number.isNaN(parseInt(value))) { - return 0; - } - return parseInt(val); - }; - - if (key === 'videoBit') { - const updatedStreams = streams.map((stream) => ({ - ...stream, - videoBit: getInt(value) - })); - updateStreams(updatedStreams); - return; - } - if (key === 'videoKiloBit') { - const updatedStreams = streams.map((stream) => ({ - ...stream, - videoKiloBit: getInt(value) - })); - updateStreams(updatedStreams); - return; - } - if (key === 'videoFormat') { - const updatedStreams = streams.map((stream) => ({ - ...stream, - videoFormat: value - })); - updateStreams(updatedStreams); - return; - } - const streamToUpdate = streams.find((stream) => stream.id === id); - if (streamToUpdate) { - const updatedStream = { - ...streamToUpdate, - [key]: - key === 'port' - ? getInt(value) - : key === 'videoBit' - ? getInt(value) - : key === 'videoKiloBit' - ? getInt(value) - : value - }; - updateStream(updatedStream); - } - }; - - const handleDeleteStream = (id: string, index: number) => { - deleteStream(id, index); - }; - return ( -
    -

    {title}

    -
    - - handleUpdateStream('videoFormat', value, streams[0].id) - } - /> - - handleUpdateStream('videoBit', value, streams[0].id) - } - /> - - - handleUpdateStream('videoKiloBit', value, streams[0].id) - } - /> -
    -
    - {streams.map((stream) => { - return ( - - ); - })} -
    - -
    - ); -} diff --git a/src/components/modal/configureOutputModal/StreamAccordion.tsx b/src/components/modal/configureOutputModal/StreamAccordion.tsx index 325fba6b..ad054930 100644 --- a/src/components/modal/configureOutputModal/StreamAccordion.tsx +++ b/src/components/modal/configureOutputModal/StreamAccordion.tsx @@ -7,8 +7,8 @@ import { OutputStream } from './ConfigureOutputModal'; type StreamAccordionProps = { stream: OutputStream; isOnlyStream: boolean; - update: (key: string, value: string, id: string) => void; - onDelete: (id: string, index: number) => void; + update: (key: string, value: string) => void; + onDelete: () => void; }; export default function StreamAccordion({ @@ -24,7 +24,7 @@ export default function StreamAccordion({ } const handleDelete = () => { - onDelete(stream.id, stream.pipelineIndex); + onDelete(); }; return ( @@ -56,26 +56,31 @@ export default function StreamAccordion({
    + update('srt_stream_id', value)} + /> update('srtMode', value, stream.id)} + update={(value) => update('srtMode', value)} /> update('port', value, stream.id)} + update={(value) => update('port', value)} /> update('ip', value, stream.id)} + update={(value) => update('ip', value)} /> update('srtPassphrase', value, stream.id)} + update={(value) => update('srtPassphrase', value)} />
    diff --git a/src/components/modal/renderingEngineModals/CreateHtmlModal.tsx b/src/components/modal/renderingEngineModals/CreateHtmlModal.tsx new file mode 100644 index 00000000..a186d3fa --- /dev/null +++ b/src/components/modal/renderingEngineModals/CreateHtmlModal.tsx @@ -0,0 +1,151 @@ +'use client'; +import { useTranslate } from '../../../i18n/useTranslate'; +import { Modal } from '../Modal'; +import Input from '../../input/Input'; +import React, { useEffect, useState } from 'react'; +import { Button } from '../../button/Button'; +import { Loader } from '../../loader/Loader'; + +type CreateHtmlModalProps = { + open: boolean; + loading: boolean; + onAbort: () => void; + onConfirm: (height: number, width: number, url: string) => void; +}; + +export function CreateHtmlModal({ + open, + loading, + onAbort, + onConfirm +}: CreateHtmlModalProps) { + const [height, setHeight] = useState(0); + const [width, setWidth] = useState(0); + const [url, setUrl] = useState(''); + const [heightError, setHeightError] = useState(false); + const [widthError, setWidthError] = useState(false); + + const t = useTranslate(); + + useEffect(() => { + if (height) { + setHeightError(false); + } + }, [height]); + + useEffect(() => { + if (width) { + setWidthError(false); + } + }, [width]); + + const handleCancel = () => { + // Reset all errors + setHeightError(false); + setWidthError(false); + + // Reset all values + setUrl(''); + + onAbort(); + }; + + const handleCreate = () => { + let hasError = false; + + if (!height || height < 20 || height > 8192) { + setHeightError(true); + hasError = true; + } + + if (!width || width < 20 || width > 8192) { + setWidthError(true); + hasError = true; + } + + if (hasError) { + return false; + } + + onConfirm(height, width, url); + handleCancel(); + }; + + const handleInputChange = ( + setter: React.Dispatch>, + errorSetter?: React.Dispatch> + ) => { + return (e: React.ChangeEvent) => { + setter(e.target.value); + if (errorSetter) { + errorSetter(false); + } + }; + }; + + return ( + +

    + {t('rendering_engine.html.create.create_html')} +

    +
    + +

    + {t('rendering_engine.html.create.width')}: +

    + + {widthError &&

    {t('rendering_engine.html.create.width_error')}

    } +
    + +

    + {t('rendering_engine.html.create.height')}: +

    + + {heightError && ( +

    {t('rendering_engine.html.create.height_error')}

    + )} +
    + +

    + {t('rendering_engine.html.create.url')}: +

    + +
    +
    +
    + <> + + {loading ? ( + + ) : ( + + )} + +
    +
    + ); +} diff --git a/src/components/modal/renderingEngineModals/CreateMediaModal.tsx b/src/components/modal/renderingEngineModals/CreateMediaModal.tsx new file mode 100644 index 00000000..32f70459 --- /dev/null +++ b/src/components/modal/renderingEngineModals/CreateMediaModal.tsx @@ -0,0 +1,79 @@ +'use client'; +import { useTranslate } from '../../../i18n/useTranslate'; +import { Modal } from '../Modal'; +import Input from '../../input/Input'; +import React, { useState } from 'react'; +import { Button } from '../../button/Button'; +import { Loader } from '../../loader/Loader'; + +type CreateMediaModalProps = { + open: boolean; + loading: boolean; + onAbort: () => void; + onConfirm: (filename: string) => void; +}; + +export function CreateMediaModal({ + open, + loading, + onAbort, + onConfirm +}: CreateMediaModalProps) { + const [filename, setFilename] = useState(''); + + const t = useTranslate(); + + const handleCancel = () => { + // Reset all values + setFilename(''); + onAbort(); + }; + + const handleCreate = () => { + if (!filename) { + setFilename(''); + } + onConfirm(filename); + handleCancel(); + }; + + return ( + +

    + {t('rendering_engine.media.create.create_media')} +

    +
    + +

    + {t('rendering_engine.media.create.filename')}: +

    + ) => + setFilename(e.target.value) + } + /> +
    +
    +
    + <> + + {loading ? ( + + ) : ( + + )} + +
    +
    + ); +} diff --git a/src/components/productionsList/DeleteProductionButton.tsx b/src/components/productionsList/DeleteProductionButton.tsx index a0dfe898..19a65893 100644 --- a/src/components/productionsList/DeleteProductionButton.tsx +++ b/src/components/productionsList/DeleteProductionButton.tsx @@ -6,44 +6,48 @@ import { useCallback, useState } from 'react'; import { Loader } from '../loader/Loader'; import { useRouter } from 'next/navigation'; import { DeleteModal } from '../modal/DeleteModal'; +import { useDeleteMultiviewLayouts } from '../../hooks/multiviewLayout'; type DeleteProductionButtonProps = { id: string; name: string; isActive: boolean; + locked: boolean; }; export function DeleteProductionButton({ id, name, - isActive + isActive, + locked }: DeleteProductionButtonProps) { const router = useRouter(); const deleteProduction = useDeleteProduction(); + const deleteLayouts = useDeleteMultiviewLayouts(); const [loading, setLoading] = useState(false); const [modalOpen, setModalOpen] = useState(false); - const onClick = useCallback(() => setModalOpen(true), []); const onAbort = useCallback(() => setModalOpen(false), []); const onConfirm = useCallback(async () => { setModalOpen(false); setLoading(true); - deleteProduction(id) + await deleteProduction(id); + deleteLayouts(id) .then(() => router.refresh()) .finally(() => setLoading(false)); - }, [router, deleteProduction, id]); + }, [deleteProduction, id, deleteLayouts, router]); return ( <> + +
    +
    + )) || + (loading && ( +
    + +
    + )) || ( +
    + +
    + + {!success && ( + + )} +
    +
    + )} + +
    + ); +}; + +export default SideNavTeardown; diff --git a/src/components/sideNav/SideNavTooltip.tsx b/src/components/sideNav/SideNavTooltip.tsx new file mode 100644 index 00000000..c56a1383 --- /dev/null +++ b/src/components/sideNav/SideNavTooltip.tsx @@ -0,0 +1,17 @@ +interface SideNavTooltipProps { + label: string; + open: boolean; +} + +const SideNavTooltip = (props: SideNavTooltipProps) => { + const { label, open } = props; + + if (open) return <>; + return ( + + {label} + + ); +}; + +export default SideNavTooltip; diff --git a/src/components/sourceCard/SourceCard.tsx b/src/components/sourceCard/SourceCard.tsx index 6d1ecd85..3f8beb8c 100644 --- a/src/components/sourceCard/SourceCard.tsx +++ b/src/components/sourceCard/SourceCard.tsx @@ -1,56 +1,90 @@ 'use client'; - -import React, { ChangeEvent, KeyboardEvent, useState } from 'react'; -import { IconTrash } from '@tabler/icons-react'; +import React, { ChangeEvent, KeyboardEvent, useContext, useState } from 'react'; +import { IconTrash, IconSettings } from '@tabler/icons-react'; import { SourceReference } from '../../interfaces/Source'; -import { SourceThumbnail } from './SourceThumbnail'; import { useTranslate } from '../../i18n/useTranslate'; import { ISource } from '../../hooks/useDragableItems'; +import ImageComponent from '../image/ImageComponent'; +import { getSourceThumbnail } from '../../utils/source'; +import { GlobalContext } from '../../contexts/GlobalContext'; +import { ConfigureAlignmentLatencyModal } from '../modal/ConfigureAlignmentLatencyModal'; +import { Production } from '../../interfaces/production'; type SourceCardProps = { - source: ISource; - label: string; - onSourceUpdate: (source: SourceReference, sourceItem: ISource) => void; - onSourceRemoval: (source: SourceReference) => void; + source?: ISource; + loading: boolean; + onSourceUpdate: (source: SourceReference) => void; + onSourceRemoval: (source: SourceReference, ingestSource?: ISource) => void; onSelectingText: (bool: boolean) => void; + onConfirm: ( + source: ISource, + data: { + pipeline_uuid: string; + stream_uuid: string; + alignment: number; + latency: number; + }[], + shouldRestart?: boolean + ) => Promise; forwardedRef?: React.LegacyRef; style?: object; - src: string; + sourceRef?: SourceReference; + productionSetup?: Production; }; export default function SourceCard({ source, - label, + loading, onSourceUpdate, onSourceRemoval, onSelectingText, + onConfirm, forwardedRef, - src, - style + style, + sourceRef, + productionSetup }: SourceCardProps) { - const [sourceLabel, setSourceLabel] = useState(label ? label : source.name); + const [sourceLabel, setSourceLabel] = useState(sourceRef?.label || ''); + const [isAlignmentModalOpen, setIsAlignmentModalOpen] = useState(false); const t = useTranslate(); + const { locked } = useContext(GlobalContext); + + const pipelinesAreSelected = + productionSetup?.production_settings.pipelines.some( + (pipeline) => + pipeline.pipeline_id === undefined || pipeline.pipeline_id === '' + ) === false; + const updateText = (event: ChangeEvent) => { setSourceLabel(event.currentTarget.value); }; + const saveText = () => { onSelectingText(false); - // if (source.name === label) { - // return; - // } - if (sourceLabel.length === 0) { - setSourceLabel(source.name); + if (sourceLabel?.length === 0) { + if (source) { + setSourceLabel(source.name); + } else if (sourceRef) { + setSourceLabel(sourceRef.label); + } } - onSourceUpdate( - { + if (source) { + onSourceUpdate({ _id: source._id.toString(), - label: sourceLabel, + type: 'ingest_source', + label: sourceLabel || source.name, input_slot: source.input_slot - }, - source - ); + }); + } else if (sourceRef) { + onSourceUpdate({ + _id: sourceRef._id, + type: sourceRef.type, + label: sourceLabel || sourceRef.label, + input_slot: sourceRef.input_slot + }); + } }; const handleKeyDown = (event: KeyboardEvent) => { @@ -59,15 +93,19 @@ export default function SourceCard({ } }; + const closeAlignmentLatencyModal = () => setIsAlignmentModalOpen(false); + return (
    - -

    - {t('source.ingest', { - ingest: source.ingest_name - })} -

    - + {source && ( + + )} + {!source && sourceRef && ( + + )} + {(source || sourceRef) && ( +

    + {t('source.input_slot', { + input_slot: + sourceRef?.input_slot?.toString() || + source?.input_slot?.toString() || + '' + })} +

    + )} + {source && ( +

    + {t('source.ingest', { + ingest: source.ingest_name + })} +

    + )} + {productionSetup && source && pipelinesAreSelected && ( + + )} + {sourceRef && ( + + )} + {source && productionSetup && ( + + )}
    ); } diff --git a/src/components/sourceCard/SourceThumbnail.tsx b/src/components/sourceCard/SourceThumbnail.tsx deleted file mode 100644 index 5aa7114f..00000000 --- a/src/components/sourceCard/SourceThumbnail.tsx +++ /dev/null @@ -1,41 +0,0 @@ -'use client'; - -import Image from 'next/image'; -import { useState } from 'react'; -import { Source } from '../../interfaces/Source'; -import { IconExclamationCircle } from '@tabler/icons-react'; - -type SourceThumbnailProps = { - source: Source; - src: string; -}; - -export function SourceThumbnail({ source, src }: SourceThumbnailProps) { - const [loaded, setLoaded] = useState(false); - - if (source.status === 'gone') { - return ( -
    - -
    - ); - } - - return ( - Preview Thumbnail setLoaded(true)} - onError={() => setLoaded(true)} - placeholder="empty" - width={0} - height={0} - sizes="20vh" - style={{ - width: 'auto', - height: '100%' - }} - /> - ); -} diff --git a/src/components/sourceCards/SourceCards.tsx b/src/components/sourceCards/SourceCards.tsx index 9666bccc..cd2901a3 100644 --- a/src/components/sourceCards/SourceCards.tsx +++ b/src/components/sourceCards/SourceCards.tsx @@ -5,35 +5,45 @@ import { SourceReference } from '../../interfaces/Source'; import { Production } from '../../interfaces/production'; import DragItem from '../dragElement/DragItem'; import SourceCard from '../sourceCard/SourceCard'; -import { EmptySlotCard } from '../emptySlotCard/EmptySlotCard'; import { ISource, useDragableItems } from '../../hooks/useDragableItems'; +import { EmptySlotCard } from '../emptySlotCard/EmptySlotCard'; export default function SourceCards({ productionSetup, + locked, + loading, updateProduction, onSourceUpdate, - onSourceRemoval + onSourceRemoval, + onConfirm }: { productionSetup: Production; + locked: boolean; + loading: boolean; updateProduction: (updated: Production) => void; - onSourceUpdate: (source: SourceReference, sourceItem: ISource) => void; - onSourceRemoval: (source: SourceReference) => void; + onSourceUpdate: (source: SourceReference) => void; + onSourceRemoval: (source: SourceReference, ingestSource?: ISource) => void; + onConfirm: ( + source: ISource, + data: { + pipeline_uuid: string; + stream_uuid: string; + alignment: number; + latency: number; + }[], + shouldRestart?: boolean + ) => Promise; }) { - const [items, moveItem, loading] = useDragableItems(productionSetup.sources); + const [items, moveItem] = useDragableItems(productionSetup.sources); + const itemsToAdd: SourceReference[] = []; const [selectingText, setSelectingText] = useState(false); - const currentOrder: SourceReference[] = items.map((source) => { - return { - _id: source._id.toString(), - label: source.label, - input_slot: source.input_slot, - stream_uuids: source.stream_uuids - }; - }); - + if (!items) return null; + const isISource = (source: SourceReference | ISource): source is ISource => { + return 'src' in source; + }; const gridItems: React.JSX.Element[] = []; let tempItems = [...items]; let firstEmptySlot = items.length + 1; - if (!items || items.length === 0) return null; for (let i = 0; i < items[items.length - 1].input_slot; i++) { if (!items.some((source) => source.input_slot === i + 1)) { @@ -41,59 +51,80 @@ export default function SourceCards({ break; } } - for (let i = 0; i < items[items.length - 1].input_slot; i++) { - // console.log(`On input slot: ${i + 1}`); - // console.log(`Checking sources:`); - // console.log(tempItems); + const productionSources = productionSetup.sources; + + for (const item of items) { + if (isISource(item)) { + const itemId = + typeof item._id === 'string' ? item._id : item._id.toString(); + const itemAsRef: SourceReference = { + _id: itemId, + type: 'ingest_source', + label: item.label, + stream_uuids: item.stream_uuids, + input_slot: item.input_slot + }; + itemsToAdd.push(itemAsRef); + } else { + itemsToAdd.push(item); + } + } + + for (let i = 0; i < itemsToAdd[itemsToAdd.length - 1].input_slot; i++) { tempItems.every((source) => { + const id = source._id ? source._id : ''; + const isSource = isISource(source); if (source.input_slot === i + 1) { - // console.log(`Found source on input slot: ${i + 1}`); - // console.log(`Removing source "${source.name}" from sources list`); tempItems = tempItems.filter((i) => i._id !== source._id); - // console.log(`Adding source "${source.name}" to grid`); - if (!productionSetup.isActive) { + if (!productionSetup.isActive && !locked) { gridItems.push( s._id === source._id) + : source + } onSourceUpdate={onSourceUpdate} onSourceRemoval={onSourceRemoval} - onSelectingText={(isSelecting: boolean) => - setSelectingText(isSelecting) - } + onSelectingText={(isSelecting) => setSelectingText(isSelecting)} + productionSetup={productionSetup} + onConfirm={onConfirm} + loading={loading} /> ); } else { gridItems.push( s._id === source._id) + : source + } onSourceUpdate={onSourceUpdate} onSourceRemoval={onSourceRemoval} - onSelectingText={(isSelecting: boolean) => - setSelectingText(isSelecting) - } + onSelectingText={(isSelecting) => setSelectingText(isSelecting)} + onConfirm={onConfirm} + productionSetup={productionSetup} + loading={loading} /> ); } return false; } else { - // console.log(`No source found on input slot: ${i + 1}`); - // console.log(`Adding empty slot to grid`); if (productionSetup.isActive) { gridItems.push( ); } - return false; } }); diff --git a/src/components/inventory/Inventory.module.scss b/src/components/sourceList/SourceList.module.scss similarity index 100% rename from src/components/inventory/Inventory.module.scss rename to src/components/sourceList/SourceList.module.scss diff --git a/src/components/sourceList/SourceList.tsx b/src/components/sourceList/SourceList.tsx new file mode 100644 index 00000000..61eee7a2 --- /dev/null +++ b/src/components/sourceList/SourceList.tsx @@ -0,0 +1,93 @@ +'use client'; + +import { useState } from 'react'; +import { SourceWithId } from '../../interfaces/Source'; +import FilterContext from '../../contexts/FilterContext'; +import SourceListItem from '../sourceListItem/SourceListItem'; +import FilterOptions from '../filter/FilterOptions'; +import styles from './SourceList.module.scss'; +import { IconX } from '@tabler/icons-react'; + +interface SourceListProps { + sources: Map; + inventoryVisible?: boolean; + onClose?: () => void; + isDisabledFunc?: (source: SourceWithId) => boolean; + action?: (source: SourceWithId) => void; + actionText: string; + locked: boolean; +} +const SourceList: React.FC = (props) => { + const { + sources, + inventoryVisible = true, + onClose, + isDisabledFunc, + action, + actionText, + locked + } = props; + + const [filteredSources, setFilteredSources] = + useState>(sources); + + function getSourcesToDisplay( + filteredSources: Map + ): React.ReactNode { + return Array.from( + filteredSources.size >= 0 ? filteredSources.values() : sources.values() + ).map((source, index) => { + if (source.status !== 'purge') { + return ( + + ); + } + }); + } + + return ( + +
    +
    +
    +
    + ) => + setFilteredSources(new Map(filtered)) + } + /> + {onClose && ( + + )} +
    +
      + {getSourcesToDisplay(filteredSources)} +
    +
    +
    +
    +
    + ); +}; + +export default SourceList; diff --git a/src/components/sourceListItem/PreviewThumbnail.tsx b/src/components/sourceListItem/PreviewThumbnail.tsx deleted file mode 100644 index dcda882d..00000000 --- a/src/components/sourceListItem/PreviewThumbnail.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import Image from 'next/image'; -import { useState } from 'react'; - -type PreviewProps = { src: string }; - -export const PreviewThumbnail = ({ src }: PreviewProps) => { - const [loaded, setLoaded] = useState(false); - - return ( -
    - Preview Thumbnail setLoaded(true)} - placeholder="empty" - width={0} - height={0} - sizes="20vh" - style={{ - width: 'auto', - height: '100%' - }} - /> -
    - ); -}; diff --git a/src/components/sourceListItem/SourceListItem.tsx b/src/components/sourceListItem/SourceListItem.tsx index 6e9aadf7..9189b83b 100644 --- a/src/components/sourceListItem/SourceListItem.tsx +++ b/src/components/sourceListItem/SourceListItem.tsx @@ -1,7 +1,5 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { Source, SourceWithId } from '../../interfaces/Source'; -import { PreviewThumbnail } from './PreviewThumbnail'; -import { getSourceThumbnail } from '../../utils/source'; +import React, { useEffect, useState } from 'react'; +import { SourceWithId } from '../../interfaces/Source'; import videoSettings from '../../utils/videoSettings'; import { getHertz } from '../../utils/stream'; import { useTranslate } from '../../i18n/useTranslate'; @@ -11,73 +9,34 @@ import Outputs from '../inventory/editView/AudioChannels/Outputs'; import { mapAudio } from '../../utils/audioMapping'; import { oneBased } from '../inventory/editView/AudioChannels/utils'; import capitalize from '../../utils/capitalize'; +import { SourceListItemThumbnail } from './SourceListItemThumbnail'; type SourceListItemProps = { source: SourceWithId; - action: (source: SourceWithId) => void; - edit?: boolean; + action?: (source: SourceWithId) => void; + actionText: string; disabled: unknown; + locked: boolean; }; -const getIcon = (source: Source) => { - const isGone = source.status === 'gone'; - const className = isGone ? 'text-error' : 'text-brand'; - - const types = { - camera: ( - - ), - microphone: ( - - ), - graphics: ( - - ) - }; - - return types[source.type]; -}; - -function InventoryListItem({ +function SourceListItem({ source, action, disabled, - edit = false + locked, + actionText }: SourceListItemProps) { const t = useTranslate(); - const [previewVisible, setPreviewVisible] = useState(false); + const [outputRows, setOutputRows] = useState< { id: string; value: string }[][] >([]); - const timeoutRef = useRef(); const { video_stream: videoStream, audio_stream: audioStream } = source; const { width, height, frame_rate: frameRate } = videoStream || {}; const { sample_rate: sampleRate, number_of_channels: numberOfChannels = 0 } = audioStream || {}; - useEffect(() => { - return () => clearTimeout(timeoutRef.current); - }, []); - - const onMouseEnter = useCallback(() => { - timeoutRef.current = setTimeout(() => setPreviewVisible(true), 1000); - }, []); - - const onMouseLeave = useCallback(() => { - setPreviewVisible(false); - clearTimeout(timeoutRef.current); - }, []); - const style = { textWrap: 'wrap' } as React.CSSProperties; const channelsInArray: number[] = []; @@ -95,22 +54,19 @@ function InventoryListItem({ : [] ); } - }, [source.audio_stream.audio_mapping]); + }, [source?.audio_stream.audio_mapping]); return (
  • (disabled || !action ? '' : action(source))} > - {source.status !== 'gone' && - source.type === 'camera' && - previewVisible && }
    -
    -
    {getIcon(source)}
    +
    +
    ))} @@ -176,37 +133,29 @@ function InventoryListItem({ ) : null}
    -
    - {!disabled ? ( -
    - -
    - ) : null} +
    + {actionText === 'edit' && ( + + )} + {actionText === 'add' && ( + + )}
  • ); } -export default InventoryListItem; +export default SourceListItem; diff --git a/src/components/sourceListItem/SourceListItemThumbnail.tsx b/src/components/sourceListItem/SourceListItemThumbnail.tsx new file mode 100644 index 00000000..3df28d57 --- /dev/null +++ b/src/components/sourceListItem/SourceListItemThumbnail.tsx @@ -0,0 +1,50 @@ +import { Source, SourceWithId } from '../../interfaces/Source'; +import { getSourceThumbnail } from '../../utils/source'; +import Icons from '../icons/Icons'; +import ImageComponent from '../image/ImageComponent'; + +type SourceThumbnailProps = { source: SourceWithId }; + +export const SourceListItemThumbnail = (props: SourceThumbnailProps) => { + const { source } = props; + + const getIcon = (source: Source) => { + const isGone = source.status === 'gone' || source.status === 'purge'; + const className = isGone ? 'text-error' : 'text-brand'; + + const types = { + camera: ( + + ), + microphone: ( + + ), + graphics: ( + + ) + }; + + return types[source.type]; + }; + + return ( +
    + {/* TODO perhaps add alts to translations */} + +
    {getIcon(source)}
    +
    +
    + ); +}; diff --git a/src/components/startProduction/ConfigureOutputButton.tsx b/src/components/startProduction/ConfigureOutputButton.tsx index 1ded9838..30761801 100644 --- a/src/components/startProduction/ConfigureOutputButton.tsx +++ b/src/components/startProduction/ConfigureOutputButton.tsx @@ -1,11 +1,12 @@ 'use client'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { IconSettings } from '@tabler/icons-react'; import { Preset } from '../../interfaces/preset'; import { useTranslate } from '../../i18n/useTranslate'; import { Button } from '../button/Button'; import { ConfigureOutputModal } from '../modal/configureOutputModal/ConfigureOutputModal'; + type ConfigureOutputButtonProps = { preset?: Preset; disabled?: boolean; @@ -18,23 +19,40 @@ export function ConfigureOutputButton({ disabled }: ConfigureOutputButtonProps) { const [modalOpen, setModalOpen] = useState(false); + const [isDisabled, setIsDisabled] = useState( + !preset || disabled || false + ); const toggleConfigModal = () => { if (preset) { setModalOpen((state) => !state); } }; + + useEffect(() => { + if (!preset || disabled) { + setIsDisabled(true); + } else { + setIsDisabled(false); + } + }, [preset, disabled]); + const t = useTranslate(); return ( <> - {preset && ( + {preset && !isDisabled && ( (); + + useEffect(() => { + if (production && modalOpen) { + updateSourceInputSlotOnMultiviewLayouts(production).then( + (updatedSetup) => { + if (!updatedSetup) return; + setProductionSetup(updatedSetup); + refreshProduction(); + } + ); + } + }, [modalOpen]); const onClick = () => { if (!production) return; @@ -80,50 +96,58 @@ export function StartProductionButton({ setStartProductionStatus(undefined); clearTimeout(timeout.current); }, []); + const onConfirm = useCallback(() => { - if (!production) { + if (!productionSetup) { return; } let productionToStart: Production; - if (!production.production_settings.pipelines[0].multiviews) { - if (!multiviewPresets || multiviewPresets.length === 0) { + if (!productionSetup.production_settings.pipelines[0].multiviews) { + if (!multiviewLayouts || multiviewLayouts.length === 0) { toast.error(t('start_production_status.unexpected')); return; } const pipelineToUpdateMultiview = - production.production_settings.pipelines[0]; + productionSetup.production_settings.pipelines[0]; productionToStart = { - ...production, - sources: production.sources.map((source, i) => ({ + ...productionSetup, + sources: productionSetup.sources.map((source, i) => ({ ...source, input_slot: i + 1 })), production_settings: { - ...production.production_settings, + ...productionSetup.production_settings, pipelines: [ - ...production.production_settings.pipelines.filter( + ...productionSetup.production_settings.pipelines.filter( (p) => p.pipeline_name !== pipelineToUpdateMultiview.pipeline_name ), { ...pipelineToUpdateMultiview, - multiviews: [{ ...multiviewPresets[0], for_pipeline_idx: 0 }] + multiviews: [ + { + ...multiviewLayouts[0], + for_pipeline_idx: 0, + _id: multiviewLayouts[0]._id?.toString() + } + ] } ] } }; } else { productionToStart = { - ...production, - sources: production.sources.map((source, i) => ({ + ...productionSetup, + sources: productionSetup.sources.map((source, i) => ({ ...source, input_slot: i + 1 })) }; } + startProduction(productionToStart) .then((status) => { if (status.ok) { - console.log(`Starting production '${production.name}'`); + console.log(`Starting production '${productionSetup.name}'`); refreshProduction(); refresh('/'); setModalOpen(false); @@ -146,7 +170,7 @@ export function StartProductionButton({ steps: [{ step: 'start', success: false }] }); }); - }, [startProduction, production]); + }, [startProduction, productionSetup]); const onStopConfirm = async () => { if (!production) return; @@ -194,8 +218,13 @@ export function StartProductionButton({ return ( <> diff --git a/src/components/startProduction/presetDropdown.tsx b/src/components/startProduction/presetDropdown.tsx index b8191285..f600869f 100644 --- a/src/components/startProduction/presetDropdown.tsx +++ b/src/components/startProduction/presetDropdown.tsx @@ -50,7 +50,7 @@ export const PresetDropdown = ({
      void; + toggleLocked: () => void; +} + +export const GlobalContext = createContext({ + locked: false, + imageRefetchKey: 0, + refetchImages: () => { + // outsmarting lint + }, + toggleLocked: () => { + // outsmarting lint + } +}); + +export const GlobalContextProvider = (props: PropsWithChildren) => { + const { children } = props; + const [locked, setLocked] = useState(true); + const [imageRefetchKey, setImageRefetchKey] = useState( + new Date().getTime() + ); + + const refetchImages = () => { + setImageRefetchKey(new Date().getTime()); + }; + + const toggleLocked = () => { + setLocked(!locked); + }; + + return ( + + + {children} + + + ); +}; diff --git a/src/hooks/controlPanels.ts b/src/hooks/controlPanels.ts index 21f8100d..3accbf7e 100644 --- a/src/hooks/controlPanels.ts +++ b/src/hooks/controlPanels.ts @@ -1,6 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; import { DataHook } from './types'; import { ResourcesControlPanelResponse } from '../../types/ateliere-live'; +import { API_SECRET_KEY } from '../utils/constants'; const ONE_MINUTE = 1000 * 60; export function useControlPanels(): [ ...DataHook, @@ -18,8 +19,7 @@ export function useControlPanels(): [ setLoading(true); fetch('/api/manager/controlpanels', { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }) .then(async (response) => { if (response.ok) { diff --git a/src/hooks/ingests.ts b/src/hooks/ingests.ts new file mode 100644 index 00000000..3969192d --- /dev/null +++ b/src/hooks/ingests.ts @@ -0,0 +1,118 @@ +import { useState, useEffect } from 'react'; +import { API_SECRET_KEY } from '../utils/constants'; +import { + ResourcesIngestResponse, + ResourcesIngestStreamResponse, + ResourcesSourceResponse +} from '../../types/ateliere-live'; +import { CallbackHook } from './types'; + +export function useIngests(): ResourcesIngestResponse[] { + const [ingests, setIngests] = useState([]); + + useEffect(() => { + fetch('/api/manager/ingests/', { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }).then(async (response) => { + if (response.ok) { + const fetchedIngests = + (await response.json()) as ResourcesIngestResponse[]; + setIngests(fetchedIngests); + } + }); + return; + }, []); + return ingests; +} + +export function useIngestStreams() { + return async ( + ingest_name: string, + ingest_source_name: string + ): Promise => { + if (ingest_name) { + try { + const response = await fetch( + `/api/manager/ingests/streams/${ingest_name}/${ingest_source_name}`, + { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ); + if (response.ok) { + return await response.json(); + } else { + throw new Error(await response.text()); + } + } catch (error) { + console.error(`Error fetching ingest streams: ${error}`); + throw error; + } + } else { + throw new Error('Missing parameters: ingest_name'); + } + }; +} + +export function useIngestSourceId(): CallbackHook< + (ingest_name: string, ingest_source_name: string) => Promise +> { + const [loading, setLoading] = useState(false); + const getIngestSourceId = async ( + ingest_name: string, + ingest_source_name: string + ): Promise => { + try { + setLoading(true); + const response = await fetch( + `/api/manager/ingests/source_id/${ingest_name}/${ingest_source_name}`, + { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ); + setLoading(false); + if (response.ok) { + return await response.json(); + } else { + throw new Error(await response.text()); + } + } catch (error) { + console.error(`Error fetching ingest source id: ${error}`); + throw error; + } + }; + return [getIngestSourceId, loading]; +} + +export function useIngestSources(): CallbackHook< + (ingestName: string) => Promise +> { + const [loading, setLoading] = useState(false); + + const getIngestSources = async ( + ingestName: string + ): Promise => { + try { + setLoading(true); + const response = await fetch( + `/api/manager/ingests/sources/${ingestName}`, + { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ); + setLoading(false); + if (response.ok) { + return await response.json(); + } else { + throw new Error(await response.text()); + } + } catch (error) { + console.error(`Error fetching ingest sources: ${error}`); + throw error; + } + }; + return [getIngestSources, loading]; +} diff --git a/src/hooks/items/addSetupItem.ts b/src/hooks/items/addSetupItem.ts index 2dda22bc..c6d496c8 100644 --- a/src/hooks/items/addSetupItem.ts +++ b/src/hooks/items/addSetupItem.ts @@ -14,22 +14,27 @@ export function addSetupItem( ...productionSetup.sources, { _id: source._id, + type: source.type, label: source.label, stream_uuids: source.stream_uuids, - input_slot: source.input_slot + input_slot: source.input_slot, + html_data: source.html_data, + media_data: source.media_data } ].sort((a, b) => a.input_slot - b.input_slot) }; - return { ...updatedSetup, sources: [ ...productionSetup.sources, { _id: source._id, + type: source.type, label: source.label, stream_uuids: source.stream_uuids, - input_slot: source.input_slot + input_slot: source.input_slot, + html_data: source.html_data, + media_data: source.media_data } ].sort((a, b) => a.input_slot - b.input_slot) }; diff --git a/src/hooks/items/removeSetupItem.ts b/src/hooks/items/removeSetupItem.ts index e3b5c56b..a490ab11 100644 --- a/src/hooks/items/removeSetupItem.ts +++ b/src/hooks/items/removeSetupItem.ts @@ -3,16 +3,37 @@ import { Production } from '../../interfaces/production'; export function removeSetupItem( source: SourceReference, - productionSetup: Production -) { + productionSetup: Production, + ingestSourceName?: string, + ingestName?: string +): Production | null { const tempItems = productionSetup.sources.filter( (tempItem) => tempItem._id !== source._id ); - const updatedSetup = { + let updatedPipelines = productionSetup.production_settings.pipelines; + + if (ingestSourceName !== undefined && ingestName !== undefined) { + updatedPipelines = productionSetup.production_settings.pipelines.map( + (pipeline) => ({ + ...pipeline, + sources: pipeline.sources + ? pipeline.sources.filter( + (pipelineSource) => + pipelineSource.ingest_source_name !== ingestSourceName || + pipelineSource.ingest_name !== ingestName + ) + : [] + }) + ); + } + + return { ...productionSetup, - sources: tempItems + sources: tempItems, + production_settings: { + ...productionSetup.production_settings, + pipelines: updatedPipelines + } }; - - return updatedSetup; } diff --git a/src/hooks/monitoring.ts b/src/hooks/monitoring.ts index 8986bea3..2814a801 100644 --- a/src/hooks/monitoring.ts +++ b/src/hooks/monitoring.ts @@ -1,6 +1,7 @@ import { useCallback, useEffect, useState } from 'react'; import { Monitoring } from '../interfaces/monitoring'; import { CallbackHook, DataHook, MonitoringHook } from './types'; +import { API_SECRET_KEY } from '../utils/constants'; const TIMEOUT = 5000; export function useMonitoring(id: string): DataHook { @@ -29,8 +30,7 @@ export function useMonitoring(id: string): DataHook { const getMonitoring = async (id: string): Promise => { const response = await fetch(`/api/manager/monitoring/${id}`, { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }); if (response.ok) { return response.json(); @@ -48,8 +48,7 @@ export function useDeleteMonitoring(): CallbackHook< return fetch(`/api/manager/monitoring/${productionId}`, { method: 'DELETE', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }) .then((response) => { if (response.ok) { diff --git a/src/hooks/multiviewLayout.ts b/src/hooks/multiviewLayout.ts new file mode 100644 index 00000000..974ca28a --- /dev/null +++ b/src/hooks/multiviewLayout.ts @@ -0,0 +1,103 @@ +import { useCallback, useEffect, useState } from 'react'; +import { TMultiviewLayout } from '../interfaces/preset'; +import { DataHook } from './types'; +import { WithId } from 'mongodb'; +import { API_SECRET_KEY } from '../utils/constants'; + +export function useGetMultiviewLayouts() { + return async (): Promise => { + const response = await fetch(`/api/manager/multiviews`, { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }); + if (response.ok) { + return response.json(); + } + throw await response.text(); + }; +} + +export function useGetMultiviewLayout() { + return async (id: string): Promise> => { + const response = await fetch(`/api/manager/multiviews/${id}`, { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }); + if (response.ok) { + return await response.json(); + } + throw await response.text(); + }; +} + +export function useMultiviewLayouts( + refresh: boolean +): DataHook { + const [loading, setLoading] = useState(true); + const [multiviewLayouts, setmultiviewLayouts] = useState( + [] + ); + + useEffect(() => { + setmultiviewLayouts([]); + + if (!refresh) { + return; + } + + setLoading(true); + fetch('/api/manager/multiviews', { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }) + .then(async (response) => { + if (response.ok) { + setmultiviewLayouts(await response.json()); + } + }) + .finally(() => setLoading(false)); + }, [refresh]); + + return [multiviewLayouts, loading, undefined]; +} + +export function usePutMultiviewLayout() { + return async (newMultiviewLayout: TMultiviewLayout): Promise => { + const response = await fetch('/api/manager/multiviews', { + method: 'PUT', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify(newMultiviewLayout) + }); + if (response.ok) { + return; + } + throw await response.text(); + }; +} + +export function useDeleteMultiviewLayout() { + return async (id: string): Promise => { + const response = await fetch(`/api/manager/multiviews/${id}`, { + method: 'DELETE', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }); + if (response.ok) { + return; + } + throw await response.text(); + }; +} + +export function useDeleteMultiviewLayouts() { + return async (id: string): Promise => { + const response = await fetch(`/api/manager/multiviews`, { + method: 'DELETE', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify(id) + }); + if (response.ok) { + return; + } + throw await response.text(); + }; +} diff --git a/src/hooks/multiviewPreset.ts b/src/hooks/multiviewPreset.ts index 78b8b21a..a31a616d 100644 --- a/src/hooks/multiviewPreset.ts +++ b/src/hooks/multiviewPreset.ts @@ -2,12 +2,13 @@ import { useEffect, useState } from 'react'; import { MultiviewPreset } from '../interfaces/preset'; import { DataHook } from './types'; import { WithId } from 'mongodb'; +import { API_SECRET_KEY } from '../utils/constants'; + export function useGetMultiviewPresets() { return async (): Promise => { - const response = await fetch(`/api/manager/multiviews`, { + const response = await fetch(`/api/manager/multiview-preset`, { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }); if (response.ok) { return response.json(); @@ -18,10 +19,9 @@ export function useGetMultiviewPresets() { export function useGetMultiviewPreset() { return async (id: string): Promise> => { - const response = await fetch(`/api/manager/multiviews/${id}`, { + const response = await fetch(`/api/manager/multiview-presets/${id}`, { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }); if (response.ok) { return await response.json(); @@ -38,10 +38,9 @@ export function useMultiviewPresets(): DataHook { useEffect(() => { setLoading(true); - fetch('/api/manager/multiviews', { + fetch('/api/manager/multiview-presets', { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }) .then(async (response) => { if (response.ok) { diff --git a/src/hooks/multiviews.ts b/src/hooks/multiviews.ts index 009dcfb1..b0ad55da 100644 --- a/src/hooks/multiviews.ts +++ b/src/hooks/multiviews.ts @@ -31,7 +31,7 @@ export function useMultiviews(): CallbackHook< const viewsToUpdate = singleMultiview.layout.views.filter( (v) => v.input_slot === source.input_slot ); - console.log(viewsToUpdate); + const updatedViewsWithLabels = viewsToUpdate.map((v) => { return { ...v, diff --git a/src/hooks/pipelines.ts b/src/hooks/pipelines.ts index f9ef473a..39d2fd81 100644 --- a/src/hooks/pipelines.ts +++ b/src/hooks/pipelines.ts @@ -2,14 +2,16 @@ import { useCallback, useEffect, useState } from 'react'; import { DataHook } from './types'; import { ResourcesCompactPipelineResponse } from '../../types/ateliere-live'; import { ManagerPipelineResponse } from '../interfaces/pipeline'; +import { API_SECRET_KEY } from '../utils/constants'; const ONE_MINUTE = 1000 * 60; +type ModifiedDataHook = [DataType | undefined, boolean]; + async function getPipeline(id: string): Promise { return fetch(`/api/manager/pipelines/${id}`, { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }).then(async (response) => { if (response.ok) { return response.json(); @@ -64,8 +66,7 @@ export function usePipelines(): [ setLoading(true); fetch('/api/manager/pipelines', { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }) .then(async (response) => { if (response.ok) { @@ -86,3 +87,29 @@ export function usePipelines(): [ refresh ]; } + +export function GetPipelines(): [ + ...ModifiedDataHook +] { + const [loading, setLoading] = useState(true); + const [pipelines, setPipelines] = useState< + ResourcesCompactPipelineResponse[] + >([]); + useEffect(() => { + setLoading(true); + fetch('/api/manager/pipelines', { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }) + .then(async (response) => { + if (response.ok) { + setPipelines((await response.json()).pipelines); + } + }) + .finally(() => { + setLoading(false); + }); + }, []); + + return [pipelines.sort((a, b) => a.name.localeCompare(b.name)), loading]; +} diff --git a/src/hooks/presets.ts b/src/hooks/presets.ts index 6134856a..595b20d2 100644 --- a/src/hooks/presets.ts +++ b/src/hooks/presets.ts @@ -1,11 +1,12 @@ import { Preset, PresetWithId } from '../interfaces/preset'; import { ObjectId } from 'mongodb'; +import { API_SECRET_KEY } from '../utils/constants'; + export function useGetPresets() { return async (): Promise => { const response = await fetch(`/api/manager/presets`, { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }); if (response.ok) { return response.json(); @@ -18,8 +19,7 @@ export function usePutPreset() { return async (id: ObjectId, preset: Preset): Promise => { const response = await fetch(`/api/manager/presets/${id}`, { method: 'PUT', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]], + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], body: JSON.stringify(preset) }); if (response.ok) { diff --git a/src/hooks/productions.ts b/src/hooks/productions.ts index e3164fe9..f3e26867 100644 --- a/src/hooks/productions.ts +++ b/src/hooks/productions.ts @@ -1,17 +1,16 @@ import { ObjectId } from 'mongodb'; import { Production } from '../interfaces/production'; +import { API_SECRET_KEY } from '../utils/constants'; export function usePostProduction() { return async (name: string): Promise => { const response = await fetch('/api/manager/productions', { method: 'POST', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]], + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], body: JSON.stringify({ isActive: false, name, - sources: [], - selectedPresetRef: undefined + sources: [] }) }); if (response.ok) { @@ -25,8 +24,7 @@ export function useGetProduction() { return async (id: string): Promise => { const response = await fetch(`/api/manager/productions/${id}`, { method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }); if (response.ok) { return response.json(); @@ -36,15 +34,14 @@ export function useGetProduction() { } export function usePutProduction() { - return async (id: string, production: Production): Promise => { + return async (id: string, production: Production): Promise => { const response = await fetch(`/api/manager/productions/${id}`, { method: 'PUT', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]], + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], body: JSON.stringify(production) }); if (response.ok) { - return; + return response.json(); } throw await response.text(); }; @@ -54,8 +51,7 @@ export function useDeleteProduction() { return async (id: string): Promise => { const response = await fetch(`/api/manager/productions/${id}`, { method: 'DELETE', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }); if (response.ok) { return; @@ -63,3 +59,81 @@ export function useDeleteProduction() { throw await response.text(); }; } + +export function useGetProductionSourceAlignmentAndLatency() { + return async ( + id: string, + pipelineId: string, + ingestName: string, + ingestSourceName: string + ): Promise<{ alignment: number; latency: number } | null> => { + const response = await fetch( + `/api/manager/productions/${id}/${pipelineId}/${ingestName}/${ingestSourceName}`, + { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ); + if (response.ok) { + const text = await response.text(); + const data = text ? JSON.parse(text) : null; + return data ? { alignment: data.alignment, latency: data.latency } : null; + } + + throw await response.text(); + }; +} + +export function usePutProductionPipelineSourceAlignmentAndLatency() { + return async ( + id: string, + pipelineId: string, + ingestName: string, + ingestSourceName: string, + alignment: number, + latency: number + ): Promise => { + const response = await fetch( + `/api/manager/productions/${id}/${pipelineId}/${ingestName}/${ingestSourceName}`, + { + method: 'PUT', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ + alignment_ms: alignment, + max_network_latency_ms: latency + }) + } + ); + + if (response.status === 204) { + return; + } + + if (response.ok) { + return await response.json(); + } + + throw await response.text(); + }; +} + +export function useReplaceProductionSourceStreamIds() { + return async ( + id: string, + sourceId: string | ObjectId, + stream_uuids: string[] + ): Promise => { + const response = await fetch( + `/api/manager/productions/${id}/sources/${sourceId}`, + { + method: 'PUT', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ stream_uuids }) + } + ); + if (response.ok) { + return; + } + throw await response.text(); + }; +} diff --git a/src/hooks/renderingEngine/useCreateHtmlSource.tsx b/src/hooks/renderingEngine/useCreateHtmlSource.tsx new file mode 100644 index 00000000..55c67518 --- /dev/null +++ b/src/hooks/renderingEngine/useCreateHtmlSource.tsx @@ -0,0 +1,49 @@ +import { useState } from 'react'; +import { + AddRenderingEngineSourceResult, + SourceReference +} from '../../interfaces/Source'; +import { Production } from '../../interfaces/production'; +import { CallbackHook } from '../types'; +import { Result } from '../../interfaces/result'; +import { API_SECRET_KEY } from '../../utils/constants'; +import { HTMLSource } from '../../interfaces/renderingEngine'; + +export function useCreateHtmlSource(): CallbackHook< + ( + production: Production, + inputSlot: number, + htmlBody: HTMLSource, + source: SourceReference + ) => Promise> +> { + const [loading, setLoading] = useState(false); + + const createHtmlSource = async ( + production: Production, + inputSlot: number, + htmlBody: HTMLSource, + source: SourceReference + ): Promise> => { + setLoading(true); + + return fetch(`/api/manager/rendering-engine/html`, { + method: 'POST', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ + production: production, + inputSlot: inputSlot, + htmlBody: htmlBody, + source: source + }) + }) + .then(async (response) => { + if (response.ok) { + return response.json(); + } + throw await response.text(); + }) + .finally(() => setLoading(false)); + }; + return [createHtmlSource, loading]; +} diff --git a/src/hooks/renderingEngine/useCreateMediaSource.tsx b/src/hooks/renderingEngine/useCreateMediaSource.tsx new file mode 100644 index 00000000..c98a0e0d --- /dev/null +++ b/src/hooks/renderingEngine/useCreateMediaSource.tsx @@ -0,0 +1,50 @@ +import { useState } from 'react'; +import { + AddRenderingEngineSourceResult, + SourceReference +} from '../../interfaces/Source'; +import { Production } from '../../interfaces/production'; +import { CallbackHook } from '../types'; +import { Result } from '../../interfaces/result'; +import { API_SECRET_KEY } from '../../utils/constants'; +import { MediaSource } from '../../interfaces/renderingEngine'; + +export function useCreateMediaSource(): CallbackHook< + ( + production: Production, + inputSlot: number, + mediaBody: MediaSource, + source: SourceReference + ) => Promise> +> { + const [loading, setLoading] = useState(false); + + const createMediaSource = async ( + production: Production, + inputSlot: number, + mediaBody: MediaSource, + source: SourceReference + ): Promise> => { + setLoading(true); + + return fetch(`/api/manager/rendering-engine/media`, { + method: 'POST', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ + production: production, + inputSlot: inputSlot, + mediaBody: mediaBody, + source: source + }) + }) + .then(async (response) => { + if (response.ok) { + const text = await response.text(); + return text ? JSON.parse(text) : {}; + } + throw await response.text(); + }) + .finally(() => setLoading(false)); + }; + return [createMediaSource, loading]; +} diff --git a/src/hooks/renderingEngine/useDeleteHtmlSource.tsx b/src/hooks/renderingEngine/useDeleteHtmlSource.tsx new file mode 100644 index 00000000..79d712a5 --- /dev/null +++ b/src/hooks/renderingEngine/useDeleteHtmlSource.tsx @@ -0,0 +1,41 @@ +import { Result } from '../../interfaces/result'; +import { DeleteRenderingEngineSourceStep } from '../../interfaces/Source'; +import { API_SECRET_KEY } from '../../utils/constants'; +import { CallbackHook } from '../types'; +import { useState } from 'react'; + +export function useDeleteHtmlSource(): CallbackHook< + ( + pipelineUuid: string, + inputSlot: number, + ldPipelineId: string + ) => Promise> +> { + const [loading, setLoading] = useState(false); + + const deleteHtmlSource = async ( + pipelineUuid: string, + inputSlot: number, + ldPipelineId: string + ): Promise> => { + setLoading(true); + + return fetch( + `/api/manager/pipelines/${pipelineUuid}/rendering-engine/html/${inputSlot}/${ldPipelineId}`, + { + method: 'DELETE', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ) + .then(async (response) => { + if (response.ok) { + const text = await response.text(); + const data = text ? JSON.parse(text) : null; + return data ? data : null; + } + throw await response.text; + }) + .finally(() => setLoading(false)); + }; + return [deleteHtmlSource, loading]; +} diff --git a/src/hooks/renderingEngine/useDeleteMediaSource.tsx b/src/hooks/renderingEngine/useDeleteMediaSource.tsx new file mode 100644 index 00000000..e56f4b2b --- /dev/null +++ b/src/hooks/renderingEngine/useDeleteMediaSource.tsx @@ -0,0 +1,41 @@ +import { Result } from '../../interfaces/result'; +import { DeleteRenderingEngineSourceStep } from '../../interfaces/Source'; +import { API_SECRET_KEY } from '../../utils/constants'; +import { CallbackHook } from '../types'; +import { useState } from 'react'; + +export function useDeleteMediaSource(): CallbackHook< + ( + pipelineUuid: string, + inputSlot: number, + ldPipelineId: string + ) => Promise> +> { + const [loading, setLoading] = useState(false); + + const deleteMediaSource = async ( + pipelineUuid: string, + inputSlot: number, + ldPipelineId: string + ) => { + setLoading(true); + + return fetch( + `/api/manager/pipelines/${pipelineUuid}/rendering-engine/media/${inputSlot}/${ldPipelineId}`, + { + method: 'DELETE', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ) + .then(async (response) => { + if (response.ok) { + const text = await response.text(); + const data = text ? JSON.parse(text) : null; + return data ? data : null; + } + throw await response.text; + }) + .finally(() => setLoading(false)); + }; + return [deleteMediaSource, loading]; +} diff --git a/src/hooks/renderingEngine/usePipelineHtmlSources.tsx b/src/hooks/renderingEngine/usePipelineHtmlSources.tsx new file mode 100644 index 00000000..8bf33357 --- /dev/null +++ b/src/hooks/renderingEngine/usePipelineHtmlSources.tsx @@ -0,0 +1,28 @@ +import { CallbackHook } from '../types'; +import { useState } from 'react'; +import { API_SECRET_KEY } from '../../utils/constants'; + +export function usePipelineHtmlSources(): CallbackHook< + (pipelineUuid: string) => Promise +> { + const [loading, setLoading] = useState(false); + + const getPipelineHtmlSources = async ( + pipelineUuid: string + ): Promise => { + setLoading(true); + const response = await fetch( + `/api/manager/pipelines/${pipelineUuid}/rendering-engine/html/get`, + { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ); + setLoading(false); + if (response.ok) { + return await response.json(); + } + throw await response.json(); + }; + return [getPipelineHtmlSources, loading]; +} diff --git a/src/hooks/renderingEngine/usePipelineMediaSources.tsx b/src/hooks/renderingEngine/usePipelineMediaSources.tsx new file mode 100644 index 00000000..ee121cc8 --- /dev/null +++ b/src/hooks/renderingEngine/usePipelineMediaSources.tsx @@ -0,0 +1,27 @@ +import { API_SECRET_KEY } from '../../utils/constants'; +import { CallbackHook } from '../types'; +import { useState } from 'react'; + +export function usePipelineMediaSources(): CallbackHook< + (pipelineUuid: string) => Promise +> { + const [loading, setLoading] = useState(false); + const getPipelineMediaSources = async ( + pipelineUuid: string + ): Promise => { + setLoading(true); + const response = await fetch( + `/api/manager/pipelines/${pipelineUuid}/rendering-engine/media/get`, + { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ); + setLoading(false); + if (response.ok) { + return await response.json(); + } + throw await response.json(); + }; + return [getPipelineMediaSources, loading]; +} diff --git a/src/hooks/renderingEngine/useRenderingEngine.tsx b/src/hooks/renderingEngine/useRenderingEngine.tsx new file mode 100644 index 00000000..6fe2943b --- /dev/null +++ b/src/hooks/renderingEngine/useRenderingEngine.tsx @@ -0,0 +1,29 @@ +import { CallbackHook } from '../types'; +import { useState } from 'react'; +import { API_SECRET_KEY } from '../../utils/constants'; +import { ResourcesRenderingEngineResponse } from '../../../types/ateliere-live'; + +export function useRenderingEngine(): CallbackHook< + (pipelineUuid: string) => Promise +> { + const [loading, setLoading] = useState(false); + + const getRenderingEngine = async ( + pipelineUuid: string + ): Promise => { + setLoading(true); + const response = await fetch( + `/api/manager/pipelines/${pipelineUuid}/rendering-engine`, + { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + } + ); + setLoading(false); + if (response.ok) { + return await response.json(); + } + throw await response.json(); + }; + return [getRenderingEngine, loading]; +} diff --git a/src/hooks/sources/useAddSource.tsx b/src/hooks/sources/useAddSource.tsx new file mode 100644 index 00000000..2c1e843d --- /dev/null +++ b/src/hooks/sources/useAddSource.tsx @@ -0,0 +1,38 @@ +import { useState } from 'react'; +import { addSetupItem } from '../items/addSetupItem'; +import { CallbackHook } from '../types'; +import { Production } from '../../interfaces/production'; +import { usePutProduction } from '../productions'; +import { SourceReference } from '../../interfaces/Source'; + +export function useAddSource(): CallbackHook< + ( + input: SourceReference, + productionSetup: Production + ) => Promise +> { + const [loading, setLoading] = useState(true); + const putProduction = usePutProduction(); + + const addSource = async ( + input: SourceReference, + productionSetup: Production + ) => { + const updatedSetup = addSetupItem( + { + _id: input._id ? input._id : undefined, + type: input.type || 'ingest_source', + label: input.label, + input_slot: input.input_slot + }, + productionSetup + ); + + if (!updatedSetup) return; + + const res = await putProduction(updatedSetup._id.toString(), updatedSetup); + return res; + }; + + return [addSource, loading]; +} diff --git a/src/hooks/sources/useCreateSrtSource.tsx b/src/hooks/sources/useCreateSrtSource.tsx new file mode 100644 index 00000000..40507fc1 --- /dev/null +++ b/src/hooks/sources/useCreateSrtSource.tsx @@ -0,0 +1,28 @@ +import { useState } from 'react'; +import { SrtSource } from '../../interfaces/Source'; +import { CallbackHook } from '../types'; +import { API_SECRET_KEY } from '../../utils/constants'; + +export function useCreateSrtSource(): CallbackHook< + (uuid: string, srtPayload: SrtSource) => Promise +> { + const [reloadList, setReloadList] = useState(false); + + const createSrtSource = async (uuid: string, srtPayload: SrtSource) => { + setReloadList(false); + return fetch(`/api/manager/ingests/${uuid}/srt/`, { + method: 'POST', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ srtPayload }) + }).then(async (response) => { + if (response.ok) { + setTimeout(() => { + setReloadList(true); + }, 1500); + return response; + } + throw await response.text(); + }); + }; + return [createSrtSource, reloadList]; +} diff --git a/src/hooks/sources/useDeleteSource.tsx b/src/hooks/sources/useDeleteSource.tsx new file mode 100644 index 00000000..804e2395 --- /dev/null +++ b/src/hooks/sources/useDeleteSource.tsx @@ -0,0 +1,43 @@ +import { useState } from 'react'; +import { CallbackHook } from '../types'; +import { API_SECRET_KEY } from '../../utils/constants'; +import { useIngests, useIngestSources } from '../ingests'; + +export function useDeleteSrtSource(): CallbackHook< + (ingest_name: string, ingest_source_name: string) => void +> { + const [deleteSrtLoading, setDeleteSrtLoading] = useState(false); + const ingests = useIngests(); + const [getSources] = useIngestSources(); + + const deleteSrtSource = async ( + ingest_name: string, + ingest_source_name: string + ) => { + const ingestUuid = + ingests.find((ingest) => ingest.name === ingest_name)?.uuid || ''; + + const sources = await getSources(ingest_name); + const sourceId = sources.find( + (source) => source.name === ingest_source_name + )?.source_id; + + if (ingestUuid !== '' && sourceId !== undefined) { + return fetch(`/api/manager/srt/${ingestUuid}/${sourceId}/`, { + method: 'DELETE', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }) + .then(async (response) => { + if (response.ok) { + return response.json(); + } + throw response.text; + }) + .finally(() => setDeleteSrtLoading(false)); + } else { + setDeleteSrtLoading(false); + return; + } + }; + return [deleteSrtSource, deleteSrtLoading]; +} diff --git a/src/hooks/sources/useRemoveInventorySource.tsx b/src/hooks/sources/useRemoveInventorySource.tsx new file mode 100644 index 00000000..ee048101 --- /dev/null +++ b/src/hooks/sources/useRemoveInventorySource.tsx @@ -0,0 +1,26 @@ +import { API_SECRET_KEY } from '../../utils/constants'; +import { useState } from 'react'; +import { CallbackHook } from '../types'; + +export function useRemoveInventorySourceItem(): CallbackHook< + (id: string) => Promise +> { + const [reloadList, setReloadList] = useState(false); + + const removeInventorySourceItem = async (id: string) => { + setReloadList(false); + return fetch(`/api/manager/inventory/${id}/database/`, { + method: 'DELETE', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }).then(async (response) => { + if (response.ok) { + setTimeout(() => { + setReloadList(true); + }, 1000); + return response; + } + throw response.text; + }); + }; + return [removeInventorySourceItem, reloadList]; +} diff --git a/src/hooks/sources/useSetSourceToPurge.tsx b/src/hooks/sources/useSetSourceToPurge.tsx index 75818cf5..df96904d 100644 --- a/src/hooks/sources/useSetSourceToPurge.tsx +++ b/src/hooks/sources/useSetSourceToPurge.tsx @@ -2,20 +2,20 @@ import { useState } from 'react'; import { SourceWithId } from '../../interfaces/Source'; import { CallbackHook } from '../types'; import { Log } from '../../api/logger'; +import { API_SECRET_KEY } from '../../utils/constants'; export function useSetSourceToPurge(): CallbackHook< (source: SourceWithId) => void > { const [reloadList, setReloadList] = useState(false); - const removeInventorySource = (source: SourceWithId) => { + const purgeInventorySource = (source: SourceWithId) => { if (source && source.status === 'gone') { setReloadList(false); fetch(`/api/manager/inventory/${source._id}`, { method: 'PUT', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }) .then((response) => { if (!response.ok) { @@ -40,5 +40,5 @@ export function useSetSourceToPurge(): CallbackHook< setReloadList(false); } }; - return [removeInventorySource, reloadList]; + return [purgeInventorySource, reloadList]; } diff --git a/src/hooks/sources/useSources.tsx b/src/hooks/sources/useSources.tsx index 1b58418a..d71d5305 100644 --- a/src/hooks/sources/useSources.tsx +++ b/src/hooks/sources/useSources.tsx @@ -1,9 +1,12 @@ import { useEffect, useState } from 'react'; import { SourceWithId } from '../../interfaces/Source'; +import { API_SECRET_KEY } from '../../utils/constants'; +import { CallbackHook } from '../types'; export function useSources( - deleteComplete?: boolean, - updatedSource?: SourceWithId + reloadList?: boolean, + updatedSource?: SourceWithId, + refreshKey?: number ): [Map, boolean] { const [sources, setSources] = useState>( new Map() @@ -11,29 +14,61 @@ export function useSources( const [loading, setLoading] = useState(true); useEffect(() => { - if (!updatedSource || deleteComplete) { - fetch('/api/manager/sources?mocked=false', { - method: 'GET', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]] - }) - .then(async (response) => { - if (response.ok) { - const fetchedSources = (await response.json()) as SourceWithId[]; - const sourceMap = new Map(); - fetchedSources - .sort((a, b) => (a.status === 'gone' ? 1 : -1)) - .map((source) => sourceMap.set(source._id.toString(), source)); - setSources(sourceMap); - } - }) - .finally(() => { - setLoading(false); + const fetchSources = async () => { + setLoading(true); + try { + const response = await fetch('/api/manager/sources?mocked=false', { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] }); - return; + + if (response.ok) { + const fetchedSources = (await response.json()) as SourceWithId[]; + const sourceMap = new Map(); + fetchedSources + .sort((a, b) => (a.status === 'gone' ? 1 : -1)) + .map((source) => sourceMap.set(source._id.toString(), source)); + setSources(sourceMap); + } + } catch (error) { + console.error('Error fetching sources:', error); + } finally { + setLoading(false); + } + }; + + if (reloadList || !updatedSource || refreshKey) { + fetchSources(); + } else if (updatedSource) { + sources.set(updatedSource._id.toString(), updatedSource); + setSources(new Map(sources)); } - sources.set(updatedSource._id.toString(), updatedSource); - setSources(new Map(sources)); - }, [updatedSource, deleteComplete]); + }, [updatedSource, reloadList, refreshKey]); + return [sources, loading]; } + +export function useUpdateSources(): CallbackHook< + () => Promise[]> +> { + const [loading, setLoading] = useState(false); + const updateSources = async () => { + try { + setLoading(true); + const response = await fetch('/api/manager/sources?mocked=false', { + method: 'GET', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]] + }); + setLoading(false); + if (response.ok) { + return await response.json(); + } else { + throw new Error(await response.text()); + } + } catch (error) { + console.error(`Error fetching sources: ${error}`); + throw error; + } + }; + return [updateSources, loading]; +} diff --git a/src/hooks/streams.ts b/src/hooks/streams.ts index c684bdde..8bfe3bbf 100644 --- a/src/hooks/streams.ts +++ b/src/hooks/streams.ts @@ -6,8 +6,8 @@ import { } from '../interfaces/Source'; import { Production } from '../interfaces/production'; import { CallbackHook } from './types'; -import { MultiviewSettings } from '../interfaces/multiview'; import { Result } from '../interfaces/result'; +import { API_SECRET_KEY } from '../utils/constants'; export function useCreateStream(): CallbackHook< ( @@ -24,16 +24,15 @@ export function useCreateStream(): CallbackHook< input_slot: number ): Promise> => { setLoading(true); - const stream = { - source: source, - input_slot: input_slot - }; return fetch(`/api/manager/streams/`, { method: 'POST', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]], - body: JSON.stringify({ ...stream, production: production }) + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ + source: source, + production: production, + input_slot: input_slot + }) }) .then(async (response) => { if (response.ok) { @@ -76,8 +75,7 @@ export function useDeleteStream(): CallbackHook< const streamRequests = streamUuids.map((streamUuid) => { return fetch(`/api/manager/streams/${streamUuid}`, { method: 'DELETE', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]], + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], body: JSON.stringify({ pipelineUUID: pipelineUUID }) @@ -138,27 +136,11 @@ export function useDeleteStream(): CallbackHook< }; } - const multiviewsWithLabels = [...restWithLabels, ...updatedMultiviews]; - - const multiview: MultiviewSettings[] = multiviews.map( - (singleMultiview, index) => ({ - ...singleMultiview, - layout: { - ...singleMultiview.layout, - views: multiviewsWithLabels - }, - for_pipeline_idx: index, - multiviewId: index + 1 - }) - ); - const streamRequests = streamUuids.map((streamUuid) => { return fetch(`/api/manager/streams/${streamUuid}`, { method: 'DELETE', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]], + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], body: JSON.stringify({ - multiview: multiview, pipelineUUID: pipelineUUID }) }); @@ -185,3 +167,35 @@ export function useDeleteStream(): CallbackHook< }; return [deleteStream, loading]; } + +export function useUpdateStream(): CallbackHook< + (streamUuid: string, alignment_ms: number) => void +> { + const [loading, setLoading] = useState(false); + + const updateStream = async ( + streamUuid: string, + alignment_ms: number + ): Promise => { + setLoading(true); + const response = await fetch(`/api/manager/streams/${streamUuid}`, { + method: 'PATCH', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ alignment_ms: alignment_ms }) + }); + + setLoading(false); + + if (response.status === 204) { + return; + } + + if (response.ok) { + return await response.json(); + } + + throw await response.text(); + }; + + return [updateStream, loading]; +} diff --git a/src/hooks/useCheckProductionPipelines.tsx b/src/hooks/useCheckProductionPipelines.tsx new file mode 100644 index 00000000..9555fa4b --- /dev/null +++ b/src/hooks/useCheckProductionPipelines.tsx @@ -0,0 +1,46 @@ +import { useState } from 'react'; +import { CallbackHook } from './types'; +import { Production } from '../interfaces/production'; +import { ResourcesCompactPipelineResponse } from '../../types/ateliere-live'; + +export function useCheckProductionPipelines(): CallbackHook< + ( + production: Production, + pipelines: ResourcesCompactPipelineResponse[] | undefined + ) => Production +> { + const [loading, setLoading] = useState(false); + + const checkProductionPipelines = ( + production: Production, + pipelines: ResourcesCompactPipelineResponse[] | undefined + ) => { + if (!production.production_settings) return production; + const productionPipelines = production.production_settings.pipelines; + + const activePipelinesForProduction = pipelines?.filter((pipeline) => + productionPipelines.some( + (productionPipeline) => + productionPipeline.pipeline_name === pipeline.name + ) + ); + const availablePipelines = productionPipelines.map((productionPipeline) => { + const activePipeForProduction = activePipelinesForProduction?.find( + (p) => p.name === productionPipeline.pipeline_name + ); + if (activePipeForProduction?.streams.length === 0) { + return productionPipeline; + } + return productionPipeline; + }); + + return { + ...production, + production_settings: { + ...production.production_settings, + pipelines: availablePipelines + } + }; + }; + return [checkProductionPipelines, loading]; +} diff --git a/src/hooks/useConfigureMultiviewLayout.tsx b/src/hooks/useConfigureMultiviewLayout.tsx new file mode 100644 index 00000000..1338d1a3 --- /dev/null +++ b/src/hooks/useConfigureMultiviewLayout.tsx @@ -0,0 +1,56 @@ +import { useEffect, useState } from 'react'; +import { MultiviewPreset, TMultiviewLayout } from '../interfaces/preset'; +import { MultiviewViews, TListSource } from '../interfaces/multiview'; + +export function useConfigureMultiviewLayout( + productionId: string | undefined, + preset: MultiviewPreset | null, + defaultLabel: string | undefined, + source: TListSource | undefined, + viewId: string | undefined, + name: string | null +) { + const [updatedPreset, setUpdatedPreset] = useState( + null + ); + + useEffect(() => { + if (productionId && preset && (defaultLabel || source)) { + const arr: MultiviewViews[] = []; + preset.layout.views.map((item, index) => { + if (index.toString() === viewId) { + if (source) { + arr.push({ + ...item, + input_slot: source.input_slot, + label: source.label, + id: source.id + }); + } + if (defaultLabel) { + arr.push({ + ...item, + input_slot: parseInt(viewId, 10), + label: defaultLabel + }); + } + } else { + arr.push({ + ...item + }); + } + }); + return setUpdatedPreset({ + ...preset, + productionId, + name: name || preset.name, + layout: { + ...preset.layout, + views: arr + } + }); + } + }, [defaultLabel, name, source, viewId]); + + return { multiviewLayout: updatedPreset }; +} diff --git a/src/hooks/useCreateInputArray.tsx b/src/hooks/useCreateInputArray.tsx new file mode 100644 index 00000000..95d362c2 --- /dev/null +++ b/src/hooks/useCreateInputArray.tsx @@ -0,0 +1,43 @@ +import { useEffect, useState } from 'react'; +import { Production } from '../interfaces/production'; +import { TListSource } from '../interfaces/multiview'; +import { GetPipelines } from './pipelines'; + +export function useCreateInputArray(production: Production | undefined) { + const [inputList, setInputList] = useState(); + const [pipelines] = GetPipelines(); + + useEffect(() => { + if (production && pipelines) { + const list: TListSource[] = []; + production.sources.map((source) => { + list.push({ + id: source._id ? source._id : '', + input_slot: source.input_slot, + label: source.label + }); + }); + pipelines.flatMap((pipeline) => + pipeline.feedback_streams.flatMap((source, index) => { + if (source.input_slot > 1000) { + list.push({ + id: (index + 1).toString(), + input_slot: source.input_slot, + label: source.name + }); + } + }) + ); + const uniqueList = list.filter( + (item, index, self) => + index === + self.findIndex( + (t) => t.input_slot === item.input_slot && t.label === item.label + ) + ); + return setInputList(uniqueList); + } + }, [production, pipelines]); + + return { inputList }; +} diff --git a/src/hooks/useDragableItems.ts b/src/hooks/useDragableItems.ts index 427ffbf1..a31a09ea 100644 --- a/src/hooks/useDragableItems.ts +++ b/src/hooks/useDragableItems.ts @@ -2,7 +2,6 @@ import { useEffect, useState } from 'react'; import { SourceReference, SourceWithId } from '../interfaces/Source'; import { useSources } from './sources/useSources'; import { getSourceThumbnail } from '../utils/source'; - export interface ISource extends SourceWithId { label: string; input_slot: number; @@ -11,57 +10,79 @@ export interface ISource extends SourceWithId { } export function useDragableItems( sources: SourceReference[] -): [ISource[], (originId: string, destinationId: string) => void, boolean] { +): [ + (SourceReference | ISource)[], + (originId: string, destinationId: string) => void, + boolean +] { const [inventorySources, loading] = useSources(); - const [items, setItems] = useState( + const [items, setItems] = useState<(SourceReference | ISource)[]>( sources.flatMap((ref) => { - const source = inventorySources.get(ref._id); + const refId = ref._id ? ref._id : ''; + const source = inventorySources.get(refId); if (!source) return []; return { ...source, + _id: refId, label: ref.label, input_slot: ref.input_slot, stream_uuids: ref.stream_uuids, - src: getSourceThumbnail(source) + src: getSourceThumbnail(source), + ingest_source_name: source.ingest_source_name, + ingest_name: source.ingest_name, + video_stream: source.video_stream, + audio_stream: source.audio_stream, + status: source.status, + type: source.type, + tags: source.tags, + name: source.name }; }) ); - useEffect(() => { - setItems( - sources.flatMap((ref) => { - const source = inventorySources.get(ref._id); - if (!source) return []; - return { - ...source, - label: ref.label, - input_slot: ref.input_slot, - stream_uuids: ref.stream_uuids, - src: getSourceThumbnail(source) - }; - }) - ); + const updatedItems = sources.map((ref) => { + const refId = ref._id ? ref._id : ''; + const source = inventorySources.get(refId); + if (!source) return { ...ref }; + return { + ...ref, + _id: refId, + status: source.status, + name: source.name, + type: source.type, + tags: source.tags, + ingest_name: source.ingest_name, + ingest_source_name: source.ingest_source_name, + ingest_type: source.ingest_type, + label: ref.label, + input_slot: ref.input_slot, + stream_uuids: ref.stream_uuids, + src: getSourceThumbnail(source), + video_stream: source.video_stream, + audio_stream: source.audio_stream, + lastConnected: source.lastConnected + }; + }); + setItems(updatedItems); }, [sources, inventorySources]); - const moveItem = (originId: string, destinationId: string) => { - const originSource = items.find((i) => i._id.toString() === originId); + const originSource = items.find( + (item) => (item._id ? item._id.toString() : '') === originId + ); const destinationSource = items.find( - (i) => i._id.toString() === destinationId + (item) => (item._id ? item._id.toString() : '') === destinationId ); if (!originSource || !destinationSource) return; - const originInputSlot = originSource.input_slot; - const destinationInputSlot = destinationSource.input_slot; - originSource.input_slot = destinationInputSlot; - destinationSource.input_slot = originInputSlot; - const updatedItems = [ - ...items.filter( - (i) => i._id !== originSource._id && i._id !== destinationSource._id - ), - originSource, - destinationSource - ].sort((a, b) => a.input_slot - b.input_slot); + const updatedItems = items + .map((item) => { + if (item._id === originSource._id) + return { ...item, input_slot: destinationSource.input_slot }; + if (item._id === destinationSource._id) + return { ...item, input_slot: originSource.input_slot }; + return item; + }) + .sort((a, b) => a.input_slot - b.input_slot); setItems(updatedItems); }; - return [items, moveItem, loading]; } diff --git a/src/hooks/useGetFirstEmptySlot.ts b/src/hooks/useGetFirstEmptySlot.ts new file mode 100644 index 00000000..8cda1821 --- /dev/null +++ b/src/hooks/useGetFirstEmptySlot.ts @@ -0,0 +1,37 @@ +import { useState } from 'react'; +import { Production } from '../interfaces/production'; +import { CallbackHook } from './types'; + +export function useGetFirstEmptySlot(): CallbackHook< + (productionSetup?: Production | undefined) => number +> { + const [loading, setLoading] = useState(true); + + const findFirstEmptySlot = (productionSetup: Production | undefined) => { + if (!productionSetup) throw 'no_production'; + + if (productionSetup) { + let firstEmptySlotTemp = productionSetup.sources.length + 1; + if (productionSetup.sources.length === 0) { + return firstEmptySlotTemp; + } + for ( + let i = 0; + i < + productionSetup.sources[productionSetup.sources.length - 1].input_slot; + i++ + ) { + if ( + !productionSetup.sources.some((source) => source.input_slot === i + 1) + ) { + firstEmptySlotTemp = i + 1; + break; + } + } + return firstEmptySlotTemp; + } else { + return 0; + } + }; + return [findFirstEmptySlot, loading]; +} diff --git a/src/hooks/useMultiviewDefaultPresets.tsx b/src/hooks/useMultiviewDefaultPresets.tsx new file mode 100644 index 00000000..f5e2e256 --- /dev/null +++ b/src/hooks/useMultiviewDefaultPresets.tsx @@ -0,0 +1,59 @@ +import { useEffect, useState } from 'react'; +import { useMultiviewPresets } from './multiviewPreset'; +import { MultiviewPreset } from '../interfaces/preset'; +import { SourceReference } from '../interfaces/Source'; + +export function useMultiviewDefaultPresets({ + sourceList, + isChecked +}: { + sourceList: SourceReference[] | undefined; + isChecked: boolean; +}) { + const [updatedMultiviewPresets, setUpdatedMultiviewPresets] = useState< + MultiviewPreset[] + >([]); + const [databaseMultiviewPresets] = useMultiviewPresets(); + + useEffect(() => { + if (databaseMultiviewPresets) { + const sourceListLength = sourceList ? sourceList.length : 0; + + const updatedPresets = databaseMultiviewPresets.map((preset) => { + return { + ...preset, + layout: { + ...preset.layout, + views: preset.layout.views.map((view, index) => { + // Remove 2 from index to remove id for Preview- and Program-view + const sourceSlot = index - 2; + const source = + sourceSlot < sourceListLength && + sourceList && + sourceSlot >= 0 && + !isChecked + ? sourceList[sourceSlot] + : { + type: 'ingest_source', + input_slot: 0, + label: '', + _id: '' + }; + + return { + ...view, + label: sourceSlot >= 0 ? source.label : view.label, + id: sourceSlot >= 0 ? source._id : view.id, + input_slot: + sourceSlot >= 0 ? source.input_slot : view.input_slot + }; + }) + } + }; + }); + setUpdatedMultiviewPresets(updatedPresets); + } + }, [databaseMultiviewPresets, sourceList, isChecked]); + + return { multiviewDefaultPresets: updatedMultiviewPresets }; +} diff --git a/src/hooks/useSetupMultiviewLayout.tsx b/src/hooks/useSetupMultiviewLayout.tsx new file mode 100644 index 00000000..9fd9af19 --- /dev/null +++ b/src/hooks/useSetupMultiviewLayout.tsx @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react'; +import { MultiviewPreset } from '../interfaces/preset'; +import { MultiviewViews } from '../interfaces/multiview'; + +export function useSetupMultiviewLayout(preset: MultiviewPreset | null) { + const [multiviewPreset, setMultiviewPreset] = useState(); + useEffect(() => { + if (preset) { + const downScale = 30; + const arr: MultiviewViews[] = []; + preset.layout.views.map((view, index) => { + arr.push({ + ...view, + x: view.x / downScale, + y: view.y / downScale, + height: view.height / downScale, + width: view.width / downScale, + id: index.toString() + }); + }); + return setMultiviewPreset({ + ...preset, + layout: { + ...preset.layout, + output_height: preset.layout.output_height / downScale + 0.5, + output_width: preset.layout.output_width / downScale + 0.5, + views: arr + } + }); + } + }, [preset]); + return { multiviewPresetLayout: multiviewPreset }; +} diff --git a/src/hooks/useUpdateSourceInputSlotOnMultiviewLayouts.tsx b/src/hooks/useUpdateSourceInputSlotOnMultiviewLayouts.tsx new file mode 100644 index 00000000..7f5c3d1b --- /dev/null +++ b/src/hooks/useUpdateSourceInputSlotOnMultiviewLayouts.tsx @@ -0,0 +1,139 @@ +import { useState } from 'react'; +import { MultiviewViews } from '../interfaces/multiview'; +import { CallbackHook } from './types'; +import { Production } from '../interfaces/production'; +import { + useGetMultiviewLayouts, + usePutMultiviewLayout +} from './multiviewLayout'; +import { usePutProduction } from './productions'; + +export function useUpdateSourceInputSlotOnMultiviewLayouts(): CallbackHook< + (production: Production) => Promise +> { + const [loading, setLoading] = useState(false); + const multiviewLayouts = useGetMultiviewLayouts(); + const updateLayout = usePutMultiviewLayout(); + const putProduction = usePutProduction(); + + const updateSourceInputSlot = async (production: Production) => { + setLoading(true); + const layouts = await multiviewLayouts(); + if (layouts) { + const updatedLayouts = layouts.map(async (singleLayout) => { + if (production._id === singleLayout.productionId) { + const updated = singleLayout.layout.views.map( + (view: MultiviewViews, index) => { + const preview = index === 0; + const program = index === 1; + const isUpdatedInputSlot = production.sources.find((source) => + view.id + ? view.id === source._id && + view.input_slot !== source.input_slot + : false + ); + const isSameInputSlot = production.sources.find((source) => + view.id + ? view.id === source._id && + view.input_slot === source.input_slot + : false + ); + + const isUpdatedLabel = production.sources.find( + (source) => + view.id === source._id && view.label !== source.label + )?.label; + + if ((view.id && view.id.length < 5) || preview || program) { + return { + ...view, + input_slot: isUpdatedInputSlot + ? isUpdatedInputSlot.input_slot + : view.input_slot, + label: isUpdatedLabel || view.label + }; + } else if (isUpdatedInputSlot || isSameInputSlot) { + return { + ...view, + input_slot: isUpdatedInputSlot + ? isUpdatedInputSlot.input_slot + : view.input_slot, + label: isUpdatedLabel || view.label + }; + } else { + return { + ...view, + input_slot: 0, + label: '' + }; + } + } + ); + + // Update the db-multiviews with the new layout-input slot + await updateLayout({ + ...singleLayout, + layout: { + ...singleLayout.layout, + views: updated + } + }); + + return { + for_pipeline_idx: singleLayout.for_pipeline_idx || 0, + _id: singleLayout._id || '', + name: singleLayout.name, + output: singleLayout.output, + layout: { + ...singleLayout.layout, + views: updated.map((view) => ({ + ...view, + label: view.input_slot === 0 ? '' : view.label + })) + } + }; + } + }); + const pipelines = production?.production_settings.pipelines; + const multiviewsArr = await Promise.all(updatedLayouts); + + const updatedMultiviews = pipelines[0].multiviews?.map((oldItem) => { + const updatedItem = multiviewsArr.find( + (newItem) => newItem && newItem._id === oldItem._id + ); + return updatedItem + ? { + ...oldItem, + layout: updatedItem.layout + } + : oldItem; + }); + + if (pipelines && pipelines[0] && updatedMultiviews) { + const updatedFirstPipeline = { + ...pipelines[0], + multiviews: updatedMultiviews.map((multiview) => ({ + ...multiview, + _id: multiview._id?.toString() || '' + })) + }; + + // Replace the first pipeline with the updated one + const newPipelines = [updatedFirstPipeline, ...pipelines.slice(1)]; + + // Update the db-production with the new layout-input slot + const res = await putProduction(production._id, { + ...production, + production_settings: { + ...production?.production_settings, + pipelines: newPipelines + } + }); + setLoading(false); + return res; + } + } + }; + + return [updateSourceInputSlot, loading]; +} diff --git a/src/hooks/workflow.ts b/src/hooks/workflow.ts index 038dc3fb..5c8b64ea 100644 --- a/src/hooks/workflow.ts +++ b/src/hooks/workflow.ts @@ -1,11 +1,15 @@ import { useCallback, useState } from 'react'; import { + FlowStep, Production, StartProductionStep, StopProductionStep } from '../interfaces/production'; import { CallbackHook } from './types'; import { Result } from '../interfaces/result'; +import { API_SECRET_KEY } from '../utils/constants'; +import { MultiviewSettings } from '../interfaces/multiview'; +import { TeardownOptions } from '../api/manager/teardown'; export function useStopProduction(): CallbackHook< (production: Production) => Promise> @@ -17,8 +21,7 @@ export function useStopProduction(): CallbackHook< return fetch('/api/manager/stop', { method: 'POST', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]], + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], body: JSON.stringify({ production }) }) .then((response) => { @@ -42,8 +45,7 @@ export function useStartProduction(): CallbackHook< setLoading(true); return fetch('/api/manager/start', { method: 'POST', - // TODO: Implement api key - headers: [['x-api-key', `Bearer apisecretkey`]], + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], body: JSON.stringify(production) }) .then((response) => { @@ -55,3 +57,105 @@ export function useStartProduction(): CallbackHook< return [startProduction, loading]; } + +export function useAddMultiviewersOnRunningProduction(): CallbackHook< + (production: Production, additions: MultiviewSettings[]) => void +> { + const [loading, setLoading] = useState(false); + + const addMultiviewersOnRunningProduction = useCallback( + async (production: Production, additions: MultiviewSettings[]) => { + setLoading(true); + + fetch('/api/manager/multiviewersOnRunningProduction', { + method: 'POST', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ production, additions }) + }).finally(() => setLoading(false)); + }, + [] + ); + + return [addMultiviewersOnRunningProduction, loading]; +} + +export function useUpdateMultiviewersOnRunningProduction(): CallbackHook< + (production: Production, updates: MultiviewSettings[]) => void +> { + const [loading, setLoading] = useState(false); + + const updateMultiviewersOnRunningProduction = useCallback( + async (production: Production, updates: MultiviewSettings[]) => { + setLoading(true); + + updates.forEach(async (multiview) => { + try { + return await fetch( + `/api/manager/multiviewersOnRunningProduction/${multiview.multiview_id}`, + { + method: 'PUT', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ production, updates }) + } + ); + } finally { + setLoading(false); + } + }); + }, + [] + ); + + return [updateMultiviewersOnRunningProduction, loading]; +} + +export function useRemoveMultiviewersOnRunningProduction(): CallbackHook< + (production: Production, removals: MultiviewSettings[]) => void +> { + const [loading, setLoading] = useState(false); + + const removeMultiviewersOnRunningProduction = useCallback( + async (production: Production, removals: MultiviewSettings[]) => { + setLoading(true); + removals.forEach(async (multiview) => { + try { + return await fetch( + `/api/manager/multiviewersOnRunningProduction/${multiview.multiview_id}`, + { + method: 'DELETE', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify({ production, removals }) + } + ); + } finally { + setLoading(false); + } + }); + }, + [] + ); + + return [removeMultiviewersOnRunningProduction, loading]; +} + +export function useTeardown(): CallbackHook< + (option: TeardownOptions) => Promise> +> { + const [loading, setLoading] = useState(false); + + const teardown = useCallback(async (options: TeardownOptions) => { + setLoading(true); + return fetch('/api/manager/teardown', { + method: 'POST', + headers: [['x-api-key', `Bearer ${API_SECRET_KEY}`]], + body: JSON.stringify(options) + }) + .then((response) => { + return response.json() as Promise>; + }) + + .finally(() => setLoading(false)); + }, []); + + return [teardown, loading]; +} diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 1d0e7e57..54118b2e 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -35,7 +35,7 @@ export const en = { disconnect_connections: 'Disconnect connections', remove_pipeline_streams: 'Remove streams', remove_pipeline_multiviews: 'Remove multiviews', - unexpected: 'Enexpeted error' + unexpected: 'Unexpected error' }, source: { type: 'Type: {{type}}', @@ -46,7 +46,8 @@ export const en = { orig: 'Original Name: {{name}}', metadata: 'Source Metadata', location_unknown: 'Unknown', - last_connected: 'Last connection' + last_connected: 'Last connection', + input_slot: 'Input slot: {{input_slot}}' }, delete_source_status: { delete_stream: 'Delete stream', @@ -58,19 +59,65 @@ export const en = { update_multiview: 'Update multiview', unexpected: 'Unexpected error' }, + rendering_engine: { + media: { + create: { + create_media: 'Create media', + filename: 'Filename', + create: 'Create', + filename_error: 'Enter a filename', + abort: 'Cancel' + }, + delete: { + delete_media: 'Delete media', + delete: 'Delete' + } + }, + html: { + create: { + create_html: 'Create HTML', + width: 'HTML graphics width', + height: 'HTML graphics height', + url: 'URL to load', + create: 'Create', + width_error: 'Width must be between 20 and 8192', + height_error: 'Height must be between 20 and 8192', + url_error: 'Enter a URL', + abort: 'Cancel' + } + } + }, empty_slot: { input_slot: 'Input slot' }, production_configuration: 'Production Configuration', production: { - add_source: 'Add Source', + productions: 'Productions', + add_source: 'Add ingested', select_preset: 'Select Preset', clear_selection: 'Clear Selection', started: 'Production started: {{name}}', failed: 'Production start failed: {{name}}', stopped: 'Production stopped: {{name}}', stop_failed: 'Production stop failed: {{name}}', - missing_multiview: 'Missing multiview reference in selected preset' + missing_multiview: 'Missing multiview reference in selected preset', + source: 'Source', + add: 'Add', + add_other_source_type: 'Add other source type', + configure_outputs: 'Configure Outputs', + manage_multiviewers: 'Manage multiviewers' + }, + configure_alignment_latency: { + configure_alignment_latency: + 'Configure alignment and latency for the pipeline streams', + source_name: 'Source name', + error: 'The alignment value must be higher than the latency', + save: 'Save', + cancel: 'Cancel', + restart_stream_info: + 'Do you wish to restart the streams you have changed latency for right away? Otherwise you will need to stop and start your production to apply your changes.', + no: 'No', + restart_stream: 'Restart stream' }, create_new: 'Create New', default_prod_placeholder: 'My New Configuration', @@ -503,23 +550,48 @@ export const en = { } } }, - inventory: 'Inventory Management', + inventory: 'Inventory', inventory_list: { + refresh_inventory: 'Refresh inventory', search: 'Search', filter: 'Filter on {{type}}', types: 'Type', locations: 'Location', active_sources: 'Active Sources', - add: 'Add', - edit: 'Edit', sort_by: 'Sort by', no_sorting_applied: 'No sorting selected', - most_recent_connection: 'Most recent connection' + most_recent_connection: 'Most recent connection', + create_srt: 'Create SRT', + create_srt_source: 'Create SRT source', + srt_metadata: 'SRT Metadata', + local_ip: 'Local IP', + local_port: 'Local port', + remote_ip: 'Remote IP', + remote_port: 'Remote port', + latency: 'Latency (ms)', + name: 'Name', + passphrase: 'Passphrase', + ingest_uuid: 'Ingest', + select_ingest: 'Select an ingest', + no_ingest_selected: 'You need to select ingest', + no_name: 'You need to enter a name', + no_local_ip: 'You need to enter a local IP', + no_local_port: 'You need to enter a local port', + no_remote_ip: 'You need to enter a remote IP', + no_remote_port: 'You need to enter a remote port', + port_already_in_use_error: + 'There is already a SRT source with this port. Choose a different port.', + passphrase_error: 'The passphrase needs to be between 10 and 79 characters', + generic_error: 'There was an error creating the SRT source', + duplicate_name_error: + 'There is already an SRT source with this name on this ingest', + cancel: 'Cancel' }, clear: 'Clear', apply: 'Apply', save: 'Save', saved: 'Saved!', + missing: 'Missing', name: 'Name', location: 'Location', type: 'Type', @@ -584,12 +656,37 @@ export const en = { }, online: 'ONLINE', offline: 'OFFLINE', + refresh_images: 'Refresh Thumbnails', + connections: 'Connections', server_error: 'No connection with {{string}}', system_controller: 'System controller', database: 'Database', application: 'Application', multiview: 'Multiview', setting_up: 'Setting up connections to external API:s...', + teardown: { + name: 'Teardown', + warning: 'WARNING!', + tearing_down: 'Tearing down...', + results: 'Teardown results', + are_you_sure: 'Are you sure?', + description: 'You are about to:', + optional: 'Optional:', + reset_pipelines: 'Reset all pipelines', + pipeline_output_streams: 'Delete all pipeline output streams', + pipeline_multiviewers: 'Delete all pipeline multiviewer outputs', + pipeline_streams: 'Delete all pipeline streams', + pipeline_control_connections: 'Delete all pipeline control connections', + ingest_streams: 'Delete all ingest streams', + ingest_src_sources: 'Delete all ingest SRT sources', + teardown_check: 'Verify' + }, + lock: { + locked: 'Locked', + unlocked: 'Unlocked', + lock: 'Lock', + unlock: 'Unlock' + }, preset: { preset_necessary: 'Preset must be selected!', low_delay: 'Low Delay Pipeline', @@ -598,6 +695,7 @@ export const en = { port: 'Port', mode: 'Mode', srt_passphrase: 'Passphrase', + srt_stream_id: 'SRT ID', video_settings: 'Video settings', video_format: 'Format', video_bit_depth: 'Bit depth', @@ -605,10 +703,33 @@ export const en = { add_stream: 'Add stream', stream_name: 'Stream', multiview_output_settings: 'Multiview output', + select_multiview_layout: 'Layout', + configure_layouts: 'Configure layouts', + create_layout: 'Create new layout', + update_layout: 'Update layout', + no_updated_layout: 'No layout updated', + layout_name_missing: 'Layout name is missing', + no_ip_selected: 'IP-adress is missing', + no_rate_selected: 'Kilobit rate is missing', + muliview_view: 'Input', + select_option: 'Select', select_multiview_preset: 'Preset', + new_preset_name: 'My layout', no_multiview_selected: 'No multiview selected', no_multiview_found: 'No multiview found', - no_port_selected: 'Unique port needed' + no_port_selected: 'Unique port needed', + unique_stream_id: 'Unique stream ID needed', + layout_already_exist: + 'Layout {{layoutNameAlreadyExist}} will be replaced on save', + remove_multiview: 'Remove multiview', + remove_layout: 'Remove layout', + clear_layout: 'Clear layout', + add_another_multiview: 'Add another multiview', + could_not_delete_layout: 'Could not delete layout', + layout_deleted: 'Layout deleted', + confirm_update_multiviewers: + 'Are you sure you want to update multiviewers for the running production?', + confirm_update: 'Update multiviewers' }, error: { missing_sources_in_db: 'Missing sources, please restart production.', diff --git a/src/i18n/locales/sv.ts b/src/i18n/locales/sv.ts index b33112df..8dca3196 100644 --- a/src/i18n/locales/sv.ts +++ b/src/i18n/locales/sv.ts @@ -48,7 +48,8 @@ export const sv = { orig: 'Enhetsnamn: {{name}}', metadata: 'Käll-metadata', location_unknown: 'Okänd', - last_connected: 'Senast uppkoppling' + last_connected: 'Senast uppkoppling', + input_slot: 'Ingång: {{input_slot}}' }, delete_source_status: { delete_stream: 'Radera ström', @@ -60,19 +61,65 @@ export const sv = { update_multiview: 'Uppdatera multiview', unexpected: 'Oväntat fel' }, + rendering_engine: { + media: { + create: { + create_media: 'Skapa media', + filename: 'Filnamn', + create: 'Skapa', + filename_error: 'Ange ett filnamn', + abort: 'Avbryt' + }, + delete: { + delete_media: 'Ta bort media', + delete: 'Ta bort' + } + }, + html: { + create: { + create_html: 'Skapa HTML', + width: 'Bredd på grafik', + height: 'Höjd på grafik', + url: 'URL att ladda in', + create: 'Skapa', + width_error: 'Bredden måste vara mellan 20 och 8192', + height_error: 'Höjden måste vara mellan 20 och 8192', + url_error: 'Ange en URL', + abort: 'Avbryt' + } + } + }, empty_slot: { input_slot: 'Ingång' }, production_configuration: 'Produktionskonfiguration', production: { - add_source: 'Lägg till källa', + productions: 'Produktioner', + add_source: 'Lägg till ingång', select_preset: 'Välj produktionsmall', clear_selection: 'Rensa val', started: 'Produktion startad: {{name}}', failed: 'Start av produktion misslyckades: {{name}}', stopped: 'Produktion stoppad: {{name}}', stop_failed: 'Stopp av produktion misslyckades: {{name}}', - missing_multiview: 'Saknar referens till en multiview i valt preset' + missing_multiview: 'Saknar referens till en multiview i valt preset', + source: 'Källa', + add: 'Lägg till', + add_other_source_type: 'Lägg till annan källtyp', + configure_outputs: 'Konfigurera Outputs', + manage_multiviewers: 'Uppdatera multiviewers' + }, + configure_alignment_latency: { + source_name: 'Källnamn', + error: 'Alignmentvärdet måste vara högre än latencyvärdet', + configure_alignment_latency: + 'Ställ in alignment och latency för strömmarna på produktionens pipelines', + save: 'Spara', + cancel: 'Avbryt', + restart_stream_info: + 'Vill du starta om strömmarna du ändrat latency för direkt? Annars behöver du stoppa och starta om produktionen för att få med dina ändringar.', + no: 'Nej', + restart_stream: 'Starta om strömmar' }, create_new: 'Skapa ny', default_prod_placeholder: 'Min Nya Konfiguration', @@ -507,21 +554,46 @@ export const sv = { }, inventory: 'Källhantering', inventory_list: { + refresh_inventory: 'Uppdatera källor', search: 'Sök', filter: 'Filtrera på {{type}}', types: 'Typ', locations: 'Plats', active_sources: 'Aktiva källor', - add: 'Lägg till', - edit: 'Redigera', sort_by: 'Sortera på', no_sorting_applied: 'Ingen sortering vald', - most_recent_connection: 'Senast anslutning' + most_recent_connection: 'Senast anslutning', + create_srt: 'Skapa SRT', + create_srt_source: 'Skapa SRT källa', + srt_metadata: 'SRT Metadata', + local_ip: 'Lokal IP', + local_port: 'Lokal port', + remote_ip: 'Remote IP', + remote_port: 'Remote port', + latency: 'Latency (ms)', + name: 'Namn', + passphrase: 'Lösenord', + ingest_uuid: 'Ingest', + select_ingest: 'Välj ingest', + no_ingest_selected: 'Du behöver välja ingest', + no_name: 'Du behöver fylla i ett namn', + no_local_ip: 'Du behöver fylla i en lokal IP', + no_local_port: 'Du behöver fylla i en lokal port', + no_remote_ip: 'Du behöver fylla i en remote IP', + no_remote_port: 'Du behöver fylla i en remote port', + port_already_in_use_error: + 'Den här porten används redan av en annan SRT källa. Välj en annan port.', + passphrase_error: 'Lösenordet måste vara mellan 10 och 79 tecken', + generic_error: 'Ett fel uppstod vid skapandet av SRT källan', + duplicate_name_error: + 'Det finns redan en SRT med det här namnet på den här ingesten.', + cancel: 'Avbryt' }, clear: 'Rensa', apply: 'Applicera', save: 'Spara', saved: 'Sparat!', + missing: 'Saknas', name: 'Namn', location: 'Plats', type: 'Typ', @@ -540,7 +612,7 @@ export const sv = { outR: 'Ut H', maxError: 'Max värde är {{max}}', minError: 'Minsta värde är 1', - alreadyUsed: 'Värdet {{value}} är redan användt', + alreadyUsed: 'Värdet {{value}} är redan använt', emptyBetween: 'Du kan inte ha tomma kanaler mellan två nummer', title: 'Ljudmappning' }, @@ -587,12 +659,38 @@ export const sv = { }, online: 'ONLINE', offline: 'OFFLINE', + refresh_images: 'Uppdatera Tumnaglar', + connections: 'Anslutningar', server_error: '{{string}}:n inte ansluten', system_controller: 'Systemkontroller', database: 'Databas', application: 'Program', multiview: 'Multiview', setting_up: 'Sätter upp anslutning till externa API:n...', + teardown: { + name: 'Rensa', + warning: 'VARNING!', + tearing_down: 'Rensar...', + results: 'Resultat', + are_you_sure: 'Är du säker?', + description: 'Du kommer att:', + optional: 'Valfritt:', + reset_pipelines: 'Återställa alla pipelines', + pipeline_output_streams: 'Stänga ner alla pipeline output strömmar', + pipeline_multiviewers: 'Stänga ner alla pipeline multiviewers', + pipeline_streams: 'Stänga ner alla pipeline strömmar', + pipeline_control_connections: + 'Stänga ner alla pipeline control connections', + ingest_streams: 'Stänga ner alla ingest strömmar', + ingest_src_sources: 'Stänga ner alla ingest SRT källor', + teardown_check: 'Verifiering' + }, + lock: { + locked: 'Låst', + unlocked: 'Olåst', + lock: 'Lås', + unlock: 'Lås Upp' + }, preset: { preset_necessary: 'Preset must be selected!', low_delay: 'Low Delay Pipeline', @@ -601,6 +699,7 @@ export const sv = { port: 'Port', mode: 'Läge', srt_passphrase: 'Lösenord', + srt_stream_id: 'SRT ID', video_settings: 'Videoinställningar', video_format: 'Format', video_bit_depth: 'Bit depth', @@ -609,9 +708,32 @@ export const sv = { stream_name: 'Ström', multiview_output_settings: 'Multiview utgång', no_multiview_selected: 'Ingen multiview vald', + no_ip_selected: 'Ingen IP-adress vald', + no_rate_selected: 'Ingen kilobit rate vald', no_multiview_found: 'Hittade ingen multiview', + select_multiview_layout: 'Komposition', + configure_layouts: 'Justera kompositioner', + create_layout: 'Skapa komposition', + update_layout: 'Uppdatera komposition', + no_updated_layout: 'Ingen uppdaterad komposition', + layout_name_missing: 'Namn på komposition saknas', + new_preset_name: 'Min komposition', + muliview_view: 'Ingång', + select_option: 'Välj', select_multiview_preset: 'Förinställningar', - no_port_selected: 'Unik port krävs' + no_port_selected: 'Unik port krävs', + unique_stream_id: 'Unikt stream ID krävs', + layout_already_exist: + 'Konfigurationen {{layoutNameAlreadyExist}} skrivs över om du sparar', + remove_multiview: 'Ta bort multiview', + remove_layout: 'Ta bort komposition', + clear_layout: 'Rensa komposition', + add_another_multiview: 'Lägg till ny multiview', + layout_deleted: 'Kompositionen har tagits bort', + could_not_delete_layout: 'Kunde inte ta bort kompositionen', + confirm_update_multiviewers: + 'Är du säker på att du vill uppdatera multiview för pågående produktion?', + confirm_update: 'Uppdatera multiviewers' }, error: { missing_sources_in_db: 'Källor saknas, var god starta om produktionen.', diff --git a/src/i18n/useTranslate.ts b/src/i18n/useTranslate.ts index a1c54cda..6751a99e 100644 --- a/src/i18n/useTranslate.ts +++ b/src/i18n/useTranslate.ts @@ -1,9 +1,9 @@ import { getTranslate } from './translate'; const DEFAULT_LOCALE = - typeof window === 'undefined' - ? process.env.UI_LANG || 'en' - : document.documentElement.lang; + (typeof window === 'undefined' + ? process.env.UI_LANG + : document.documentElement.lang) || 'en'; export function useTranslate(locale = DEFAULT_LOCALE) { return getTranslate(locale); diff --git a/src/interfaces/Source.ts b/src/interfaces/Source.ts index e59afa4a..9c6e1af0 100644 --- a/src/interfaces/Source.ts +++ b/src/interfaces/Source.ts @@ -1,6 +1,10 @@ import { ObjectId, WithId } from 'mongodb'; +import { HTMLSource, MediaSource } from './renderingEngine'; +import { ResourcesSrt } from '../../types/ateliere-live'; export type SourceType = 'camera' | 'graphics' | 'microphone'; export type SourceStatus = 'ready' | 'new' | 'gone' | 'purge'; +export type Type = 'ingest_source' | 'html' | 'mediaplayer'; +export type SrtMode = 'caller' | 'listener'; export type VideoStream = { height?: number; width?: number; @@ -16,7 +20,7 @@ export type AudioStream = { export type Numbers = number | number[]; export interface Source { - _id?: ObjectId; + _id?: ObjectId | string; status: SourceStatus; name: string; type: SourceType; @@ -30,13 +34,17 @@ export interface Source { video_stream: VideoStream; audio_stream: AudioStream; lastConnected: Date; + srt?: ResourcesSrt; } export interface SourceReference { - _id: string; + _id?: string; + type: Type; label: string; stream_uuids?: string[]; input_slot: number; + html_data?: HTMLSource; + media_data?: MediaSource; } export type SourceWithId = WithId; @@ -53,6 +61,12 @@ export interface DeleteSourceStep { message?: string; } +export interface DeleteRenderingEngineSourceStep { + step: 'delete_html' | 'delete_media' | 'update_multiview'; + success: boolean; + message?: string; +} + export interface DeleteSourceStatus { success: boolean; steps: DeleteSourceStep[]; @@ -79,3 +93,25 @@ export type AddSourceResult = success: false; steps: AddSourceStep[]; }; + +export type AddRenderingEngineSourceResult = + | { + success: true; + streams: SourceToPipelineStream[]; + steps: AddSourceStep[]; + } + | { + success: false; + steps: AddSourceStep[]; + }; + +export interface SrtSource { + latency_ms?: number; + local_ip?: string; + local_port?: number; + mode: SrtMode; + name: string; + passphrase?: string; + remote_ip?: string; + remote_port?: number; +} diff --git a/src/interfaces/multiview.ts b/src/interfaces/multiview.ts index 6ea686a7..e1de08d7 100644 --- a/src/interfaces/multiview.ts +++ b/src/interfaces/multiview.ts @@ -5,8 +5,15 @@ export interface MultiviewViews { height: number; width: number; label: string; + id?: string; } +export type TListSource = { + id: string; + input_slot: number; + label: string; +}; + export interface MultiviewOutputSettings { format: string; frame_rate_d: number; @@ -18,11 +25,12 @@ export interface MultiviewOutputSettings { srt_mode: string; srt_latency_ms: number; srt_passphrase: string; + srt_stream_id: string; video_format: string; video_kilobit_rate: number; } -export interface MultiviewLayout { +export interface MultiviewStructureLayout { output_height: number; output_width: number; views: MultiviewViews[]; @@ -33,6 +41,7 @@ export interface MultiviewSettings { multiview_id?: number; for_pipeline_idx: number; name: string; - layout: MultiviewLayout; + layout: MultiviewStructureLayout; output: MultiviewOutputSettings; + _id?: string; } diff --git a/src/interfaces/pipeline.ts b/src/interfaces/pipeline.ts index b97f48f5..f1c6bd46 100644 --- a/src/interfaces/pipeline.ts +++ b/src/interfaces/pipeline.ts @@ -9,6 +9,15 @@ export interface SrtOutput { url: string; } +export interface PipelineSource { + ingest_source_name: string; + ingest_name: string; + settings: { + alignment_ms?: number; + max_network_latency_ms?: number; + }; +} + export interface ManagerPipelineResponse { pipeline: ResourcesPipelineResponse; status: { @@ -62,6 +71,7 @@ export interface PipelineSettings { audio_mapping: string; program_output_port: number; // deprecated but kept for backward compatibility program_output: ProgramOutput[]; + outputs?: PipelineOutput[]; multiviews?: MultiviewSettings[]; interfaces: [ { @@ -70,8 +80,22 @@ export interface PipelineSettings { protocol: string; } ]; + sources?: PipelineSource[]; } +export interface PipelineOutput { + uuid: string; + settings: PipelineOutputEncoderSettings; + streams: PipelineOutputSettings[]; +} + +// In order for there to be multiple streams on the same output +// all streams need to share encoder settings +export type PipelineOutputEncoderSettings = Pick< + PipelineOutputSettings, + 'video_format' | 'video_bit_depth' | 'video_kilobit_rate' +>; + export interface PipelineOutputSettings { audio_format: string; audio_kilobit_rate: number; @@ -87,6 +111,7 @@ export interface PipelineOutputSettings { video_format: string; video_gop_length: number; video_kilobit_rate: number; + srt_stream_id: string; } export interface PipelineStreamSettings { diff --git a/src/interfaces/preset.ts b/src/interfaces/preset.ts index 2a51a721..6e791cfc 100644 --- a/src/interfaces/preset.ts +++ b/src/interfaces/preset.ts @@ -1,6 +1,6 @@ import { WithId, ObjectId } from 'mongodb'; import { PipelineSettings } from './pipeline'; -import { MultiviewLayout, MultiviewOutputSettings } from './multiview'; +import { MultiviewStructureLayout, MultiviewOutputSettings } from './multiview'; import { ControlConnection } from './controlConnections'; export interface Preset { @@ -19,12 +19,18 @@ export interface PresetReference { } export interface MultiviewPreset { - _id?: ObjectId; + _id?: ObjectId | string; name: string; - layout: MultiviewLayout; + layout: MultiviewStructureLayout; output: MultiviewOutputSettings; } +export type TMultiviewLayout = MultiviewPreset & { + productionId?: string; + multiview_id?: number; + for_pipeline_idx?: number; +}; + export type MultiviewPresetWithId = WithId; export interface MultiviewPresetReference { diff --git a/src/interfaces/production.ts b/src/interfaces/production.ts index eb4d9655..00c5f480 100644 --- a/src/interfaces/production.ts +++ b/src/interfaces/production.ts @@ -63,3 +63,19 @@ export interface StopProductionStep { success: boolean; message?: string; } + +export type TeardownStepNames = + | 'pipeline_output_streams' + | 'pipeline_multiviewers' + | 'pipeline_streams' + | 'pipeline_control_connections' + | 'reset_pipelines' + | 'ingest_streams' + | 'ingest_src_sources' + | 'teardown_check'; + +export interface FlowStep { + step: string; + success: boolean; + message?: string; +} diff --git a/src/interfaces/renderingEngine.ts b/src/interfaces/renderingEngine.ts new file mode 100644 index 00000000..5402ae63 --- /dev/null +++ b/src/interfaces/renderingEngine.ts @@ -0,0 +1,14 @@ +export interface RenderingEngine { + html: HTMLSource[]; + media: MediaSource[]; +} + +export interface HTMLSource { + height: number; + url?: string; + width: number; +} + +export interface MediaSource { + filename?: string; +} diff --git a/src/middleware.ts b/src/middleware.ts index 7724e3b7..cac08478 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -41,4 +41,4 @@ export default withAuth(function middleware(req) { } }); -export const config = { matcher: ['/', '/((?!api|images).*)/'] }; +export const config = { matcher: ['/', '/((?!api|images|html_input).*)/'] }; diff --git a/tailwind.config.mjs b/tailwind.config.mjs index 9f8d5896..31513a77 100644 --- a/tailwind.config.mjs +++ b/tailwind.config.mjs @@ -16,6 +16,7 @@ const config = { colors: { background: colors.zinc['900'], container: colors.zinc['800'], + light: colors.zinc['600'], indicatorGreen: GREEN_COLOR, indicatorYellow: INDICATOR_YELLOW, indicatorRed: INDICATOR_RED, diff --git a/types/ateliere-live.d.ts b/types/ateliere-live.d.ts index e49739ef..9a9c0574 100644 --- a/types/ateliere-live.d.ts +++ b/types/ateliere-live.d.ts @@ -529,6 +529,33 @@ export interface ResourcesFeedbackStream { name: string; } +export interface ResourcesHTMLBrowser { + /** + * Height in pixels of the browser canvas + * @min 20 + * @max 8192 + * @example 720 + */ + height: number; + /** + * The input slot this HTML browser is allocated to + * @example 1 + */ + input_slot: number; + /** + * The active URL in the browser + * @example "https://www.example.com" + */ + url: string; + /** + * Width in pixels of the browser canvas + * @min 20 + * @max 8192 + * @example 1280 + */ + width: number; +} + export interface ResourcesHTTPError { /** * HTTP error code @@ -593,7 +620,7 @@ export interface ResourcesIngestResponse { export interface ResourcesIngestStreamResponse { /** * The average, minimum and maximum time to encode an audio frame in microseconds. - * Based on the latest 250 frames. + * Based on the last 250 frames. */ audio_encode_duration: ResourcesMinMaxAverageTimings; /** @@ -680,12 +707,12 @@ export interface ResourcesIngestStreamResponse { pipeline_uuid: string; /** * The average, minimum and maximum processing time of audio frames in this stream measured from the capture time to handover to the network interface. - * Based on the latest 250 frames. + * Based on the last 250 frames. */ processing_time_audio: ResourcesMinMaxAverageTimings; /** * The average, minimum and maximum processing time of video frames in this stream measured from the capture time to handover to the network interface. - * Based on the latest 250 frames. + * Based on the last 250 frames. */ processing_time_video: ResourcesMinMaxAverageTimings; /** @@ -716,7 +743,7 @@ export interface ResourcesIngestStreamResponse { stream_uuid: string; /** * The average, minimum and maximum time to encode a video frame in microseconds. - * Based on the latest 250 frames. + * Based on the last 250 frames. */ video_encode_duration: ResourcesMinMaxAverageTimings; /** The maximum number of video frames that can be kept in queue before it is full */ @@ -744,6 +771,19 @@ export interface ResourcesListeningInterface { port: number; } +export interface ResourcesMediaPlayer { + /** + * The filename/path of the file to play + * @example "/media/news.mp4" + */ + filename: string; + /** + * The input slot this media player is allocated to + * @example 2 + */ + input_slot: number; +} + export interface ResourcesMinMaxAverageTimings { /** Average time in micro seconds */ avg: number; @@ -904,7 +944,7 @@ export interface ResourcesMultiviewOutputResponse { * @example 1234 */ rendered_frames: number; - /** The average, minimum and maximum time to render a frame on this multi-view output in microseconds, based on the latest 250 frames. */ + /** The average, minimum and maximum time to render a frame on this multi-view output in microseconds, based on the last 250 frames. */ rendering_duration: ResourcesMinMaxAverageTimings; } @@ -1495,6 +1535,8 @@ export interface ResourcesPipelineResponse { * @example "1.2.3.4" */ public_ip: string; + /** The rendering engine specific status */ + rendering_engine: ResourcesRenderingEngineResponse; /** A list of the streams connected to this Pipeline */ streams: ResourcesPipelineStreamResponse[]; /** @@ -1520,7 +1562,7 @@ export interface ResourcesPipelineStreamResponse { alignment_ms: number; /** * The average, minimum and maximum time to decode an audio frame in microseconds. - * Based on the latest 250 frames. + * Based on the last 250 frames. */ audio_decode_duration: ResourcesMinMaxAverageTimings; /** @@ -1612,27 +1654,27 @@ export interface ResourcesPipelineStreamResponse { stream_uuid: string; /** * The average, minimum and maximum "time to arrival" for audio frames in this stream measured from the capture time in the Ingest to handover from the network - * interface in the Pipeline. Based on the latest 250 frames. + * interface in the Pipeline. Based on the last 250 frames. */ time_to_arrival_audio: ResourcesMinMaxAverageTimings; /** * The average, minimum and maximum "time to arrival" for video frames in this stream measured from the capture time in the Ingest to handover from the network - * interface in the Pipeline. Based on the latest 250 frames. + * interface in the Pipeline. Based on the last 250 frames. */ time_to_arrival_video: ResourcesMinMaxAverageTimings; /** * The average, minimum and maximum "time to ready for delivery" of audio frames in this stream measured from the capture time in the Ingest to the time when - * the frames are put in the delivery queue to the Rendering Engine in the Pipeline. Based on the latest 250 frames. + * the frames are put in the delivery queue to the Rendering Engine in the Pipeline. Based on the last 250 frames. */ time_to_ready_audio: ResourcesMinMaxAverageTimings; /** * The average, minimum and maximum "time to ready for delivery" of video frames in this stream measured from the capture time in the Ingest to the time when - * the frames are put in the delivery queue to the Rendering Engine in the Pipeline. Based on the latest 250 frames. + * the frames are put in the delivery queue to the Rendering Engine in the Pipeline. Based on the last 250 frames. */ time_to_ready_video: ResourcesMinMaxAverageTimings; /** * The average, minimum and maximum time to encode a video frame in microseconds. - * Based on the latest 250 frames. + * Based on the last 250 frames. */ video_decode_duration: ResourcesMinMaxAverageTimings; /** The number of video frames currently in queue to the video decoder */ @@ -1664,12 +1706,17 @@ export interface ResourcesReceiverNetworkEndpoint { */ sender_uuid: string; /** - * The transport time of messages from this connection in microseconds based on the latest 20 messages. Measured by comparing the send + * The transport time of messages from this connection in microseconds based on the last 20 messages. Measured by comparing the send * timestamp of the messages with the local time when received. */ transport_duration: ResourcesMinMaxAverageTimings; } +export interface ResourcesRenderingEngineResponse { + html: ResourcesHTMLBrowser[]; + media: ResourcesMediaPlayer[]; +} + export interface ResourcesRoundTripTimeMs { /** * Average round trip time in milliseconds @@ -1779,10 +1826,6 @@ export interface ResourcesSourceResponse { source_id: number; /** Statistics from SRT. Only included for SRT sources. */ srt?: ResourcesSrt; - /** Adjustment of incoming audio timestamps during the latest 250 frames. This field is meant for debugging purposes only and might be removed in future versions. */ - time_adjustment_audio: ResourcesMinMaxAverageTimings; - /** Adjustment of incoming video timestamps during the latest 250 frames. This field is meant for debugging purposes only and might be removed in future versions. */ - time_adjustment_video: ResourcesMinMaxAverageTimings; /** * The type of interface used by the source. NDI for NDI sources, BMD for SDI or HDMI sources using a DeckLink card, SRT for SRT sources * @example "NDI" @@ -1831,11 +1874,6 @@ export interface ResourcesSrt { * @example "AAC" */ audio_format: 'AAC'; - /** - * The number of continuity counter errors found in the input MPEG-TS of this SRT media source - * @example 34 - */ - cc_errors: number; /** SRT statistics */ connection?: ResourcesConnection; /** The number of successfully decoded audio frames */