diff --git a/tools/winscope-ng/package-lock.json b/tools/winscope-ng/package-lock.json index 71bad18b8..5b94d1ca0 100644 --- a/tools/winscope-ng/package-lock.json +++ b/tools/winscope-ng/package-lock.json @@ -14,13 +14,22 @@ "@angular/core": "^14.0.1", "@angular/elements": "^14.0.1", "@angular/forms": "^14.0.0", + "@angular/material": "^14.0.4", "@angular/platform-browser": "^14.0.0", "@angular/platform-browser-dynamic": "^14.0.0", "@angular/router": "^14.0.0", + "@auth0/auth0-angular": "^1.10.0", + "@ngrx/effects": "^14.0.2", + "@ngrx/store": "^14.0.2", + "@ngxs/store": "^3.7.4", + "@types/jsbn": "^1.2.30", "angular2-template-loader": "^0.6.2", + "auth0": "^2.42.0", "html-loader": "^3.1.0", "html-webpack-inline-source-plugin": "^1.0.0-beta.2", "html-webpack-plugin": "^5.5.0", + "jsbn": "^1.1.0", + "jsbn-rsa": "^1.0.4", "kotlin": "^1.7.0", "kotlin-compiler": "^1.7.0", "loader-utils": "^2.0.0", @@ -38,7 +47,11 @@ "@angular-devkit/build-angular": "^14.0.0", "@angular/cli": "~14.0.0", "@angular/compiler-cli": "^14.0.0", + "@ngxs/devtools-plugin": "^3.7.4", "@types/jasmine": "~4.0.0", + "@types/jquery": "^3.5.14", + "@types/node": "^18.0.4", + "@types/w3c-web-usb": "^1.0.6", "@typescript-eslint/eslint-plugin": "^5.30.6", "@typescript-eslint/parser": "^5.30.6", "eslint": "^8.19.0", @@ -460,6 +473,30 @@ "@angular/core": "14.0.6" } }, + "node_modules/@angular/cdk": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.0.4.tgz", + "integrity": "sha512-zPM4VZadoKzTF9TZ7Yx5gJ7GtQpt62f8ofdH/BF2atG+TaNzOEFqtzogP4WuJDFAxJXOPMePobhth4YjUk0Wbw==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^14.0.0 || ^15.0.0", + "@angular/core": "^14.0.0 || ^15.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cdk/node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true, + "peer": true + }, "node_modules/@angular/cli": { "version": "14.0.6", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.0.6.tgz", @@ -607,6 +644,23 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/material": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.0.4.tgz", + "integrity": "sha512-Ysz6oPbpLH7CvRR6oxQwpUImSbFqxL4+eiH0LPc7vkaOSrvGdZ/7cWhAfT6hVnw3bEY+eq5qBSMgyVUB44z4eg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^14.0.0 || ^15.0.0", + "@angular/cdk": "14.0.4", + "@angular/common": "^14.0.0 || ^15.0.0", + "@angular/core": "^14.0.0 || ^15.0.0", + "@angular/forms": "^14.0.0 || ^15.0.0", + "@angular/platform-browser": "^14.0.0 || ^15.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/platform-browser": { "version": "14.0.6", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.0.6.tgz", @@ -668,6 +722,34 @@ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", "dev": true }, + "node_modules/@auth0/auth0-angular": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@auth0/auth0-angular/-/auth0-angular-1.10.0.tgz", + "integrity": "sha512-ypM7PIHkWBB+DY5CqLD38VIQpjkbSfmncJxmQurqsHcpsJNIXoL3LGvv6hX+Wo9tp8kxKHHjV7RZ07JOplV90w==", + "dependencies": { + "@auth0/auth0-spa-js": "^1.22.0", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": ">=9 <=14", + "@angular/core": ">=9 <=14", + "@angular/router": ">=9 <=14" + } + }, + "node_modules/@auth0/auth0-spa-js": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-1.22.1.tgz", + "integrity": "sha512-l0FCmiN3XubpgCtB3U0ds4h+5WQNTnIF4eLT/fudHEtcyrT65QF/03LybGVdLyuvqdIF/D6OQsfjwYw0Ms605Q==", + "dependencies": { + "abortcontroller-polyfill": "^1.7.3", + "browser-tabs-lock": "^1.2.15", + "core-js": "^3.22.6", + "es-cookie": "^1.3.2", + "fast-text-encoding": "^1.0.3", + "promise-polyfill": "^8.2.3", + "unfetch": "^4.2.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -2876,6 +2958,31 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "node_modules/@ngrx/effects": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-14.0.2.tgz", + "integrity": "sha512-1VBJ9eBoeCKSBDkn+wllV1gFGLBrRwq5So0F0QUX2rPcjpE5N1pdpuLAmmwQ84f8nttVlMOx4P9kcEM8qW9jLg==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/core": "^14.0.0", + "@ngrx/store": "14.0.2", + "rxjs": "^6.5.3 || ^7.5.0" + } + }, + "node_modules/@ngrx/store": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-14.0.2.tgz", + "integrity": "sha512-ogxcK7fj4zXCxCVJXzX2+jbVCfvC7ehbvbD5oWXT7GFajUIvA8FSXQzapUPAQEqFbjxgFzhjMFBApMaOcfleIQ==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/core": "^14.0.0", + "rxjs": "^6.5.3 || ^7.5.0" + } + }, "node_modules/@ngtools/webpack": { "version": "14.0.6", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.0.6.tgz", @@ -2892,6 +2999,51 @@ "webpack": "^5.54.0" } }, + "node_modules/@ngxs/devtools-plugin": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@ngxs/devtools-plugin/-/devtools-plugin-3.7.4.tgz", + "integrity": "sha512-aUKBMak80t606983SvBf5hnwfkWU+Wf9s3zqQD2pr5UFc2D1VIo+hmqI3oITJaKNmv3ODLwpN3af83rZq4+7zA==", + "dev": true, + "dependencies": { + "tslib": "^1.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ngxs" + }, + "peerDependencies": { + "@angular/core": ">=6.1.0 <15.0.0", + "@ngxs/store": "^3.7.4 || ^3.7.4-dev", + "rxjs": ">=6.5.5" + } + }, + "node_modules/@ngxs/devtools-plugin/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@ngxs/store": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.7.4.tgz", + "integrity": "sha512-/W+sSz4jOkS0ZlxozdQj2+fPhl2rv6Yhbuj3WDqRw++jJ+ZU29blOpfd5lgjc24EH/Dbi0ACZl5IyX1CUbJlNQ==", + "dependencies": { + "tslib": "^1.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ngxs" + }, + "peerDependencies": { + "@angular/core": ">=6.1.0 <15.0.0", + "rxjs": ">=6.5.5" + } + }, + "node_modules/@ngxs/store/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3099,7 +3251,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true, "engines": { "node": ">= 6" } @@ -3108,7 +3259,6 @@ "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, "dependencies": { "@types/connect": "*", "@types/node": "*" @@ -3133,7 +3283,6 @@ "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -3187,7 +3336,6 @@ "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dev": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", @@ -3195,17 +3343,33 @@ "@types/serve-static": "*" } }, + "node_modules/@types/express-jwt": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz", + "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==", + "dependencies": { + "@types/express": "*", + "@types/express-unless": "*" + } + }, "node_modules/@types/express-serve-static-core": { "version": "4.17.29", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz", "integrity": "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==", - "dev": true, "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*" } }, + "node_modules/@types/express-unless": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.3.tgz", + "integrity": "sha512-TyPLQaF6w8UlWdv4gj8i46B+INBVzURBNRahCozCSXfsK2VTlL1wNyTlMKw817VHygBtlcl5jfnPadlydr06Yw==", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -3226,6 +3390,20 @@ "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==", "dev": true }, + "node_modules/@types/jquery": { + "version": "3.5.14", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz", + "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==", + "dev": true, + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/jsbn": { + "version": "1.2.30", + "resolved": "https://registry.npmjs.org/@types/jsbn/-/jsbn-1.2.30.tgz", + "integrity": "sha512-VZouplBofjq3YOIHLNRBDxILs/nAArdTZ2QP1ooflyhS+yPExWsFE+i2paBIBb7OI3NJShfcde/nogqk4SPB/Q==" + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -3239,8 +3417,7 @@ "node_modules/@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "node_modules/@types/node": { "version": "18.0.4", @@ -3262,14 +3439,12 @@ "node_modules/@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" }, "node_modules/@types/range-parser": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "node_modules/@types/retry": { "version": "0.12.0", @@ -3296,12 +3471,17 @@ "version": "1.13.10", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, "dependencies": { "@types/mime": "^1", "@types/node": "*" } }, + "node_modules/@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true + }, "node_modules/@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -3311,6 +3491,12 @@ "@types/node": "*" } }, + "node_modules/@types/w3c-web-usb": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz", + "integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==", + "dev": true + }, "node_modules/@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -3696,6 +3882,11 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/abortcontroller-polyfill": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", + "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==" + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3763,7 +3954,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "dependencies": { "debug": "4" }, @@ -4046,8 +4236,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/atob": { "version": "2.1.2", @@ -4061,6 +4250,44 @@ "node": ">= 4.5.0" } }, + "node_modules/auth0": { + "version": "2.42.0", + "resolved": "https://registry.npmjs.org/auth0/-/auth0-2.42.0.tgz", + "integrity": "sha512-IiA224CoqjPF9Pnx0R80wQZBGlXMia69s7lQHg328Oe2mngdp6Qi32DaIKkkVtJb8FuMleWMgI+zmbwW0znhYw==", + "dependencies": { + "axios": "^0.26.1", + "form-data": "^3.0.1", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^1.12.1", + "lru-memoizer": "^2.1.4", + "rest-facade": "^1.16.3", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/auth0/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/auth0/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/autoprefixer": { "version": "10.4.7", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", @@ -4109,6 +4336,14 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, "node_modules/babel-loader": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", @@ -4370,6 +4605,15 @@ "node": ">=8" } }, + "node_modules/browser-tabs-lock": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.2.15.tgz", + "integrity": "sha512-J8K9vdivK0Di+b8SBdE7EZxDr88TnATing7XoLw6+nFkXMQ6sVBh92K3NQvZlZU91AIkFRi0w3sztk5Z+vsswA==", + "hasInstallScript": true, + "dependencies": { + "lodash": ">=4.17.21" + } + }, "node_modules/browserslist": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz", @@ -4464,6 +4708,11 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -4520,7 +4769,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -4591,6 +4839,68 @@ "node": ">=4" } }, + "node_modules/change-case": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-2.3.1.tgz", + "integrity": "sha512-3HE5jrTqqn9jeKzD0+yWi7FU4OMicLbwB57ph4bpwEn5jGi3hZug5WjZjnBD2RY7YyTKAAck86ACfShXUWJKLg==", + "dependencies": { + "camel-case": "^1.1.1", + "constant-case": "^1.1.0", + "dot-case": "^1.1.0", + "is-lower-case": "^1.1.0", + "is-upper-case": "^1.1.0", + "lower-case": "^1.1.1", + "lower-case-first": "^1.0.0", + "param-case": "^1.1.0", + "pascal-case": "^1.1.0", + "path-case": "^1.1.0", + "sentence-case": "^1.1.1", + "snake-case": "^1.1.0", + "swap-case": "^1.1.0", + "title-case": "^1.1.0", + "upper-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + }, + "node_modules/change-case/node_modules/camel-case": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", + "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==", + "dependencies": { + "sentence-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, + "node_modules/change-case/node_modules/dot-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-1.1.2.tgz", + "integrity": "sha512-NzEIt12UjECXi6JZ/R/nBey6EE1qCN0yUTEFaPIaKW0AcOEwlKqujtcJVbtSfLNnj3CDoXLQyli79vAaqohyvw==", + "dependencies": { + "sentence-case": "^1.1.2" + } + }, + "node_modules/change-case/node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, + "node_modules/change-case/node_modules/param-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-1.1.2.tgz", + "integrity": "sha512-gksk6zeZQxwBm1AHsKh+XDFsTGf1LvdZSkkpSIkfDtzW+EQj/P2PBgNb3Cs0Y9Xxqmbciv2JZe3fWU6Xbher+Q==", + "dependencies": { + "sentence-case": "^1.1.2" + } + }, + "node_modules/change-case/node_modules/pascal-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", + "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==", + "dependencies": { + "camel-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + }, "node_modules/chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -4768,7 +5078,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -4793,8 +5102,7 @@ "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "node_modules/compressible": { "version": "2.0.18", @@ -4901,6 +5209,15 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, + "node_modules/constant-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-1.1.2.tgz", + "integrity": "sha512-FQ/HuOuSnX6nIF8OnofRWj+KnOpGAHXQpOKHmsL1sAnuLwu6r5mHGK+mJc0SkHkbmNfcU/SauqXLTEOL1JQfJA==", + "dependencies": { + "snake-case": "^1.1.0", + "upper-case": "^1.1.1" + } + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -4966,6 +5283,11 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, + "node_modules/cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, "node_modules/copy-anything": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", @@ -5077,6 +5399,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/core-js": { + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.23.5.tgz", + "integrity": "sha512-7Vh11tujtAZy82da4duVreQysIoO2EvVrur7y6IzZkH1IHPSekuDi8Vuw1+YKjkbfWLRD7Nc9ICQ/sIUDutcyg==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-compat": { "version": "3.23.4", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.4.tgz", @@ -5408,7 +5740,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -5445,6 +5776,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/deepmerge": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", + "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -5596,7 +5935,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -5779,6 +6117,20 @@ "safer-buffer": "^2.1.0" } }, + "node_modules/ecc-jsbn/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5940,6 +6292,11 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-cookie": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz", + "integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==" + }, "node_modules/es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", @@ -6980,6 +7337,16 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fast-text-encoding": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.4.tgz", + "integrity": "sha512-x6lDDm/tBAzX9kmsPcZsNbvDs3Zey3+scsxaZElS8xWLgUMAg/oFLeewfUz0mu1CblHhhsu15jGkraldkFh8KQ==" + }, "node_modules/fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", @@ -7141,7 +7508,6 @@ "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "dev": true, "funding": [ { "type": "individual", @@ -7180,6 +7546,15 @@ "node": ">= 0.12" } }, + "node_modules/formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==", + "deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau", + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -7315,7 +7690,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", - "dev": true, "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -7534,7 +7908,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -7785,7 +8158,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, "dependencies": { "@tootallnate/once": "1", "agent-base": "6", @@ -7838,7 +8210,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -8035,8 +8406,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { "version": "3.0.0", @@ -8255,6 +8625,19 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, + "node_modules/is-lower-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==", + "dependencies": { + "lower-case": "^1.1.0" + } + }, + "node_modules/is-lower-case/node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -8349,6 +8732,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-upper-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==", + "dependencies": { + "upper-case": "^1.1.0" + } + }, "node_modules/is-what": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", @@ -8569,10 +8960,14 @@ } }, "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, + "node_modules/jsbn-rsa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/jsbn-rsa/-/jsbn-rsa-1.0.4.tgz", + "integrity": "sha512-unHyEPFGjr6WCzrcMiwdNhYMlq4gXt6Hg5JuKOyE7OXJ7GbVMpottnqsUkPeZCAYqByAkn4N8gJwCpnacduOew==" }, "node_modules/jsesc": { "version": "2.5.2", @@ -8653,6 +9048,35 @@ "node >= 0.2.0" ] }, + "node_modules/jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -8704,6 +9128,50 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jwks-rsa": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.12.3.tgz", + "integrity": "sha512-cFipFDeYYaO9FhhYJcZWX/IyZgc0+g316rcHnDpT2dNRNIE/lMOmWKKqp09TkJoYlNFzrEVODsR4GgXJMgWhnA==", + "dependencies": { + "@types/express-jwt": "0.0.42", + "axios": "^0.21.1", + "debug": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jsonwebtoken": "^8.5.1", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.2", + "ms": "^2.1.2", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/jwks-rsa/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/karma": { "version": "6.3.20", "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz", @@ -9138,6 +9606,11 @@ "immediate": "~3.0.5" } }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -9181,18 +9654,63 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "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==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -9313,6 +9831,19 @@ "tslib": "^2.0.3" } }, + "node_modules/lower-case-first": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "integrity": "sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==", + "dependencies": { + "lower-case": "^1.1.2" + } + }, + "node_modules/lower-case-first/node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, "node_modules/lru-cache": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.0.tgz", @@ -9322,6 +9853,29 @@ "node": ">=12" } }, + "node_modules/lru-memoizer": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz", + "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==", + "dependencies": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "dependencies": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "node_modules/lru-memoizer/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + }, "node_modules/magic-string": { "version": "0.26.1", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz", @@ -9548,7 +10102,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -9569,7 +10122,6 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, "bin": { "mime": "cli.js" }, @@ -9782,8 +10334,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -10296,7 +10847,6 @@ "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -10725,6 +11275,14 @@ "tslib": "^2.0.3" } }, + "node_modules/path-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-1.1.2.tgz", + "integrity": "sha512-2snAGA6xVRqTuTPa40bn0iEpYtVK6gEqeyS/63dqpm5pGlesOv6EmRcnB9Rr6eAnAC2Wqlbz0tqgJZryttxhxg==", + "dependencies": { + "sentence-case": "^1.1.2" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -11563,6 +12121,11 @@ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true }, + "node_modules/promise-polyfill": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz", + "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg==" + }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", @@ -11929,6 +12492,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -11936,6 +12504,11 @@ "dev": true, "optional": true }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -11973,7 +12546,6 @@ "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, "dependencies": { "side-channel": "^1.0.4" }, @@ -12077,7 +12649,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -12372,6 +12943,25 @@ "node": ">=0.10.0" } }, + "node_modules/rest-facade": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/rest-facade/-/rest-facade-1.16.3.tgz", + "integrity": "sha512-9BQTPLiwg23XZwcWi0ys1wTizfc//0b2G3U6vBWcgqh56ozs2K6CD+Jw4DYcw3AqdPQN7jj8nzRUcUXFVGzb0Q==", + "dependencies": { + "change-case": "^2.3.0", + "deepmerge": "^3.2.0", + "lodash.get": "^4.4.2", + "superagent": "^5.1.1" + }, + "peerDependencies": { + "superagent-proxy": "^3.0.0" + }, + "peerDependenciesMeta": { + "superagent-proxy": { + "optional": true + } + } + }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -12864,6 +13454,19 @@ "node": ">= 0.8" } }, + "node_modules/sentence-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", + "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==", + "dependencies": { + "lower-case": "^1.1.1" + } + }, + "node_modules/sentence-case/node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, "node_modules/serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -13008,7 +13611,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -13043,6 +13645,14 @@ "npm": ">= 3.0.0" } }, + "node_modules/snake-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-1.1.2.tgz", + "integrity": "sha512-oapUKC+qulnUIN+/O7Tbl2msi9PQvJeivGN9RNbygxzI2EOY0gA96i8BJLYnGUWSLGcYtyW4YYqnGTZEySU/gg==", + "dependencies": { + "sentence-case": "^1.1.2" + } + }, "node_modules/socket.io": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", @@ -13303,6 +13913,12 @@ "node": ">=0.10.0" } }, + "node_modules/sshpk/node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, "node_modules/ssri": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", @@ -13342,7 +13958,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -13351,7 +13966,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -13512,6 +14126,41 @@ "node": "*" } }, + "node_modules/superagent": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz", + "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==", + "deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at .", + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 7.0.0" + } + }, + "node_modules/superagent/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -13535,6 +14184,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swap-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==", + "dependencies": { + "lower-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, + "node_modules/swap-case/node_modules/lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -13754,6 +14417,15 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "node_modules/title-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-1.1.2.tgz", + "integrity": "sha512-xYbo5Um5MBgn24xJSK+x5hZ8ehuGXTVhgx32KJCThHRHwpyIb1lmABi1DH5VvN9E7rNEquPjz//rF/tZQd7mjQ==", + "dependencies": { + "sentence-case": "^1.1.1", + "upper-case": "^1.0.3" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -14025,6 +14697,11 @@ "node": "*" } }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -14126,6 +14803,19 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==" + }, + "node_modules/upper-case-first": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", + "dependencies": { + "upper-case": "^1.1.1" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -14137,8 +14827,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utila": { "version": "0.4.0", @@ -15269,6 +15958,25 @@ "tslib": "^2.3.0" } }, + "@angular/cdk": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.0.4.tgz", + "integrity": "sha512-zPM4VZadoKzTF9TZ7Yx5gJ7GtQpt62f8ofdH/BF2atG+TaNzOEFqtzogP4WuJDFAxJXOPMePobhth4YjUk0Wbw==", + "peer": true, + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true, + "peer": true + } + } + }, "@angular/cli": { "version": "14.0.6", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.0.6.tgz", @@ -15355,6 +16063,14 @@ "tslib": "^2.3.0" } }, + "@angular/material": { + "version": "14.0.4", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.0.4.tgz", + "integrity": "sha512-Ysz6oPbpLH7CvRR6oxQwpUImSbFqxL4+eiH0LPc7vkaOSrvGdZ/7cWhAfT6hVnw3bEY+eq5qBSMgyVUB44z4eg==", + "requires": { + "tslib": "^2.3.0" + } + }, "@angular/platform-browser": { "version": "14.0.6", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.0.6.tgz", @@ -15385,6 +16101,29 @@ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", "dev": true }, + "@auth0/auth0-angular": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@auth0/auth0-angular/-/auth0-angular-1.10.0.tgz", + "integrity": "sha512-ypM7PIHkWBB+DY5CqLD38VIQpjkbSfmncJxmQurqsHcpsJNIXoL3LGvv6hX+Wo9tp8kxKHHjV7RZ07JOplV90w==", + "requires": { + "@auth0/auth0-spa-js": "^1.22.0", + "tslib": "^2.0.0" + } + }, + "@auth0/auth0-spa-js": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-1.22.1.tgz", + "integrity": "sha512-l0FCmiN3XubpgCtB3U0ds4h+5WQNTnIF4eLT/fudHEtcyrT65QF/03LybGVdLyuvqdIF/D6OQsfjwYw0Ms605Q==", + "requires": { + "abortcontroller-polyfill": "^1.7.3", + "browser-tabs-lock": "^1.2.15", + "core-js": "^3.22.6", + "es-cookie": "^1.3.2", + "fast-text-encoding": "^1.0.3", + "promise-polyfill": "^8.2.3", + "unfetch": "^4.2.0" + } + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -16941,6 +17680,22 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", "dev": true }, + "@ngrx/effects": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-14.0.2.tgz", + "integrity": "sha512-1VBJ9eBoeCKSBDkn+wllV1gFGLBrRwq5So0F0QUX2rPcjpE5N1pdpuLAmmwQ84f8nttVlMOx4P9kcEM8qW9jLg==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@ngrx/store": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-14.0.2.tgz", + "integrity": "sha512-ogxcK7fj4zXCxCVJXzX2+jbVCfvC7ehbvbD5oWXT7GFajUIvA8FSXQzapUPAQEqFbjxgFzhjMFBApMaOcfleIQ==", + "requires": { + "tslib": "^2.0.0" + } + }, "@ngtools/webpack": { "version": "14.0.6", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.0.6.tgz", @@ -16948,6 +17703,38 @@ "dev": true, "requires": {} }, + "@ngxs/devtools-plugin": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@ngxs/devtools-plugin/-/devtools-plugin-3.7.4.tgz", + "integrity": "sha512-aUKBMak80t606983SvBf5hnwfkWU+Wf9s3zqQD2pr5UFc2D1VIo+hmqI3oITJaKNmv3ODLwpN3af83rZq4+7zA==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@ngxs/store": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.7.4.tgz", + "integrity": "sha512-/W+sSz4jOkS0ZlxozdQj2+fPhl2rv6Yhbuj3WDqRw++jJ+ZU29blOpfd5lgjc24EH/Dbi0ACZl5IyX1CUbJlNQ==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -17116,14 +17903,12 @@ "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==" }, "@types/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", - "dev": true, "requires": { "@types/connect": "*", "@types/node": "*" @@ -17148,7 +17933,6 @@ "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", - "dev": true, "requires": { "@types/node": "*" } @@ -17202,7 +17986,6 @@ "version": "4.17.13", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", - "dev": true, "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.18", @@ -17210,17 +17993,33 @@ "@types/serve-static": "*" } }, + "@types/express-jwt": { + "version": "0.0.42", + "resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz", + "integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==", + "requires": { + "@types/express": "*", + "@types/express-unless": "*" + } + }, "@types/express-serve-static-core": { "version": "4.17.29", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz", "integrity": "sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q==", - "dev": true, "requires": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*" } }, + "@types/express-unless": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.3.tgz", + "integrity": "sha512-TyPLQaF6w8UlWdv4gj8i46B+INBVzURBNRahCozCSXfsK2VTlL1wNyTlMKw817VHygBtlcl5jfnPadlydr06Yw==", + "requires": { + "@types/express": "*" + } + }, "@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -17241,6 +18040,20 @@ "integrity": "sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg==", "dev": true }, + "@types/jquery": { + "version": "3.5.14", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz", + "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==", + "dev": true, + "requires": { + "@types/sizzle": "*" + } + }, + "@types/jsbn": { + "version": "1.2.30", + "resolved": "https://registry.npmjs.org/@types/jsbn/-/jsbn-1.2.30.tgz", + "integrity": "sha512-VZouplBofjq3YOIHLNRBDxILs/nAArdTZ2QP1ooflyhS+yPExWsFE+i2paBIBb7OI3NJShfcde/nogqk4SPB/Q==" + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -17254,8 +18067,7 @@ "@types/mime": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", - "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==", - "dev": true + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" }, "@types/node": { "version": "18.0.4", @@ -17277,14 +18089,12 @@ "@types/qs": { "version": "6.9.7", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", - "dev": true + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" }, "@types/range-parser": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", - "dev": true + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, "@types/retry": { "version": "0.12.0", @@ -17311,12 +18121,17 @@ "version": "1.13.10", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", - "dev": true, "requires": { "@types/mime": "^1", "@types/node": "*" } }, + "@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true + }, "@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -17326,6 +18141,12 @@ "@types/node": "*" } }, + "@types/w3c-web-usb": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/w3c-web-usb/-/w3c-web-usb-1.0.6.tgz", + "integrity": "sha512-cSjhgrr8g4KbPnnijAr/KJDNKa/bBa+ixYkywFRvrhvi9n1WEl7yYbtRyzE6jqNQiSxxJxoAW3STaOQwJHndaw==", + "dev": true + }, "@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", @@ -17609,6 +18430,11 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "abortcontroller-polyfill": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz", + "integrity": "sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==" + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -17657,7 +18483,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "requires": { "debug": "4" } @@ -17869,8 +18694,7 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "atob": { "version": "2.1.2", @@ -17878,6 +18702,37 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "auth0": { + "version": "2.42.0", + "resolved": "https://registry.npmjs.org/auth0/-/auth0-2.42.0.tgz", + "integrity": "sha512-IiA224CoqjPF9Pnx0R80wQZBGlXMia69s7lQHg328Oe2mngdp6Qi32DaIKkkVtJb8FuMleWMgI+zmbwW0znhYw==", + "requires": { + "axios": "^0.26.1", + "form-data": "^3.0.1", + "jsonwebtoken": "^8.5.1", + "jwks-rsa": "^1.12.1", + "lru-memoizer": "^2.1.4", + "rest-facade": "^1.16.3", + "retry": "^0.13.1" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + } + } + }, "autoprefixer": { "version": "10.4.7", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.7.tgz", @@ -17904,6 +18759,14 @@ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", "dev": true }, + "axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "requires": { + "follow-redirects": "^1.14.8" + } + }, "babel-loader": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", @@ -18111,6 +18974,14 @@ "fill-range": "^7.0.1" } }, + "browser-tabs-lock": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/browser-tabs-lock/-/browser-tabs-lock-1.2.15.tgz", + "integrity": "sha512-J8K9vdivK0Di+b8SBdE7EZxDr88TnATing7XoLw6+nFkXMQ6sVBh92K3NQvZlZU91AIkFRi0w3sztk5Z+vsswA==", + "requires": { + "lodash": ">=4.17.21" + } + }, "browserslist": { "version": "4.21.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz", @@ -18171,6 +19042,11 @@ "ieee754": "^1.1.13" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -18221,7 +19097,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" @@ -18270,6 +19145,70 @@ "supports-color": "^5.3.0" } }, + "change-case": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-2.3.1.tgz", + "integrity": "sha512-3HE5jrTqqn9jeKzD0+yWi7FU4OMicLbwB57ph4bpwEn5jGi3hZug5WjZjnBD2RY7YyTKAAck86ACfShXUWJKLg==", + "requires": { + "camel-case": "^1.1.1", + "constant-case": "^1.1.0", + "dot-case": "^1.1.0", + "is-lower-case": "^1.1.0", + "is-upper-case": "^1.1.0", + "lower-case": "^1.1.1", + "lower-case-first": "^1.0.0", + "param-case": "^1.1.0", + "pascal-case": "^1.1.0", + "path-case": "^1.1.0", + "sentence-case": "^1.1.1", + "snake-case": "^1.1.0", + "swap-case": "^1.1.0", + "title-case": "^1.1.0", + "upper-case": "^1.1.1", + "upper-case-first": "^1.1.0" + }, + "dependencies": { + "camel-case": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-1.2.2.tgz", + "integrity": "sha512-rUug78lL8mqStaLehmH2F0LxMJ2TM9fnPFxb+gFkgyUjUM/1o2wKTQtalypHnkb2cFwH/DENBw7YEAOYLgSMxQ==", + "requires": { + "sentence-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, + "dot-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-1.1.2.tgz", + "integrity": "sha512-NzEIt12UjECXi6JZ/R/nBey6EE1qCN0yUTEFaPIaKW0AcOEwlKqujtcJVbtSfLNnj3CDoXLQyli79vAaqohyvw==", + "requires": { + "sentence-case": "^1.1.2" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + }, + "param-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-1.1.2.tgz", + "integrity": "sha512-gksk6zeZQxwBm1AHsKh+XDFsTGf1LvdZSkkpSIkfDtzW+EQj/P2PBgNb3Cs0Y9Xxqmbciv2JZe3fWU6Xbher+Q==", + "requires": { + "sentence-case": "^1.1.2" + } + }, + "pascal-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-1.1.2.tgz", + "integrity": "sha512-QWlbdQHdKWlcyTEuv/M0noJtlCa7qTmg5QFAqhx5X9xjAfCU1kXucL+rcOmd2HliESuRLIOz8521RAW/yhuQog==", + "requires": { + "camel-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + } + } + }, "chardet": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", @@ -18402,7 +19341,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -18421,8 +19359,7 @@ "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "compressible": { "version": "2.0.18", @@ -18518,6 +19455,15 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, + "constant-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-1.1.2.tgz", + "integrity": "sha512-FQ/HuOuSnX6nIF8OnofRWj+KnOpGAHXQpOKHmsL1sAnuLwu6r5mHGK+mJc0SkHkbmNfcU/SauqXLTEOL1JQfJA==", + "requires": { + "snake-case": "^1.1.0", + "upper-case": "^1.1.1" + } + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -18562,6 +19508,11 @@ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "dev": true }, + "cookiejar": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.3.tgz", + "integrity": "sha512-JxbCBUdrfr6AQjOXrxoTvAMJO4HBTUIlBzslcJPAz+/KT8yk53fXun51u+RenNYvad/+Vc2DIz5o9UxlCDymFQ==" + }, "copy-anything": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", @@ -18634,6 +19585,11 @@ } } }, + "core-js": { + "version": "3.23.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.23.5.tgz", + "integrity": "sha512-7Vh11tujtAZy82da4duVreQysIoO2EvVrur7y6IzZkH1IHPSekuDi8Vuw1+YKjkbfWLRD7Nc9ICQ/sIUDutcyg==" + }, "core-js-compat": { "version": "3.23.4", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.23.4.tgz", @@ -18870,7 +19826,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -18893,6 +19848,11 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "deepmerge": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.3.0.tgz", + "integrity": "sha512-GRQOafGHwMHpjPx9iCvTgpu9NojZ49q794EEL94JVEw6VaeA8XTUyBKvAkOOjBX9oJNiV6G3P+T+tihFjo2TqA==" + }, "default-gateway": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", @@ -19012,8 +19972,7 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, "delegates": { "version": "1.0.0", @@ -19154,6 +20113,22 @@ "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" + }, + "dependencies": { + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + } + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" } }, "ee-first": { @@ -19286,6 +20261,11 @@ "is-arrayish": "^0.2.1" } }, + "es-cookie": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-cookie/-/es-cookie-1.3.2.tgz", + "integrity": "sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==" + }, "es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", @@ -19974,6 +20954,16 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "fast-text-encoding": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.4.tgz", + "integrity": "sha512-x6lDDm/tBAzX9kmsPcZsNbvDs3Zey3+scsxaZElS8xWLgUMAg/oFLeewfUz0mu1CblHhhsu15jGkraldkFh8KQ==" + }, "fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", @@ -20103,8 +21093,7 @@ "follow-redirects": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", - "dev": true + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" }, "forever-agent": { "version": "0.6.1", @@ -20123,6 +21112,11 @@ "mime-types": "^2.1.12" } }, + "formidable": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", + "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -20223,7 +21217,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", - "dev": true, "requires": { "function-bind": "^1.1.1", "has": "^1.0.3", @@ -20389,8 +21382,7 @@ "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" }, "has-unicode": { "version": "2.0.1", @@ -20590,7 +21582,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, "requires": { "@tootallnate/once": "1", "agent-base": "6", @@ -20625,7 +21616,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, "requires": { "agent-base": "6", "debug": "4" @@ -20760,8 +21750,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "3.0.0", @@ -20922,6 +21911,21 @@ "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, + "is-lower-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha512-+5A1e/WJpLLXZEDlgz4G//WYSHyQBD32qa4Jd3Lw06qQlv3fJHnp3YIHjTQSGzHMgzmVKz2ZP3rBxTHkPw/lxA==", + "requires": { + "lower-case": "^1.1.0" + }, + "dependencies": { + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + } + } + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -20983,6 +21987,14 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "is-upper-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha512-GQYSJMgfeAmVwh9ixyk888l7OIhNAGKtY6QA+IrWlu9MDTCaXmeozOZ2S9Knj7bQwBO/H6J2kb+pbyTUiMNbsw==", + "requires": { + "upper-case": "^1.1.0" + } + }, "is-what": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", @@ -21158,10 +22170,14 @@ } }, "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" + }, + "jsbn-rsa": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/jsbn-rsa/-/jsbn-rsa-1.0.4.tgz", + "integrity": "sha512-unHyEPFGjr6WCzrcMiwdNhYMlq4gXt6Hg5JuKOyE7OXJ7GbVMpottnqsUkPeZCAYqByAkn4N8gJwCpnacduOew==" }, "jsesc": { "version": "2.5.2", @@ -21225,6 +22241,30 @@ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, "jsprim": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", @@ -21275,6 +22315,52 @@ } } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jwks-rsa": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.12.3.tgz", + "integrity": "sha512-cFipFDeYYaO9FhhYJcZWX/IyZgc0+g316rcHnDpT2dNRNIE/lMOmWKKqp09TkJoYlNFzrEVODsR4GgXJMgWhnA==", + "requires": { + "@types/express-jwt": "0.0.42", + "axios": "^0.21.1", + "debug": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jsonwebtoken": "^8.5.1", + "limiter": "^1.1.5", + "lru-memoizer": "^2.1.2", + "ms": "^2.1.2", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "requires": { + "follow-redirects": "^1.14.0" + } + } + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "karma": { "version": "6.3.20", "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz", @@ -21606,6 +22692,11 @@ "immediate": "~3.0.5" } }, + "limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==" + }, "lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -21640,18 +22731,63 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "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==" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -21744,12 +22880,52 @@ "tslib": "^2.0.3" } }, + "lower-case-first": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "integrity": "sha512-UuxaYakO7XeONbKrZf5FEgkantPf5DUqDayzP5VXZrtRPdH86s4kN47I8B3TW10S4QKiE3ziHNf3kRN//okHjA==", + "requires": { + "lower-case": "^1.1.2" + }, + "dependencies": { + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + } + } + }, "lru-cache": { "version": "7.13.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.0.tgz", "integrity": "sha512-SNFKDOORR41fkWP3DXiIUvXvfzDRPg3bxD1+29iRyP2ZW+Njp2o6zhx9YkEpq1tbP0AEDNW2VBUedzDIxmNhdg==", "dev": true }, + "lru-memoizer": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.1.4.tgz", + "integrity": "sha512-IXAq50s4qwrOBrXJklY+KhgZF+5y98PDaNo0gi/v2KQBFLyWr+JyFvijZXkGKjQj/h9c0OwoE+JZbwUXce76hQ==", + "requires": { + "lodash.clonedeep": "^4.5.0", + "lru-cache": "~4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", + "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "requires": { + "pseudomap": "^1.0.1", + "yallist": "^2.0.0" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" + } + } + }, "magic-string": { "version": "0.26.1", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.1.tgz", @@ -21934,8 +23110,7 @@ "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" }, "micromatch": { "version": "4.0.5", @@ -21949,8 +23124,7 @@ "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" }, "mime-db": { "version": "1.52.0", @@ -22101,8 +23275,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multicast-dns": { "version": "7.2.5", @@ -22507,8 +23680,7 @@ "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", - "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", - "dev": true + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" }, "object-keys": { "version": "1.1.1", @@ -22833,6 +24005,14 @@ "tslib": "^2.0.3" } }, + "path-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-1.1.2.tgz", + "integrity": "sha512-2snAGA6xVRqTuTPa40bn0iEpYtVK6gEqeyS/63dqpm5pGlesOv6EmRcnB9Rr6eAnAC2Wqlbz0tqgJZryttxhxg==", + "requires": { + "sentence-case": "^1.1.2" + } + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -23344,6 +24524,11 @@ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true }, + "promise-polyfill": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-8.2.3.tgz", + "integrity": "sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg==" + }, "promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", @@ -23637,6 +24822,11 @@ } } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -23644,6 +24834,11 @@ "dev": true, "optional": true }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" + }, "psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -23671,7 +24866,6 @@ "version": "6.10.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", - "dev": true, "requires": { "side-channel": "^1.0.4" } @@ -23743,7 +24937,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -23979,6 +25172,17 @@ } } }, + "rest-facade": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/rest-facade/-/rest-facade-1.16.3.tgz", + "integrity": "sha512-9BQTPLiwg23XZwcWi0ys1wTizfc//0b2G3U6vBWcgqh56ozs2K6CD+Jw4DYcw3AqdPQN7jj8nzRUcUXFVGzb0Q==", + "requires": { + "change-case": "^2.3.0", + "deepmerge": "^3.2.0", + "lodash.get": "^4.4.2", + "superagent": "^5.1.1" + } + }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -24345,6 +25549,21 @@ } } }, + "sentence-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-1.1.3.tgz", + "integrity": "sha512-laa/UDTPXsrQnoN/Kc8ZO7gTeEjMsuPiDgUCk9N0iINRZvqAMCTXjGl8+tD27op1eF/JHbdUlEUmovDh6AX7sA==", + "requires": { + "lower-case": "^1.1.1" + }, + "dependencies": { + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + } + } + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -24470,7 +25689,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, "requires": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", @@ -24495,6 +25713,14 @@ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true }, + "snake-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-1.1.2.tgz", + "integrity": "sha512-oapUKC+qulnUIN+/O7Tbl2msi9PQvJeivGN9RNbygxzI2EOY0gA96i8BJLYnGUWSLGcYtyW4YYqnGTZEySU/gg==", + "requires": { + "sentence-case": "^1.1.2" + } + }, "socket.io": { "version": "4.5.1", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.1.tgz", @@ -24709,6 +25935,14 @@ "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" + }, + "dependencies": { + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + } } }, "ssri": { @@ -24741,7 +25975,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" }, @@ -24749,8 +25982,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -24851,6 +26083,36 @@ "normalize-path": "^3.0.0" } }, + "superagent": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz", + "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==", + "requires": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -24865,6 +26127,22 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, + "swap-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==", + "requires": { + "lower-case": "^1.1.1", + "upper-case": "^1.1.1" + }, + "dependencies": { + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==" + } + } + }, "symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -25026,6 +26304,15 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "title-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-1.1.2.tgz", + "integrity": "sha512-xYbo5Um5MBgn24xJSK+x5hZ8ehuGXTVhgx32KJCThHRHwpyIb1lmABi1DH5VvN9E7rNEquPjz//rF/tZQd7mjQ==", + "requires": { + "sentence-case": "^1.1.1", + "upper-case": "^1.0.3" + } + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -25214,6 +26501,11 @@ "integrity": "sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==", "dev": true }, + "unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==" + }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -25281,6 +26573,19 @@ "picocolors": "^1.0.0" } }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==" + }, + "upper-case-first": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha512-wINKYvI3Db8dtjikdAqoBbZoP6Q+PZUyfMR7pmwHzjC2quzSkUq5DmPrTtPEqHaz8AGtmsB4TqwapMTM1QAQOQ==", + "requires": { + "upper-case": "^1.1.1" + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -25292,8 +26597,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utila": { "version": "0.4.0", diff --git a/tools/winscope-ng/package.json b/tools/winscope-ng/package.json index eee118a98..8e0dd14af 100644 --- a/tools/winscope-ng/package.json +++ b/tools/winscope-ng/package.json @@ -22,13 +22,22 @@ "@angular/core": "^14.0.1", "@angular/elements": "^14.0.1", "@angular/forms": "^14.0.0", + "@angular/material": "^14.0.4", "@angular/platform-browser": "^14.0.0", "@angular/platform-browser-dynamic": "^14.0.0", "@angular/router": "^14.0.0", + "@auth0/auth0-angular": "^1.10.0", + "@ngrx/effects": "^14.0.2", + "@ngrx/store": "^14.0.2", + "@ngxs/store": "^3.7.4", + "@types/jsbn": "^1.2.30", "angular2-template-loader": "^0.6.2", + "auth0": "^2.42.0", "html-loader": "^3.1.0", "html-webpack-inline-source-plugin": "^1.0.0-beta.2", "html-webpack-plugin": "^5.5.0", + "jsbn": "^1.1.0", + "jsbn-rsa": "^1.0.4", "kotlin": "^1.7.0", "kotlin-compiler": "^1.7.0", "loader-utils": "^2.0.0", @@ -46,7 +55,11 @@ "@angular-devkit/build-angular": "^14.0.0", "@angular/cli": "~14.0.0", "@angular/compiler-cli": "^14.0.0", + "@ngxs/devtools-plugin": "^3.7.4", "@types/jasmine": "~4.0.0", + "@types/jquery": "^3.5.14", + "@types/node": "^18.0.4", + "@types/w3c-web-usb": "^1.0.6", "@typescript-eslint/eslint-plugin": "^5.30.6", "@typescript-eslint/parser": "^5.30.6", "eslint": "^8.19.0", diff --git a/tools/winscope-ng/src/adb/winscope_proxy.py b/tools/winscope-ng/src/adb/winscope_proxy.py new file mode 100644 index 000000000..b032599ab --- /dev/null +++ b/tools/winscope-ng/src/adb/winscope_proxy.py @@ -0,0 +1,867 @@ +#!/usr/bin/python3 + +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# This is an ADB proxy for Winscope. +# +# Requirements: python3.5 and ADB installed and in system PATH. +# +# Usage: +# run: python3 winscope_proxy.py +# + +import json +import logging +import os +import re +import secrets +import signal +import subprocess +import sys +import threading +import time +from abc import abstractmethod +from enum import Enum +from http import HTTPStatus +from http.server import HTTPServer, BaseHTTPRequestHandler +from tempfile import NamedTemporaryFile +import base64 + +# CONFIG # + +LOG_LEVEL = logging.DEBUG + +PORT = 5544 + +# Keep in sync with WINSCOPE_PROXY_VERSION in Winscope DataAdb.vue +VERSION = '0.8' + +WINSCOPE_VERSION_HEADER = "Winscope-Proxy-Version" +WINSCOPE_TOKEN_HEADER = "Winscope-Token" + +# Location to save the proxy security token +WINSCOPE_TOKEN_LOCATION = os.path.expanduser('~/.config/winscope/.token') + +# Winscope traces extensions +WINSCOPE_EXT = ".winscope" +WINSCOPE_EXT_LEGACY = ".pb" +WINSCOPE_EXTS = [WINSCOPE_EXT, WINSCOPE_EXT_LEGACY] + +# Winscope traces directory +WINSCOPE_DIR = "/data/misc/wmtrace/" + +# Max interval between the client keep-alive requests in seconds +KEEP_ALIVE_INTERVAL_S = 5 + +logging.basicConfig(stream=sys.stderr, level=LOG_LEVEL, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') +log = logging.getLogger("ADBProxy") + + +class File: + def __init__(self, file, filetype) -> None: + self.file = file + self.type = filetype + + def get_filepaths(self, device_id): + return [self.file] + + def get_filetype(self): + return self.type + + +class FileMatcher: + def __init__(self, path, matcher, filetype) -> None: + self.path = path + self.matcher = matcher + self.type = filetype + + def get_filepaths(self, device_id): + matchingFiles = call_adb( + f"shell su root find {self.path} -name {self.matcher}", device_id) + + log.debug("Found file %s", matchingFiles.split('\n')[:-1]) + return matchingFiles.split('\n')[:-1] + + def get_filetype(self): + return self.type + + +class WinscopeFileMatcher(FileMatcher): + def __init__(self, path, matcher, filetype) -> None: + self.path = path + self.internal_matchers = list(map(lambda ext: FileMatcher(path, f'{matcher}{ext}', filetype), + WINSCOPE_EXTS)) + self.type = filetype + + def get_filepaths(self, device_id): + for matcher in self.internal_matchers: + files = matcher.get_filepaths(device_id) + if len(files) > 0: + return files + log.debug("No files found") + return [] + + +class TraceTarget: + """Defines a single parameter to trace. + + Attributes: + file_matchers: the matchers used to identify the paths on the device the trace results are saved to. + trace_start: command to start the trace from adb shell, must not block. + trace_stop: command to stop the trace, should block until the trace is stopped. + """ + + def __init__(self, files, trace_start: str, trace_stop: str) -> None: + if type(files) is not list: + files = [files] + self.files = files + self.trace_start = trace_start + self.trace_stop = trace_stop + +# Order of files matters as they will be expected in that order and decoded in that order +TRACE_TARGETS = { + "window_trace": TraceTarget( + WinscopeFileMatcher(WINSCOPE_DIR, "wm_trace", "window_trace"), + 'su root cmd window tracing start\necho "WM trace started."', + 'su root cmd window tracing stop >/dev/null 2>&1' + ), + "accessibility_trace": TraceTarget( + WinscopeFileMatcher("/data/misc/a11ytrace", "a11y_trace", "accessibility_trace"), + 'su root cmd accessibility start-trace\necho "Accessibility trace started."', + 'su root cmd accessibility stop-trace >/dev/null 2>&1' + ), + "layers_trace": TraceTarget( + WinscopeFileMatcher(WINSCOPE_DIR, "layers_trace", "layers_trace"), + 'su root service call SurfaceFlinger 1025 i32 1\necho "SF trace started."', + 'su root service call SurfaceFlinger 1025 i32 0 >/dev/null 2>&1' + ), + "screen_recording": TraceTarget( + File(f'/data/local/tmp/screen.mp4', "screen_recording"), + f'screenrecord --bit-rate 8M /data/local/tmp/screen.mp4 >/dev/null 2>&1 &\necho "ScreenRecorder started."', + 'pkill -l SIGINT screenrecord >/dev/null 2>&1' + ), + "transactions": TraceTarget( + WinscopeFileMatcher(WINSCOPE_DIR, "transactions_trace", "transactions"), + 'su root service call SurfaceFlinger 1041 i32 1\necho "SF transactions recording started."', + 'su root service call SurfaceFlinger 1041 i32 0 >/dev/null 2>&1' + ), + "transactions_legacy": TraceTarget( + [ + WinscopeFileMatcher(WINSCOPE_DIR, "transaction_trace", "transactions_legacy"), + FileMatcher(WINSCOPE_DIR, f'transaction_merges_*', "transaction_merges"), + ], + 'su root service call SurfaceFlinger 1020 i32 1\necho "SF transactions recording started."', + 'su root service call SurfaceFlinger 1020 i32 0 >/dev/null 2>&1' + ), + "proto_log": TraceTarget( + WinscopeFileMatcher(WINSCOPE_DIR, "wm_log", "proto_log"), + 'su root cmd window logging start\necho "WM logging started."', + 'su root cmd window logging stop >/dev/null 2>&1' + ), + "ime_trace_clients": TraceTarget( + WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_clients", "ime_trace_clients"), + 'su root ime tracing start\necho "Clients IME trace started."', + 'su root ime tracing stop >/dev/null 2>&1' + ), + "ime_trace_service": TraceTarget( + WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_service", "ime_trace_service"), + 'su root ime tracing start\necho "Service IME trace started."', + 'su root ime tracing stop >/dev/null 2>&1' + ), + "ime_trace_managerservice": TraceTarget( + WinscopeFileMatcher(WINSCOPE_DIR, "ime_trace_managerservice", "ime_trace_managerservice"), + 'su root ime tracing start\necho "ManagerService IME trace started."', + 'su root ime tracing stop >/dev/null 2>&1' + ), + "wayland_trace": TraceTarget( + WinscopeFileMatcher("/data/misc/wltrace", "wl_trace", "wl_trace"), + 'su root service call Wayland 26 i32 1 >/dev/null\necho "Wayland trace started."', + 'su root service call Wayland 26 i32 0 >/dev/null' + ), +} + + +class SurfaceFlingerTraceConfig: + """Handles optional configuration for surfaceflinger traces. + """ + + def __init__(self) -> None: + # default config flags CRITICAL | INPUT | SYNC + self.flags = 1 << 0 | 1 << 1 | 1 << 6 + + def add(self, config: str) -> None: + self.flags |= CONFIG_FLAG[config] + + def is_valid(self, config: str) -> bool: + return config in CONFIG_FLAG + + def command(self) -> str: + return f'su root service call SurfaceFlinger 1033 i32 {self.flags}' + +class SurfaceFlingerTraceSelectedConfig: + """Handles optional selected configuration for surfaceflinger traces. + """ + + def __init__(self) -> None: + # defaults set for all configs + self.selectedConfigs = { + "sfbuffersize": "16000" + } + + def add(self, configType, configValue) -> None: + self.selectedConfigs[configType] = configValue + + def is_valid(self, configType) -> bool: + return configType in CONFIG_SF_SELECTION + + def setBufferSize(self) -> str: + return f'su root service call SurfaceFlinger 1029 i32 {self.selectedConfigs["sfbuffersize"]}' + +class WindowManagerTraceSelectedConfig: + """Handles optional selected configuration for windowmanager traces. + """ + + def __init__(self) -> None: + # defaults set for all configs + self.selectedConfigs = { + "wmbuffersize": "16000", + "tracinglevel": "debug", + "tracingtype": "frame", + } + + def add(self, configType, configValue) -> None: + self.selectedConfigs[configType] = configValue + + def is_valid(self, configType) -> bool: + return configType in CONFIG_WM_SELECTION + + def setBufferSize(self) -> str: + return f'su root cmd window tracing size {self.selectedConfigs["wmbuffersize"]}' + + def setTracingLevel(self) -> str: + return f'su root cmd window tracing level {self.selectedConfigs["tracinglevel"]}' + + def setTracingType(self) -> str: + return f'su root cmd window tracing {self.selectedConfigs["tracingtype"]}' + + +CONFIG_FLAG = { + "composition": 1 << 2, + "metadata": 1 << 3, + "hwc": 1 << 4, + "tracebuffers": 1 << 5 +} + +#Keep up to date with options in DataAdb.vue +CONFIG_SF_SELECTION = [ + "sfbuffersize", +] + +#Keep up to date with options in DataAdb.vue +CONFIG_WM_SELECTION = [ + "wmbuffersize", + "tracingtype", + "tracinglevel", +] + +class DumpTarget: + """Defines a single parameter to trace. + + Attributes: + file: the path on the device the dump results are saved to. + dump_command: command to dump state to file. + """ + + def __init__(self, files, dump_command: str) -> None: + if type(files) is not list: + files = [files] + self.files = files + self.dump_command = dump_command + + +DUMP_TARGETS = { + "window_dump": DumpTarget( + File(f'/data/local/tmp/wm_dump{WINSCOPE_EXT}', "window_dump"), + f'su root dumpsys window --proto > /data/local/tmp/wm_dump{WINSCOPE_EXT}' + ), + "layers_dump": DumpTarget( + File(f'/data/local/tmp/sf_dump{WINSCOPE_EXT}', "layers_dump"), + f'su root dumpsys SurfaceFlinger --proto > /data/local/tmp/sf_dump{WINSCOPE_EXT}' + ) +} + + +# END OF CONFIG # + + +def get_token() -> str: + """Returns saved proxy security token or creates new one""" + try: + with open(WINSCOPE_TOKEN_LOCATION, 'r') as token_file: + token = token_file.readline() + log.debug("Loaded token {} from {}".format( + token, WINSCOPE_TOKEN_LOCATION)) + return token + except IOError: + token = secrets.token_hex(32) + os.makedirs(os.path.dirname(WINSCOPE_TOKEN_LOCATION), exist_ok=True) + try: + with open(WINSCOPE_TOKEN_LOCATION, 'w') as token_file: + log.debug("Created and saved token {} to {}".format( + token, WINSCOPE_TOKEN_LOCATION)) + token_file.write(token) + os.chmod(WINSCOPE_TOKEN_LOCATION, 0o600) + except IOError: + log.error("Unable to save persistent token {} to {}".format( + token, WINSCOPE_TOKEN_LOCATION)) + return token + + +secret_token = get_token() + + +class RequestType(Enum): + GET = 1 + POST = 2 + HEAD = 3 + + +def add_standard_headers(server): + server.send_header('Cache-Control', 'no-cache, no-store, must-revalidate') + server.send_header('Access-Control-Allow-Origin', '*') + server.send_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') + server.send_header('Access-Control-Allow-Headers', + WINSCOPE_TOKEN_HEADER + ', Content-Type, Content-Length') + server.send_header('Access-Control-Expose-Headers', + 'Winscope-Proxy-Version') + server.send_header(WINSCOPE_VERSION_HEADER, VERSION) + server.end_headers() + + +class RequestEndpoint: + """Request endpoint to use with the RequestRouter.""" + + @abstractmethod + def process(self, server, path): + pass + + +class AdbError(Exception): + """Unsuccessful ADB operation""" + pass + + +class BadRequest(Exception): + """Invalid client request""" + pass + + +class RequestRouter: + """Handles HTTP request authentication and routing""" + + def __init__(self, handler): + self.request = handler + self.endpoints = {} + + def register_endpoint(self, method: RequestType, name: str, endpoint: RequestEndpoint): + self.endpoints[(method, name)] = endpoint + + def __bad_request(self, error: str): + log.warning("Bad request: " + error) + self.request.respond(HTTPStatus.BAD_REQUEST, b"Bad request!\nThis is Winscope ADB proxy.\n\n" + + error.encode("utf-8"), 'text/txt') + + def __internal_error(self, error: str): + log.error("Internal error: " + error) + self.request.respond(HTTPStatus.INTERNAL_SERVER_ERROR, + error.encode("utf-8"), 'text/txt') + + def __bad_token(self): + log.info("Bad token") + self.request.respond(HTTPStatus.FORBIDDEN, b"Bad Winscope authorisation token!\nThis is Winscope ADB proxy.\n", + 'text/txt') + + def process(self, method: RequestType): + token = self.request.headers[WINSCOPE_TOKEN_HEADER] + if not token or token != secret_token: + return self.__bad_token() + path = self.request.path.strip('/').split('/') + if path and len(path) > 0: + endpoint_name = path[0] + try: + return self.endpoints[(method, endpoint_name)].process(self.request, path[1:]) + except KeyError: + return self.__bad_request("Unknown endpoint /{}/".format(endpoint_name)) + except AdbError as ex: + return self.__internal_error(str(ex)) + except BadRequest as ex: + return self.__bad_request(str(ex)) + except Exception as ex: + return self.__internal_error(repr(ex)) + self.__bad_request("No endpoint specified") + + +def call_adb(params: str, device: str = None, stdin: bytes = None): + command = ['adb'] + (['-s', device] if device else []) + params.split(' ') + try: + log.debug("Call: " + ' '.join(command)) + return subprocess.check_output(command, stderr=subprocess.STDOUT, input=stdin).decode('utf-8') + except OSError as ex: + log.debug('Error executing adb command: {}\n{}'.format( + ' '.join(command), repr(ex))) + raise AdbError('Error executing adb command: {}\n{}'.format( + ' '.join(command), repr(ex))) + except subprocess.CalledProcessError as ex: + log.debug('Error executing adb command: {}\n{}'.format( + ' '.join(command), ex.output.decode("utf-8"))) + raise AdbError('Error executing adb command: adb {}\n{}'.format( + params, ex.output.decode("utf-8"))) + + +def call_adb_outfile(params: str, outfile, device: str = None, stdin: bytes = None): + try: + process = subprocess.Popen(['adb'] + (['-s', device] if device else []) + params.split(' '), stdout=outfile, + stderr=subprocess.PIPE) + _, err = process.communicate(stdin) + outfile.seek(0) + if process.returncode != 0: + log.debug('Error executing adb command: adb {}\n'.format(params) + err.decode( + 'utf-8') + '\n' + outfile.read().decode('utf-8')) + raise AdbError('Error executing adb command: adb {}\n'.format(params) + err.decode( + 'utf-8') + '\n' + outfile.read().decode('utf-8')) + except OSError as ex: + log.debug('Error executing adb command: adb {}\n{}'.format( + params, repr(ex))) + raise AdbError( + 'Error executing adb command: adb {}\n{}'.format(params, repr(ex))) + + +class CheckWaylandServiceEndpoint(RequestEndpoint): + _listDevicesEndpoint = None + + def __init__(self, listDevicesEndpoint): + self._listDevicesEndpoint = listDevicesEndpoint + + def process(self, server, path): + self._listDevicesEndpoint.process(server, path) + foundDevices = self._listDevicesEndpoint._foundDevices + + if len(foundDevices) > 1: + res = 'false' + else: + raw_res = call_adb('shell service check Wayland') + res = 'false' if 'not found' in raw_res else 'true' + server.respond(HTTPStatus.OK, res.encode("utf-8"), "text/json") + + +class ListDevicesEndpoint(RequestEndpoint): + ADB_INFO_RE = re.compile("^([A-Za-z0-9.:\\-]+)\\s+(\\w+)(.*model:(\\w+))?") + _foundDevices = None + + def process(self, server, path): + lines = list(filter(None, call_adb('devices -l').split('\n'))) + devices = {m.group(1): { + 'authorised': str(m.group(2)) != 'unauthorized', + 'model': m.group(4).replace('_', ' ') if m.group(4) else '' + } for m in [ListDevicesEndpoint.ADB_INFO_RE.match(d) for d in lines[1:]] if m} + self._foundDevices = devices + j = json.dumps(devices) + log.debug("Detected devices: " + j) + server.respond(HTTPStatus.OK, j.encode("utf-8"), "text/json") + + +class DeviceRequestEndpoint(RequestEndpoint): + def process(self, server, path): + if len(path) > 0 and re.fullmatch("[A-Za-z0-9.:\\-]+", path[0]): + self.process_with_device(server, path[1:], path[0]) + else: + raise BadRequest("Device id not specified") + + @abstractmethod + def process_with_device(self, server, path, device_id): + pass + + def get_request(self, server) -> str: + try: + length = int(server.headers["Content-Length"]) + except KeyError as err: + raise BadRequest("Missing Content-Length header\n" + str(err)) + except ValueError as err: + raise BadRequest("Content length unreadable\n" + str(err)) + return json.loads(server.rfile.read(length).decode("utf-8")) + + +class FetchFilesEndpoint(DeviceRequestEndpoint): + def process_with_device(self, server, path, device_id): + if len(path) != 1: + raise BadRequest("File not specified") + if path[0] in TRACE_TARGETS: + files = TRACE_TARGETS[path[0]].files + elif path[0] in DUMP_TARGETS: + files = DUMP_TARGETS[path[0]].files + else: + raise BadRequest("Unknown file specified") + + file_buffers = dict() + + for f in files: + file_type = f.get_filetype() + file_paths = f.get_filepaths(device_id) + + for file_path in file_paths: + with NamedTemporaryFile() as tmp: + log.debug( + f"Fetching file {file_path} from device to {tmp.name}") + call_adb_outfile('exec-out su root cat ' + + file_path, tmp, device_id) + log.debug(f"Deleting file {file_path} from device") + call_adb('shell su root rm ' + file_path, device_id) + log.debug(f"Uploading file {tmp.name}") + if file_type not in file_buffers: + file_buffers[file_type] = [] + buf = base64.encodebytes(tmp.read()).decode("utf-8") + file_buffers[file_type].append(buf) + + if (len(file_buffers) == 0): + log.error("Proxy didn't find any file to fetch") + + # server.send_header('X-Content-Type-Options', 'nosniff') + # add_standard_headers(server) + j = json.dumps(file_buffers) + server.respond(HTTPStatus.OK, j.encode("utf-8"), "text/json") + + +def check_root(device_id): + log.debug("Checking root access on {}".format(device_id)) + return int(call_adb('shell su root id -u', device_id)) == 0 + + +TRACE_THREADS = {} + + +class TraceThread(threading.Thread): + def __init__(self, device_id, command): + self._keep_alive_timer = None + self.trace_command = command + self._device_id = device_id + self.out = None, + self.err = None, + self._success = False + try: + shell = ['adb', '-s', self._device_id, 'shell'] + log.debug("Starting trace shell {}".format(' '.join(shell))) + self.process = subprocess.Popen(shell, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, stdin=subprocess.PIPE, start_new_session=True) + except OSError as ex: + raise AdbError( + 'Error executing adb command: adb shell\n{}'.format(repr(ex))) + + super().__init__() + + def timeout(self): + if self.is_alive(): + log.warning( + "Keep-alive timeout for trace on {}".format(self._device_id)) + self.end_trace() + if self._device_id in TRACE_THREADS: + TRACE_THREADS.pop(self._device_id) + + def reset_timer(self): + log.debug( + "Resetting keep-alive clock for trace on {}".format(self._device_id)) + if self._keep_alive_timer: + self._keep_alive_timer.cancel() + self._keep_alive_timer = threading.Timer( + KEEP_ALIVE_INTERVAL_S, self.timeout) + self._keep_alive_timer.start() + + def end_trace(self): + if self._keep_alive_timer: + self._keep_alive_timer.cancel() + log.debug("Sending SIGINT to the trace process on {}".format( + self._device_id)) + self.process.send_signal(signal.SIGINT) + try: + log.debug("Waiting for trace shell to exit for {}".format( + self._device_id)) + self.process.wait(timeout=5) + except TimeoutError: + log.debug( + "TIMEOUT - sending SIGKILL to the trace process on {}".format(self._device_id)) + self.process.kill() + self.join() + + def run(self): + log.debug("Trace started on {}".format(self._device_id)) + self.reset_timer() + self.out, self.err = self.process.communicate(self.trace_command) + log.debug("Trace ended on {}, waiting for cleanup".format(self._device_id)) + time.sleep(0.2) + for i in range(50): + if call_adb("shell su root cat /data/local/tmp/winscope_status", device=self._device_id) == 'TRACE_OK\n': + call_adb( + "shell su root rm /data/local/tmp/winscope_status", device=self._device_id) + log.debug("Trace finished successfully on {}".format( + self._device_id)) + self._success = True + break + log.debug("Still waiting for cleanup on {}".format(self._device_id)) + time.sleep(0.1) + + def success(self): + return self._success + + +class StartTrace(DeviceRequestEndpoint): + TRACE_COMMAND = """ +set -e + +echo "Starting trace..." +echo "TRACE_START" > /data/local/tmp/winscope_status + +# Do not print anything to stdout/stderr in the handler +function stop_trace() {{ + trap - EXIT HUP INT + +{} + + echo "TRACE_OK" > /data/local/tmp/winscope_status +}} + +trap stop_trace EXIT HUP INT +echo "Signal handler registered." + +{} + +# ADB shell does not handle hung up well and does not call HUP handler when a child is active in foreground, +# as a workaround we sleep for short intervals in a loop so the handler is called after a sleep interval. +while true; do sleep 0.1; done +""" + + def process_with_device(self, server, path, device_id): + try: + requested_types = self.get_request(server) + requested_traces = [TRACE_TARGETS[t] for t in requested_types] + except KeyError as err: + raise BadRequest("Unsupported trace target\n" + str(err)) + if device_id in TRACE_THREADS: + log.warning("Trace already in progress for {}", device_id) + server.respond(HTTPStatus.OK, b'', "text/plain") + if not check_root(device_id): + raise AdbError( + "Unable to acquire root privileges on the device - check the output of 'adb -s {} shell su root id'".format( + device_id)) + command = StartTrace.TRACE_COMMAND.format( + '\n'.join([t.trace_stop for t in requested_traces]), + '\n'.join([t.trace_start for t in requested_traces])) + log.debug("Trace requested for {} with targets {}".format( + device_id, ','.join(requested_types))) + TRACE_THREADS[device_id] = TraceThread( + device_id, command.encode('utf-8')) + TRACE_THREADS[device_id].start() + server.respond(HTTPStatus.OK, b'', "text/plain") + + +class EndTrace(DeviceRequestEndpoint): + def process_with_device(self, server, path, device_id): + if device_id not in TRACE_THREADS: + raise BadRequest("No trace in progress for {}".format(device_id)) + if TRACE_THREADS[device_id].is_alive(): + TRACE_THREADS[device_id].end_trace() + + success = TRACE_THREADS[device_id].success() + out = TRACE_THREADS[device_id].out + \ + b"\n" + TRACE_THREADS[device_id].err + command = TRACE_THREADS[device_id].trace_command + TRACE_THREADS.pop(device_id) + if success: + server.respond(HTTPStatus.OK, out, "text/plain") + else: + raise AdbError( + "Error tracing the device\n### Output ###\n" + out.decode( + "utf-8") + "\n### Command: adb -s {} shell ###\n### Input ###\n".format(device_id) + command.decode( + "utf-8")) + + +def execute_command(server, device_id, shell, configType, configValue): + process = subprocess.Popen(shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + stdin=subprocess.PIPE, start_new_session=True) + log.debug(f"Changing trace config on device {device_id} {configType}:{configValue}") + out, err = process.communicate(configValue.encode('utf-8')) + if process.returncode != 0: + raise AdbError( + f"Error executing command:\n {configValue}\n\n### OUTPUT ###{out.decode('utf-8')}\n{err.decode('utf-8')}") + log.debug(f"Changing trace config finished on device {device_id}") + server.respond(HTTPStatus.OK, b'', "text/plain") + + +class ConfigTrace(DeviceRequestEndpoint): + def process_with_device(self, server, path, device_id): + try: + requested_configs = self.get_request(server) + config = SurfaceFlingerTraceConfig() + for requested_config in requested_configs: + if not config.is_valid(requested_config): + raise BadRequest( + f"Unsupported config {requested_config}\n") + config.add(requested_config) + except KeyError as err: + raise BadRequest("Unsupported trace target\n" + str(err)) + if device_id in TRACE_THREADS: + BadRequest(f"Trace in progress for {device_id}") + if not check_root(device_id): + raise AdbError( + f"Unable to acquire root privileges on the device - check the output of 'adb -s {device_id} shell su root id'") + command = config.command() + shell = ['adb', '-s', device_id, 'shell'] + log.debug(f"Starting shell {' '.join(shell)}") + execute_command(server, device_id, shell, "sf buffer size", command) + + +def add_selected_request_to_config(self, server, device_id, config): + try: + requested_configs = self.get_request(server) + for requested_config in requested_configs: + if config.is_valid(requested_config): + config.add(requested_config, requested_configs[requested_config]) + else: + raise BadRequest( + f"Unsupported config {requested_config}\n") + except KeyError as err: + raise BadRequest("Unsupported trace target\n" + str(err)) + if device_id in TRACE_THREADS: + BadRequest(f"Trace in progress for {device_id}") + if not check_root(device_id): + raise AdbError( + f"Unable to acquire root privileges on the device - check the output of 'adb -s {device_id} shell su root id'") + return config + + +class SurfaceFlingerSelectedConfigTrace(DeviceRequestEndpoint): + def process_with_device(self, server, path, device_id): + config = SurfaceFlingerTraceSelectedConfig() + config = add_selected_request_to_config(self, server, device_id, config) + setBufferSize = config.setBufferSize() + shell = ['adb', '-s', device_id, 'shell'] + log.debug(f"Starting shell {' '.join(shell)}") + execute_command(server, device_id, shell, "sf buffer size", setBufferSize) + + +class WindowManagerSelectedConfigTrace(DeviceRequestEndpoint): + def process_with_device(self, server, path, device_id): + config = WindowManagerTraceSelectedConfig() + config = add_selected_request_to_config(self, server, device_id, config) + setBufferSize = config.setBufferSize() + setTracingType = config.setTracingType() + setTracingLevel = config.setTracingLevel() + shell = ['adb', '-s', device_id, 'shell'] + log.debug(f"Starting shell {' '.join(shell)}") + execute_command(server, device_id, shell, "wm buffer size", setBufferSize) + execute_command(server, device_id, shell, "tracing type", setTracingType) + execute_command(server, device_id, shell, "tracing level", setTracingLevel) + + +class StatusEndpoint(DeviceRequestEndpoint): + def process_with_device(self, server, path, device_id): + if device_id not in TRACE_THREADS: + raise BadRequest("No trace in progress for {}".format(device_id)) + TRACE_THREADS[device_id].reset_timer() + server.respond(HTTPStatus.OK, str( + TRACE_THREADS[device_id].is_alive()).encode("utf-8"), "text/plain") + + +class DumpEndpoint(DeviceRequestEndpoint): + def process_with_device(self, server, path, device_id): + try: + requested_types = self.get_request(server) + requested_traces = [DUMP_TARGETS[t] for t in requested_types] + except KeyError as err: + raise BadRequest("Unsupported trace target\n" + str(err)) + if device_id in TRACE_THREADS: + BadRequest("Trace in progress for {}".format(device_id)) + if not check_root(device_id): + raise AdbError( + "Unable to acquire root privileges on the device - check the output of 'adb -s {} shell su root id'" + .format(device_id)) + command = '\n'.join(t.dump_command for t in requested_traces) + shell = ['adb', '-s', device_id, 'shell'] + log.debug("Starting dump shell {}".format(' '.join(shell))) + process = subprocess.Popen(shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + stdin=subprocess.PIPE, start_new_session=True) + log.debug("Starting dump on device {}".format(device_id)) + out, err = process.communicate(command.encode('utf-8')) + if process.returncode != 0: + raise AdbError("Error executing command:\n" + command + "\n\n### OUTPUT ###" + out.decode('utf-8') + "\n" + + err.decode('utf-8')) + log.debug("Dump finished on device {}".format(device_id)) + server.respond(HTTPStatus.OK, b'', "text/plain") + + +class ADBWinscopeProxy(BaseHTTPRequestHandler): + def __init__(self, request, client_address, server): + self.router = RequestRouter(self) + listDevicesEndpoint = ListDevicesEndpoint() + self.router.register_endpoint( + RequestType.GET, "devices", listDevicesEndpoint) + self.router.register_endpoint( + RequestType.GET, "status", StatusEndpoint()) + self.router.register_endpoint( + RequestType.GET, "fetch", FetchFilesEndpoint()) + self.router.register_endpoint(RequestType.POST, "start", StartTrace()) + self.router.register_endpoint(RequestType.POST, "end", EndTrace()) + self.router.register_endpoint(RequestType.POST, "dump", DumpEndpoint()) + self.router.register_endpoint( + RequestType.POST, "configtrace", ConfigTrace()) + self.router.register_endpoint( + RequestType.POST, "selectedsfconfigtrace", SurfaceFlingerSelectedConfigTrace()) + self.router.register_endpoint( + RequestType.POST, "selectedwmconfigtrace", WindowManagerSelectedConfigTrace()) + self.router.register_endpoint( + RequestType.GET, "checkwayland", CheckWaylandServiceEndpoint(listDevicesEndpoint)) + super().__init__(request, client_address, server) + + def respond(self, code: int, data: bytes, mime: str) -> None: + self.send_response(code) + self.send_header('Content-type', mime) + add_standard_headers(self) + self.wfile.write(data) + + def do_GET(self): + self.router.process(RequestType.GET) + + def do_POST(self): + self.router.process(RequestType.POST) + + def do_OPTIONS(self): + self.send_response(HTTPStatus.OK) + self.send_header('Allow', 'GET,POST') + add_standard_headers(self) + self.end_headers() + self.wfile.write(b'GET,POST') + + def log_request(self, code='-', size='-'): + log.info('{} {} {}'.format(self.requestline, str(code), str(size))) + + +if __name__ == '__main__': + print("Winscope ADB Connect proxy version: " + VERSION) + print('Winscope token: ' + secret_token) + httpd = HTTPServer(('localhost', PORT), ADBWinscopeProxy) + try: + httpd.serve_forever() + except KeyboardInterrupt: + log.info("Shutting down") \ No newline at end of file diff --git a/tools/winscope-ng/src/app/adb_proxy.component.spec.ts b/tools/winscope-ng/src/app/adb_proxy.component.spec.ts new file mode 100644 index 000000000..f048b6b55 --- /dev/null +++ b/tools/winscope-ng/src/app/adb_proxy.component.spec.ts @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { CommonModule } from "@angular/common"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { AdbProxyComponent } from "./adb_proxy.component"; +import { proxyClient, ProxyState } from "../trace_collection/proxy_client"; +import { MatIconModule } from "@angular/material/icon"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatInputModule } from "@angular/material/input"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { MatButtonModule } from "@angular/material/button"; +import { NO_ERRORS_SCHEMA } from "@angular/core"; + +describe("AdbProxyComponent", () => { + let fixture: ComponentFixture; + let component: AdbProxyComponent; + let htmlElement: HTMLElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + MatIconModule, + MatFormFieldModule, + MatInputModule, + BrowserAnimationsModule, + MatButtonModule + ], + declarations: [AdbProxyComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + fixture = TestBed.createComponent(AdbProxyComponent); + component = fixture.componentInstance; + component.proxy = proxyClient; + htmlElement = fixture.nativeElement; + }); + + it("can be created", () => { + expect(component).toBeTruthy(); + }); + + it("check correct icon and message displays if no proxy", () => { + component.proxy.setState(ProxyState.NO_PROXY); + fixture.detectChanges(); + expect(htmlElement.querySelector(".adb-info")?.innerHTML).toBe("Unable to connect to Winscope ADB proxy"); + expect(htmlElement.querySelector(".adb-icon")?.innerHTML).toBe("error"); + }); + + it("check correct icon and message displays if invalid proxy", () => { + component.proxy.setState(ProxyState.INVALID_VERSION); + fixture.detectChanges(); + expect(htmlElement.querySelector(".adb-info")?.innerHTML).toBe("Your local proxy version is incompatible with Winscope."); + expect(htmlElement.querySelector(".adb-icon")?.innerHTML).toBe("update"); + }); + + it("check correct icon and message displays if unauthorised proxy", () => { + component.proxy.setState(ProxyState.UNAUTH); + fixture.detectChanges(); + expect(htmlElement.querySelector(".adb-info")?.innerHTML).toBe("Proxy authorisation required"); + expect(htmlElement.querySelector(".adb-icon")?.innerHTML).toBe("lock"); + }); + + it("check retry button acts as expected", async () => { + component.proxy.setState(ProxyState.NO_PROXY); + fixture.detectChanges(); + spyOn(component, "restart").and.callThrough(); + const button: HTMLButtonElement | null = htmlElement.querySelector(".retry"); + expect(button).toBeInstanceOf(HTMLButtonElement); + button?.dispatchEvent(new Event("click")); + await fixture.whenStable(); + expect(component.restart).toHaveBeenCalled(); + }); +}); diff --git a/tools/winscope-ng/src/app/adb_proxy.component.ts b/tools/winscope-ng/src/app/adb_proxy.component.ts new file mode 100644 index 000000000..81f6c79dd --- /dev/null +++ b/tools/winscope-ng/src/app/adb_proxy.component.ts @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Component, Input, Output, EventEmitter } from "@angular/core"; +import { proxyClient, ProxyClient, ProxyState } from "../trace_collection/proxy_client"; + +@Component({ + selector: "adb-proxy", + template: ` +
+
+ error + Unable to connect to Winscope ADB proxy +
+
+

Launch the Winscope ADB Connect proxy to capture traces directly from your browser.

+

Python 3.5+ and ADB are required.

+

Run:

+
python3
+
$ANDROID_BUILD_TOP/development/tools/winscope-ng/src/adb/winscope_proxy.py
+

Or get it from the AOSP repository.

+
+
+ + +
+
+ +
+
+ update + Your local proxy version is incompatible with Winscope. +
+
+

Please update the proxy to version {{ proxyVersion }}.

+

Run:

+
python3
+
$ANDROID_BUILD_TOP/development/tools/winscope-ng/src/adb/winscope_proxy.py
+

Or get it from the AOSP repository.

+
+
+ + +
+
+ +
+
+ lock + Proxy authorisation required +
+
+

Enter Winscope proxy token:

+ + + +

The proxy token is printed to console on proxy launch, copy and paste it above.

+
+
+ +
+
+ + `, + styles: [".proxy-key-field {width: 30rem}"] +}) +export class AdbProxyComponent { + @Input() + proxy: ProxyClient = proxyClient; + + @Output() + proxyChange = new EventEmitter(); + + @Output() + addKey = new EventEmitter(); + + states = ProxyState; + proxyKeyItem = ""; + readonly proxyVersion = this.proxy.VERSION; + readonly downloadProxyUrl: string = "https://android.googlesource.com/platform/development/+/master/tools/winscope/adb_proxy/winscope_proxy.py"; + + public restart() { + this.addKey.emit(this.proxyKeyItem); + this.proxy.setState(this.states.CONNECTING); + this.proxyChange.emit(this.proxy); + } +} diff --git a/tools/winscope-ng/src/app/app.component.spec.ts b/tools/winscope-ng/src/app/app.component.spec.ts index e1e4aa1ad..a8e00c87d 100644 --- a/tools/winscope-ng/src/app/app.component.spec.ts +++ b/tools/winscope-ng/src/app/app.component.spec.ts @@ -13,21 +13,50 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { ChangeDetectionStrategy } from "@angular/core"; import {ComponentFixture, TestBed} from "@angular/core/testing"; -import {AppComponent} from "./app.component"; +import { CommonModule } from "@angular/common"; +import { MatCardModule } from "@angular/material/card"; +import { MatButtonModule } from "@angular/material/button"; +import { MatGridListModule } from "@angular/material/grid-list"; + +import { AppComponent } from "./app.component"; +import { CollectTracesComponent } from "./collect_traces.component"; +import { UploadTracesComponent } from "./upload_traces.component"; +import { AdbProxyComponent } from "./adb_proxy.component"; +import { WebAdbComponent } from "./web_adb.component"; +import { TraceConfigComponent } from "./trace_config.component"; + +import { ComponentFixtureAutoDetect } from "@angular/core/testing"; + describe("AppComponent", () => { let fixture: ComponentFixture; let component: AppComponent; let htmlElement: HTMLElement; - beforeAll(async () => { + beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - AppComponent + providers: [ + { provide: ComponentFixtureAutoDetect, useValue: true } ], + imports: [ + CommonModule, + MatCardModule, + MatButtonModule, + MatGridListModule, + ], + declarations: [ + AppComponent, + CollectTracesComponent, + UploadTracesComponent, + AdbProxyComponent, + WebAdbComponent, + TraceConfigComponent, + ], + }).overrideComponent(AppComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } }).compileComponents(); - fixture = TestBed.createComponent(AppComponent); component = fixture.componentInstance; htmlElement = fixture.nativeElement; @@ -41,7 +70,23 @@ describe("AppComponent", () => { expect(component.title).toEqual("winscope-ng"); }); - it("renders the title", () => { - expect(htmlElement.querySelector("div#title")?.innerHTML).toContain("Winscope Viewer 2.0"); + it("renders the page title", () => { + expect(htmlElement.querySelector("#title")?.innerHTML).toContain("Winscope Viewer 2.0"); + }); + + it("displays correct elements when no data loaded", async () => { + component.dataLoaded = false; + fixture.detectChanges(); + expect(htmlElement.querySelector("#collect-traces-card")).toBeTruthy(); + expect(htmlElement.querySelector("#upload-traces-card")).toBeTruthy(); + expect(htmlElement.querySelector("#loaded-data-card")).toBeFalsy(); + }); + + it("displays correct elements when data loaded", async () => { + component.dataLoaded = true; + fixture.detectChanges(); + expect(htmlElement.querySelector("#collect-traces-card")).toBeFalsy(); + expect(htmlElement.querySelector("#upload-traces-card")).toBeFalsy(); + expect(htmlElement.querySelector("#loaded-data-card")).toBeTruthy(); }); }); diff --git a/tools/winscope-ng/src/app/app.component.ts b/tools/winscope-ng/src/app/app.component.ts index 0e66ed4f2..b2d586d21 100644 --- a/tools/winscope-ng/src/app/app.component.ts +++ b/tools/winscope-ng/src/app/app.component.ts @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import {Component, Injector, Inject} from "@angular/core"; -import {createCustomElement} from "@angular/elements"; -import {ViewerWindowManagerComponent} from "viewers/viewer_window_manager/viewer_window_manager.component"; -import {Core} from "./core"; +import { Component, Injector, Inject } from "@angular/core"; +import { createCustomElement } from "@angular/elements"; +import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/viewer_window_manager.component"; +import { Core } from "./core"; +import { ProxyState } from "trace_collection/proxy_client"; +import { PersistentStore } from "../common/persistent_store"; @Component({ selector: "app-root", @@ -25,43 +27,57 @@ import {Core} from "./core"; Winscope Viewer 2.0 -
- +
+ + + + + + +
+ +
+ + Loaded data + +
- -
- -
+
- ` + +
+
+ `, + styles: [".home{width: 100%; display:flex; flex-direction: row; overflow: auto;}"] }) export class AppComponent { title = "winscope-ng"; + core: Core; + states = ProxyState; + store: PersistentStore = new PersistentStore(); + dataLoaded = false; - private core!: Core; - - constructor(@Inject(Injector) injector: Injector) { - customElements.define("viewer-window-manager", - createCustomElement(ViewerWindowManagerComponent, {injector})); + constructor( + @Inject(Injector) injector: Injector + ) { + this.core = new Core(); + if (!customElements.get("viewer-window-manager")) { + customElements.define("viewer-window-manager", + createCustomElement(ViewerWindowManagerComponent, {injector})); + } } - public async onInputFile(event: Event) { - const files = await this.getInputFiles(event); + onCoreChange(newCore: Core) { + this.core = newCore; + } - this.core = new Core(); - await this.core.bootstrap(files); - - const viewersDiv = document.querySelector("div#viewers")!; - viewersDiv.innerHTML = ""; - this.core.getViews().forEach(view => viewersDiv!.appendChild(view) ); - - const timestampsDiv = document.querySelector("div#timestamps")!; - timestampsDiv.innerHTML = `Retrieved ${this.core.getTimestamps().length} unique timestamps`; + onDataLoadedChange(loaded: boolean) { + this.dataLoaded = loaded; } public notifyCurrentTimestamp() { @@ -69,14 +85,8 @@ export class AppComponent { this.core.notifyCurrentTimestamp(dummyTimestamp); } - //TODO: extend with support for multiple files, archives, etc... - private getInputFiles(event: Event): File[] { - const files: any = (event?.target as HTMLInputElement)?.files; - - if (!files || !files[0]) { - return []; - } - - return [files[0]]; + public clearData() { + this.dataLoaded = false; + this.core.clearData(); } } diff --git a/tools/winscope-ng/src/app/app.module.ts b/tools/winscope-ng/src/app/app.module.ts index 9f6140299..659bd16ba 100644 --- a/tools/winscope-ng/src/app/app.module.ts +++ b/tools/winscope-ng/src/app/app.module.ts @@ -1,16 +1,58 @@ import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; +import { CommonModule } from "@angular/common"; +import { MatCardModule } from "@angular/material/card"; +import { MatButtonModule } from "@angular/material/button"; +import { MatGridListModule } from "@angular/material/grid-list"; +import { MatListModule } from "@angular/material/list"; +import { MatProgressSpinnerModule } from "@angular/material/progress-spinner"; +import { MatProgressBarModule } from "@angular/material/progress-bar"; +import { FormsModule } from "@angular/forms"; +import { MatCheckboxModule } from "@angular/material/checkbox"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatIconModule } from "@angular/material/icon"; +import { MatInputModule } from "@angular/material/input"; +import { MatSelectModule } from "@angular/material/select"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { HttpClientModule } from "@angular/common/http"; import { AppComponent } from "./app.component"; -import { ViewerWindowManagerComponent} from "viewers/viewer_window_manager/viewer_window_manager.component"; +import { ViewerWindowManagerComponent } from "viewers/viewer_window_manager/viewer_window_manager.component"; +import { CollectTracesComponent } from "./collect_traces.component"; +import { AdbProxyComponent } from "./adb_proxy.component"; +import { WebAdbComponent } from "./web_adb.component"; +import { TraceConfigComponent } from "./trace_config.component"; +import { UploadTracesComponent } from "./upload_traces.component"; + @NgModule({ declarations: [ AppComponent, - ViewerWindowManagerComponent + ViewerWindowManagerComponent, + CollectTracesComponent, + UploadTracesComponent, + AdbProxyComponent, + WebAdbComponent, + TraceConfigComponent, ], imports: [ - BrowserModule + BrowserModule, + HttpClientModule, + CommonModule, + MatCardModule, + MatButtonModule, + MatGridListModule, + FormsModule, + MatListModule, + MatCheckboxModule, + MatIconModule, + MatProgressSpinnerModule, + MatProgressBarModule, + MatFormFieldModule, + MatInputModule, + MatSelectModule, + BrowserAnimationsModule, + HttpClientModule ], providers: [], bootstrap: [AppComponent] diff --git a/tools/winscope-ng/src/app/collect_traces.component.spec.ts b/tools/winscope-ng/src/app/collect_traces.component.spec.ts new file mode 100644 index 000000000..7f4189c32 --- /dev/null +++ b/tools/winscope-ng/src/app/collect_traces.component.spec.ts @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {ComponentFixture, TestBed} from "@angular/core/testing"; +import {CollectTracesComponent} from "./collect_traces.component"; +import { MatIconModule } from "@angular/material/icon"; +import { MatCardModule } from "@angular/material/card"; +import { AdbProxyComponent } from "./adb_proxy.component"; +import { WebAdbComponent } from "./web_adb.component"; +import { TraceConfigComponent } from "./trace_config.component"; +import { MatListModule } from "@angular/material/list"; +import { MatButtonModule } from "@angular/material/button"; +import { MatProgressBarModule } from "@angular/material/progress-bar"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; + +describe("CollectTracesComponent", () => { + let fixture: ComponentFixture; + let component: CollectTracesComponent; + let htmlElement: HTMLElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatIconModule, + MatCardModule, + MatListModule, + MatIconModule, + MatButtonModule, + MatProgressBarModule, + BrowserAnimationsModule + ], + declarations: [ + CollectTracesComponent, + AdbProxyComponent, + WebAdbComponent, + TraceConfigComponent, + ], + }).compileComponents(); + fixture = TestBed.createComponent(CollectTracesComponent); + component = fixture.componentInstance; + htmlElement = fixture.nativeElement; + component.isAdbProxy = true; + }); + + it("can be created", () => { + expect(component).toBeTruthy(); + }); + + it("renders the expected card title", () => { + fixture.detectChanges(); + expect(htmlElement.querySelector("#title")?.innerHTML).toContain("Collect Traces"); + }); + + it("displays connecting message", () => { + component.connect.isConnectingState = jasmine.createSpy().and.returnValue(true); + fixture.detectChanges(); + expect(htmlElement.querySelector(".connecting-message")?.innerHTML).toContain("Connecting..."); + }); + + it("displays adb set up", async () => { + component.connect.adbSuccess = jasmine.createSpy().and.returnValue(false); + fixture.detectChanges(); + fixture.whenStable().then( () => { + expect(htmlElement.querySelector(".set-up-adb")).toBeTruthy(); + const proxyTab: HTMLButtonElement | null = htmlElement.querySelector("#proxy-tab"); + expect(proxyTab).toBeInstanceOf(HTMLButtonElement); + const webTab: HTMLButtonElement | null = htmlElement.querySelector("#web-tab"); + expect(webTab).toBeInstanceOf(HTMLButtonElement); + }); + }); + + it("displays adb proxy element", async () => { + component.connect.adbSuccess = jasmine.createSpy().and.returnValue(false); + component.isAdbProxy = true; + fixture.detectChanges(); + fixture.whenStable().then( () => { + expect(htmlElement.querySelector("adb-proxy")).toBeTruthy(); + expect(htmlElement.querySelector("web-adb")).toBeFalsy(); + }); + }); + + it("displays web adb element", async () => { + component.connect.adbSuccess = jasmine.createSpy().and.returnValue(false); + component.isAdbProxy = false; + fixture.detectChanges(); + fixture.whenStable().then( () => { + expect(htmlElement.querySelector("adb-proxy")).toBeFalsy(); + expect(htmlElement.querySelector("web-adb")).toBeTruthy(); + }); + }); + + it("changes to adb workflow tab", async () => { + component.connect.adbSuccess = jasmine.createSpy().and.returnValue(false); + component.isAdbProxy = true; + fixture.detectChanges(); + fixture.whenStable().then(() => { + const webTab: HTMLButtonElement | null = htmlElement.querySelector("#web-tab"); + expect(webTab).toBeInstanceOf(HTMLButtonElement); + webTab?.dispatchEvent(new Event("click")); + fixture.whenStable().then(() => { + expect(component.displayWebAdbTab).toHaveBeenCalled(); + }); + }); + }); + + it("displays no connected devices", async () => { + component.connect.isDevicesState = jasmine.createSpy().and.returnValue(true); + component.connect.devices = jasmine.createSpy().and.returnValue({}); + fixture.detectChanges(); + fixture.whenStable().then( () => { + const el = htmlElement.querySelector("devices-connecting"); + expect(el).toBeTruthy(); + expect(el?.innerHTML).toContain("No devices detected"); + }); + }); + + it("displays connected authorised devices", async () => { + component.connect.isDevicesState = jasmine.createSpy().and.returnValue(true); + component.connect.devices = jasmine.createSpy().and.returnValue({"35562": {model: "Pixel 6", authorised:true}}); + fixture.detectChanges(); + fixture.whenStable().then( () => { + const el = htmlElement.querySelector("devices-connecting"); + expect(el).toBeTruthy(); + expect(el?.innerHTML).toContain("Connected devices:"); + expect(el?.innerHTML).toContain("Pixel 6"); + expect(el?.innerHTML).toContain("smartphone"); + }); + }); + + it("displays connected unauthorised devices", async () => { + component.connect.isDevicesState = jasmine.createSpy().and.returnValue(true); + component.connect.devices = jasmine.createSpy().and.returnValue({"35562": {model: "Pixel 6", authorised:false}}); + fixture.detectChanges(); + fixture.whenStable().then( () => { + const el = htmlElement.querySelector("devices-connecting"); + expect(el).toBeTruthy(); + expect(el?.innerHTML).toContain("Connected devices:"); + expect(el?.innerHTML).toContain("unauthorised"); + expect(el?.innerHTML).toContain("screen_lock_portrait"); + }); + }); + + it("displays trace collection config elements", async () => { + component.connect.isStartTraceState = jasmine.createSpy().and.returnValue(true); + const mock = {model: "Pixel 6", authorised:true}; + component.connect.devices = jasmine.createSpy().and.returnValue({"35562": mock}); + component.connect.selectedDevice = jasmine.createSpy().and.returnValue(mock); + fixture.detectChanges(); + + fixture.whenStable().then( () => { + const el = htmlElement.querySelector("trace-collection-config"); + expect(el).toBeTruthy(); + expect(el?.innerHTML).toContain("smartphone"); + expect(el?.innerHTML).toContain("Pixel 6"); + expect(el?.innerHTML).toContain("35562"); + + const traceSection = htmlElement.querySelector("trace-section"); + expect(traceSection).toBeTruthy(); + + const dumpSection = htmlElement.querySelector("dump-section"); + expect(dumpSection).toBeTruthy(); + }); + }); + + it("start trace button works as expected", async () => { + component.connect.isStartTraceState = jasmine.createSpy().and.returnValue(true); + const mock = {model: "Pixel 6", authorised:true}; + component.connect.devices = jasmine.createSpy().and.returnValue({"35562": mock}); + component.connect.selectedDevice = jasmine.createSpy().and.returnValue(mock); + fixture.detectChanges(); + + fixture.whenStable().then( () => { + const start: HTMLButtonElement | null = htmlElement.querySelector(".start-btn"); + expect(start).toBeInstanceOf(HTMLButtonElement); + start?.dispatchEvent(new Event("click")); + fixture.whenStable().then(() => { + expect(component.startTracing).toHaveBeenCalled(); + expect(component.connect.startTrace).toHaveBeenCalled(); + }); + }); + }); + + it("dump state button works as expected", async () => { + component.connect.isStartTraceState = jasmine.createSpy().and.returnValue(true); + const mock = {model: "Pixel 6", authorised:true}; + component.connect.devices = jasmine.createSpy().and.returnValue({"35562": mock}); + component.connect.selectedDevice = jasmine.createSpy().and.returnValue(mock); + fixture.detectChanges(); + + fixture.whenStable().then( () => { + const dump: HTMLButtonElement | null = htmlElement.querySelector(".dump-btn"); + expect(dump).toBeInstanceOf(HTMLButtonElement); + dump?.dispatchEvent(new Event("click")); + fixture.whenStable().then(() => { + expect(component.dumpState).toHaveBeenCalled(); + expect(component.connect.dumpState).toHaveBeenCalled(); + }); + }); + }); + + it("change device button works as expected", async () => { + component.connect.isStartTraceState = jasmine.createSpy().and.returnValue(true); + const mock = {model: "Pixel 6", authorised:true}; + component.connect.devices = jasmine.createSpy().and.returnValue({"35562": mock}); + component.connect.selectedDevice = jasmine.createSpy().and.returnValue(mock); + fixture.detectChanges(); + + fixture.whenStable().then( () => { + const change: HTMLButtonElement | null = htmlElement.querySelector(".change-btn"); + expect(change).toBeInstanceOf(HTMLButtonElement); + change?.dispatchEvent(new Event("click")); + fixture.whenStable().then(() => { + expect(component.connect.resetLastDevice).toHaveBeenCalled(); + }); + }); + }); + + it("displays unknown error message", () => { + component.connect.isErrorState = jasmine.createSpy().and.returnValue(true); + component.connect.proxy!.errorText = "bad things are happening"; + fixture.detectChanges(); + fixture.whenStable().then( () => { + const el = htmlElement.querySelector(".unknown-error"); + expect(el?.innerHTML).toContain("Error:"); + expect(el?.innerHTML).toContain("bad things are happening"); + const retry: HTMLButtonElement | null = htmlElement.querySelector(".retry-btn"); + expect(retry).toBeInstanceOf(HTMLButtonElement); + retry?.dispatchEvent(new Event("click")); + fixture.whenStable().then(() => { + expect(component.connect.restart).toHaveBeenCalled(); + }); + }); + }); + + it("displays end tracing elements", () => { + component.connect.isEndTraceState = jasmine.createSpy().and.returnValue(true); + fixture.detectChanges(); + fixture.whenStable().then( () => { + const el = htmlElement.querySelector(".end-tracing"); + expect(el?.innerHTML).toContain("Tracing..."); + expect(htmlElement.querySelector("mat-progress-bar")).toBeTruthy(); + + const end: HTMLButtonElement | null = htmlElement.querySelector(".end"); + expect(end).toBeInstanceOf(HTMLButtonElement); + end?.dispatchEvent(new Event("click")); + fixture.whenStable().then(() => { + expect(component.endTrace).toHaveBeenCalled(); + expect(component.connect.endTrace).toHaveBeenCalled(); + }); + }); + }); + + it("displays loading data elements", () => { + component.connect.isLoadDataState = jasmine.createSpy().and.returnValue(true); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(htmlElement.querySelector(".load-data")?.innerHTML).toContain("Loading data..."); + expect(htmlElement.querySelector("mat-progress-bar")).toBeTruthy(); + }); + }); +}); diff --git a/tools/winscope-ng/src/app/collect_traces.component.ts b/tools/winscope-ng/src/app/collect_traces.component.ts new file mode 100644 index 000000000..04fdca90d --- /dev/null +++ b/tools/winscope-ng/src/app/collect_traces.component.ts @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Component, Input, OnInit, Output, EventEmitter } from "@angular/core"; +import { ProxyConnection } from "trace_collection/proxy_connection"; +import { Connection } from "trace_collection/connection"; +import { setTraces } from "trace_collection/set_traces"; +import { ProxyState } from "../trace_collection/proxy_client"; +import { traceConfigurations, configMap, SelectionConfiguration, TraceConfigurationMap, EnableConfiguration } from "../trace_collection/trace_collection_utils"; +import { Core } from "app/core"; +import { PersistentStore } from "../common/persistent_store"; + + +@Component({ + selector: "collect-traces", + template: ` + Collect Traces + + +
Connecting...
+ +
+ + + + +
+ +
+
{{ objectKeys(connect.devices()).length > 0 ? "Connected devices:" : "No devices detected" }}
+ + + + {{ connect.devices()[deviceId].authorised ? "smartphone" : "screen_lock_portrait" }} + + + {{ connect.devices()[deviceId].authorised ? connect.devices()[deviceId].model : "unauthorised" }} ({{ deviceId }}) + + + +
+ +
+
+ + + smartphone + + {{ connect.selectedDevice().model }} ({{ connect.selectedDeviceId() }}) + + + +
+ +
+
+ + + +
+

Trace targets:

+ +
+ +
+

Dump targets:

+
+ {{setTraces.DUMPS[dumpKey].name}} +
+
+
+ +
+ error + Error: +
+            {{ connect.proxy?.errorText }}
+        
+ +
+ +
+ Tracing... + + +
+ +
+ Loading data... + +
+ +
+ `, + styles: [".device-choice {cursor: pointer}"] +}) +export class CollectTracesComponent implements OnInit { + objectKeys = Object.keys; + isAdbProxy = true; + traceConfigurations = traceConfigurations; + connect: Connection = new ProxyConnection(); + setTraces = setTraces; + + @Input() + store: PersistentStore = new PersistentStore(); + + @Input() + core: Core = new Core(); + + @Output() + coreChange = new EventEmitter(); + + @Input() + dataLoaded = false; + + @Output() + dataLoadedChange = new EventEmitter(); + + ngOnInit(): void { + if (this.isAdbProxy) { + this.connect = new ProxyConnection(); + } else { + //TODO: change to WebAdbConnection + this.connect = new ProxyConnection(); + } + } + + ngOnDestroy(): void { + this.connect.proxy?.removeOnProxyChange(this.onProxyChange); + } + + public onAddKey(key: string) { + this.store.addToStore("adb.proxyKey", key); + if (this.connect.setProxyKey) { + this.connect.setProxyKey(key); + } + this.connect.restart(); + } + + public onProxyChange(newState: ProxyState) { + this.connect.onConnectChange(newState); + } + + public displayAdbProxyTab() { + this.isAdbProxy = true; + this.connect = new ProxyConnection(); + } + + public displayWebAdbTab() { + this.isAdbProxy = false; + //TODO: change to WebAdbConnection + this.connect = new ProxyConnection(); + } + + public requestedTraces() { + const tracesFromCollection: Array = []; + const req = Object.keys(setTraces.DYNAMIC_TRACES) + .filter((traceKey:string) => { + const traceConfig = setTraces.DYNAMIC_TRACES[traceKey]; + if (traceConfig.isTraceCollection) { + traceConfig.config?.enableConfigs.forEach((innerTrace:EnableConfiguration) => { + if (innerTrace.enabled) { + tracesFromCollection.push(innerTrace.key); + } + }); + return false; + } + return traceConfig.run; + }); + return req.concat(tracesFromCollection); + } + + public requestedDumps() { + return Object.keys(setTraces.DUMPS) + .filter((dumpKey:string) => { + return setTraces.DUMPS[dumpKey].run; + }); + } + + public requestedEnableConfig(): Array | undefined { + const req: Array = []; + Object.keys(setTraces.DYNAMIC_TRACES) + .forEach((traceKey:string) => { + const trace = setTraces.DYNAMIC_TRACES[traceKey]; + if(!trace.isTraceCollection + && trace.run + && trace.config + && trace.config.enableConfigs) { + trace.config.enableConfigs.forEach((con:EnableConfiguration) => { + if (con.enabled) { + req.push(con.key); + } + }); + } + }); + if (req.length === 0) { + return undefined; + } + return req; + } + + public requestedSelection(traceType: string): configMap | undefined { + if (!setTraces.DYNAMIC_TRACES[traceType].run) { + return undefined; + } + const selected: configMap = {}; + setTraces.DYNAMIC_TRACES[traceType].config?.selectionConfigs.forEach( + (con: SelectionConfiguration) => { + selected[con.key] = con.value; + } + ); + return selected; + } + + public startTracing() { + console.log("begin tracing"); + setTraces.reqTraces = this.requestedTraces(); + const reqEnableConfig = this.requestedEnableConfig(); + const reqSelectedSfConfig = this.requestedSelection("layers_trace"); + const reqSelectedWmConfig = this.requestedSelection("window_trace"); + if (setTraces.reqTraces.length < 1) { + this.connect.throwNoTargetsError(); + return; + } + this.connect.startTrace( + reqEnableConfig, + reqSelectedSfConfig, + reqSelectedWmConfig + ); + } + + public async dumpState() { + console.log("begin dump"); + setTraces.reqDumps = this.requestedDumps(); + await this.connect.dumpState(); + while (!setTraces.dataReady && !setTraces.dumpError) { + await this.waitForData(1000); + } + if (!setTraces.dumpError) { + await this.loadFiles(); + } else { + this.core.clearData(); + } + } + + public async endTrace() { + console.log("end tracing"); + await this.connect.endTrace(); + while (!setTraces.dataReady) { + await this.waitForData(1000); + } + await this.loadFiles(); + } + + public async loadFiles() { + console.log("loading files", this.connect.adbData()); + await this.core.bootstrap(this.connect.adbData()); + this.dataLoaded = true; + this.dataLoadedChange.emit(this.dataLoaded); + this.coreChange.emit(this.core); + console.log("finished loading data!"); + } + + public tabClass(adbTab: boolean) { + let isActive: string; + if (adbTab) { + isActive = this.isAdbProxy ? "active" : "inactive"; + } else { + isActive = !this.isAdbProxy ? "active" : "inactive"; + } + return ["tab", isActive]; + } + + private waitForData(ms: number) { + return new Promise( resolve => setTimeout(resolve, ms) ); + } +} diff --git a/tools/winscope-ng/src/app/core.ts b/tools/winscope-ng/src/app/core.ts index d3a305573..57d476c61 100644 --- a/tools/winscope-ng/src/app/core.ts +++ b/tools/winscope-ng/src/app/core.ts @@ -16,6 +16,8 @@ import {TraceTypeId} from "common/trace/type_id"; import {Parser} from "parsers/parser"; import {ParserFactory} from "parsers/parser_factory"; +import { setTraces } from "trace_collection/set_traces"; +import { proxyClient } from "trace_collection/proxy_client"; import {Viewer} from "viewers/viewer"; import {ViewerFactory} from "viewers/viewer_factory"; @@ -29,6 +31,7 @@ class Core { } async bootstrap(traces: Blob[]) { + this.clearData(); this.parsers = await new ParserFactory().createParsers(traces); console.log("created parsers: ", this.parsers); @@ -71,6 +74,13 @@ class Core { viewer.notifyCurrentTraceEntries(traceEntries); }); } + + clearData() { + this.parsers = []; + this.viewers = []; + setTraces.dataReady = false; + proxyClient.adbData = []; + } } -export { Core }; +export { Core }; \ No newline at end of file diff --git a/tools/winscope-ng/src/app/trace_config.component.spec.ts b/tools/winscope-ng/src/app/trace_config.component.spec.ts new file mode 100644 index 000000000..8b2cec2b4 --- /dev/null +++ b/tools/winscope-ng/src/app/trace_config.component.spec.ts @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { CommonModule } from "@angular/common"; +import {ComponentFixture, TestBed} from "@angular/core/testing"; +import {TraceConfigComponent} from "./trace_config.component"; +import { MatCheckboxModule } from "@angular/material/checkbox"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; +import { MatInputModule } from "@angular/material/input"; +import { MatSelectModule } from "@angular/material/select"; +import { NO_ERRORS_SCHEMA } from "@angular/core"; + +describe("TraceConfigComponent", () => { + let fixture: ComponentFixture; + let component: TraceConfigComponent; + let htmlElement: HTMLElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + MatCheckboxModule, + MatFormFieldModule, + MatInputModule, + MatSelectModule, + BrowserAnimationsModule + ], + declarations: [TraceConfigComponent], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + fixture = TestBed.createComponent(TraceConfigComponent); + component = fixture.componentInstance; + htmlElement = fixture.nativeElement; + component.trace = { + name: "layers_trace", + run: false, + config: { + enableConfigs: [{ + name:"trace buffers", + key:"tracebuffers", + enabled:true + }], + selectionConfigs: [{ + key: "tracinglevel", + name: "tracing level", + options: [ + "verbose", + "debug", + "critical", + ], + value: "debug" + }] + } + }; + }); + + it("can be created", () => { + expect(component).toBeTruthy(); + }); + + it("check that trace checkbox ticked on default run", () => { + component.trace.run = true; + fixture.detectChanges(); + const box = htmlElement.querySelector(".trace-box"); + expect(box?.innerHTML).toContain("aria-checked=\"true\""); + expect(box?.innerHTML).toContain("layers_trace"); + }); + + it("check that trace checkbox not ticked on default run", () => { + component.trace.run = false; + fixture.detectChanges(); + const box = htmlElement.querySelector(".trace-box"); + expect(box?.innerHTML).toContain("aria-checked=\"false\""); + }); + + it("check that correct advanced enable config only shows", () => { + component.trace.config!.selectionConfigs = []; + fixture.detectChanges(); + const adv = htmlElement.querySelector(".adv-config"); + expect(adv).toBeTruthy(); + expect(adv?.innerHTML).toContain("trace buffers"); + expect(adv?.innerHTML).not.toContain("tracing level"); + }); + + it("check that correct advanced selection config shows", () => { + component.trace.config!.enableConfigs = []; + fixture.detectChanges(); + const adv = htmlElement.querySelector(".adv-config"); + expect(adv).toBeTruthy(); + expect(adv?.innerHTML).not.toContain("trace buffers"); + expect(adv?.innerHTML).toContain("tracing level"); + }); + + it("check that changing enable config causes box to change", async () => { spyOn(component, "changeTraceCollectionConfig"); + component.trace.config!.enableConfigs[0].enabled = false; + fixture.detectChanges(); + await fixture.whenStable(); + expect(htmlElement.querySelector(".enable-config")?.innerHTML).toContain("aria-checked=\"false\""); + }); + + it("check that changing selected config causes select to change", async () => { + fixture.detectChanges(); + expect(htmlElement.querySelector(".selection")?.innerHTML).toContain("value=\"debug\""); + component.trace.config!.selectionConfigs[0].value = "verbose"; + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(htmlElement.querySelector(".selection")?.innerHTML).toContain("value=\"verbose\""); + }); + }); +}); diff --git a/tools/winscope-ng/src/app/trace_config.component.ts b/tools/winscope-ng/src/app/trace_config.component.ts new file mode 100644 index 000000000..b329781d2 --- /dev/null +++ b/tools/winscope-ng/src/app/trace_config.component.ts @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Component, Input } from "@angular/core"; +import { EnableConfiguration, SelectionConfiguration, TraceConfiguration } from "../trace_collection/trace_collection_utils"; + +@Component({ + selector: "trace-config", + template: ` +
+
+ {{trace.name}} + +
+ {{enableConfig.name}} + +
+ {{selectionConfig.name}} + + {{ option }} + + +
+
+
+ `, + styles: [".adv-config {margin-left: 5rem;}"], +}) + +export class TraceConfigComponent { + @Input() + trace: TraceConfiguration = {}; + + public traceEnableConfigs(): Array { + if (this.trace.config) { + return this.trace.config.enableConfigs; + } else { + return []; + } + } + + public traceSelectionConfigs(): Array { + if (this.trace.config) { + return this.trace.config.selectionConfigs; + } else { + return []; + } + } + + public someTraces(): boolean { + return this.traceEnableConfigs().filter(trace => trace.enabled).length > 0 + && !this.trace.run; + } + + public changeRunTrace(run: boolean): void { + this.trace.run = run; + if (this.trace.isTraceCollection) { + this.traceEnableConfigs().forEach((c: EnableConfiguration) => (c.enabled = run)); + } + } + + public changeTraceCollectionConfig(): void { + if (this.trace.isTraceCollection) { + this.trace.run = this.traceEnableConfigs().every((c: EnableConfiguration) => c.enabled); + } + } +} diff --git a/tools/winscope-ng/src/app/upload_traces.component.spec.ts b/tools/winscope-ng/src/app/upload_traces.component.spec.ts new file mode 100644 index 000000000..d617dbd38 --- /dev/null +++ b/tools/winscope-ng/src/app/upload_traces.component.spec.ts @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {ComponentFixture, TestBed} from "@angular/core/testing"; +import {UploadTracesComponent} from "./upload_traces.component"; +import { MatCardModule } from "@angular/material/card"; + +describe("CollectTracesComponent", () => { + let fixture: ComponentFixture; + let component: UploadTracesComponent; + let htmlElement: HTMLElement; + + beforeAll(async () => { + await TestBed.configureTestingModule({ + imports: [MatCardModule], + declarations: [UploadTracesComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(UploadTracesComponent); + component = fixture.componentInstance; + htmlElement = fixture.nativeElement; + }); + + it("can be created", () => { + expect(component).toBeTruthy(); + }); + +}); diff --git a/tools/winscope-ng/src/app/upload_traces.component.ts b/tools/winscope-ng/src/app/upload_traces.component.ts new file mode 100644 index 000000000..4b3b5cd54 --- /dev/null +++ b/tools/winscope-ng/src/app/upload_traces.component.ts @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { Component, Input, Output, EventEmitter } from "@angular/core"; +import { Core } from "app/core"; + + +@Component({ + selector: "upload-traces", + template: ` + Upload Traces + +
+ +
+
+ `, + +}) +export class UploadTracesComponent { + @Input() + core: Core = new Core(); + + @Output() + coreChange = new EventEmitter(); + + public async onInputFile(event: Event) { + const files = this.getInputFiles(event); + await this.core.bootstrap(files); + + const viewersDiv = document.querySelector("div#viewers")!; + viewersDiv.innerHTML = ""; + this.core.getViews().forEach(view => viewersDiv!.appendChild(view) ); + this.coreChange.emit(this.core); + + const timestampsDiv = document.querySelector("div#timestamps")!; + timestampsDiv.innerHTML = `Retrieved ${this.core.getTimestamps().length} unique timestamps`; + } + + //TODO: extend with support for multiple files, archives, etc... + private getInputFiles(event: Event): File[] { + const files: any = (event?.target as HTMLInputElement)?.files; + + if (!files || !files[0]) { + return []; + } + + return [files[0]]; + } +} diff --git a/tools/winscope-ng/src/app/web_adb.component.spec.ts b/tools/winscope-ng/src/app/web_adb.component.spec.ts new file mode 100644 index 000000000..f5874e224 --- /dev/null +++ b/tools/winscope-ng/src/app/web_adb.component.spec.ts @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {ComponentFixture, TestBed} from "@angular/core/testing"; +import {WebAdbComponent} from "./web_adb.component"; +import { MatIconModule } from "@angular/material/icon"; +import { MatCardModule } from "@angular/material/card"; + +describe("WebAdbComponent", () => { + let fixture: ComponentFixture; + let component: WebAdbComponent; + let htmlElement: HTMLElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatIconModule, MatCardModule], + declarations: [WebAdbComponent], + }).compileComponents(); + fixture = TestBed.createComponent(WebAdbComponent); + component = fixture.componentInstance; + htmlElement = fixture.nativeElement; + }); + + it("can be created", () => { + expect(component).toBeTruthy(); + }); + + it("renders the info message", () => { + fixture.detectChanges(); + expect(htmlElement.querySelector(".adb-info")?.innerHTML).toBe("Add new device"); + expect(htmlElement.querySelector(".adb-icon")?.innerHTML).toBe("info"); + }); +}); diff --git a/tools/winscope-ng/src/app/web_adb.component.ts b/tools/winscope-ng/src/app/web_adb.component.ts new file mode 100644 index 000000000..08a6ca9d1 --- /dev/null +++ b/tools/winscope-ng/src/app/web_adb.component.ts @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {Component} from "@angular/core"; + +@Component({ + selector: "web-adb", + template: ` +
+ info + Add new device +
+
+

Click the button below to follow instructions in the Chrome pop-up.

+

Selecting a device will kill all existing ADB connections.

+
+
+ +
+ `, + styles: [".icon-message {vertical-align: middle;}"] +}) +export class WebAdbComponent { + adbDevice = null; +} diff --git a/tools/winscope-ng/src/common/persistent_store.ts b/tools/winscope-ng/src/common/persistent_store.ts new file mode 100644 index 000000000..62a510fc8 --- /dev/null +++ b/tools/winscope-ng/src/common/persistent_store.ts @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export class PersistentStore { + public addToStore(key: string, value: string) { + localStorage.setItem(key, value); + } + public getFromStore(key: string) { + return localStorage.getItem(key); + } +} \ No newline at end of file diff --git a/tools/winscope-ng/src/styles.css b/tools/winscope-ng/src/styles.css index 50e45553c..274d4f56c 100644 --- a/tools/winscope-ng/src/styles.css +++ b/tools/winscope-ng/src/styles.css @@ -13,6 +13,57 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@import "~@angular/material/prebuilt-themes/indigo-pink.css"; +@import 'https://fonts.googleapis.com/icon?family=Material+Icons'; + #title { - color: aqua; + font-weight: bold; + font-family: Arial, Helvetica, sans-serif; + color:rgb(194, 65, 108); + font-size: 20; } + +button { + cursor: pointer; +} + +.homepage-card { + border: 1px solid rgb(129, 129, 129); + width: 45rem; + height: 30rem; + overflow: auto; + display: flex; + margin: 10px; +} + +mat-checkbox { + margin-left: 10px; +} + +mat-form-field { + margin: 10px; + height: 5px; +} + +mat-icon { + margin: 5px; +} + +.icon-message, .adb-icon, .adb-info { + vertical-align: middle; +} + +.card-block { + margin: 15px; +} + +button.mat-raised-button { + background-color:rgb(194, 65, 108); + color: white; + margin: 10px; +} + +.tab.inactive { + background-color:white; + color: black; +} \ No newline at end of file diff --git a/tools/winscope-ng/src/trace_collection/connection.ts b/tools/winscope-ng/src/trace_collection/connection.ts new file mode 100644 index 000000000..80144a7b3 --- /dev/null +++ b/tools/winscope-ng/src/trace_collection/connection.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { ProxyClient } from "trace_collection/proxy_client"; +import { configMap } from "./trace_collection_utils"; + +export interface Device { + [key: string]: DeviceProperties +} + +export interface DeviceProperties { + authorised: boolean; + model: string; +} + +export interface Connection { + adbSuccess: () => boolean; + setProxyKey?(key:string): any; + devices(): Device; + selectedDevice(): DeviceProperties; + selectedDeviceId(): string; + restart(): any; + selectDevice(id:string): any; + state(): any; + onConnectChange(newState: any): any; + resetLastDevice(): any; + isDevicesState(): boolean; + isStartTraceState(): boolean; + isErrorState(): boolean; + isEndTraceState(): boolean; + isLoadDataState(): boolean; + isConnectingState(): boolean; + throwNoTargetsError(): any; + startTrace( + reqEnableConfig?: Array, + reqSelectedSfConfig?: configMap, + reqSelectedWmConfig?: configMap + ): any; + endTrace(): any; + adbData(): Array; + dumpState(): any; + proxy?: ProxyClient; + loadProgress: number; +} diff --git a/tools/winscope-ng/src/trace_collection/proxy_client.ts b/tools/winscope-ng/src/trace_collection/proxy_client.ts new file mode 100644 index 000000000..b10d65e7a --- /dev/null +++ b/tools/winscope-ng/src/trace_collection/proxy_client.ts @@ -0,0 +1,272 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { PersistentStore } from "../common/persistent_store"; +import { configMap, TRACES } from "./trace_collection_utils"; +import { setTraces, SetTraces } from "./set_traces"; +import { Device } from "./connection"; +import { ProxyConnection } from "./proxy_connection"; + +export enum ProxyState { + ERROR = 0, + CONNECTING = 1, + NO_PROXY = 2, + INVALID_VERSION = 3, + UNAUTH = 4, + DEVICES = 5, + START_TRACE = 6, + END_TRACE = 7, + LOAD_DATA = 8, +} + +export enum ProxyEndpoint { + DEVICES = "/devices/", + START_TRACE = "/start/", + END_TRACE = "/end/", + ENABLE_CONFIG_TRACE = "/configtrace/", + SELECTED_WM_CONFIG_TRACE = "/selectedwmconfigtrace/", + SELECTED_SF_CONFIG_TRACE = "/selectedsfconfigtrace/", + DUMP = "/dump/", + FETCH = "/fetch/", + STATUS = "/status/", + CHECK_WAYLAND = "/checkwayland/", +} + +// from here, all requests to the proxy are made +class ProxyRequest { + async call( + method: string, + path: string, + view: any, + onSuccess: any, + type?: XMLHttpRequestResponseType, + jsonRequest: any = null + ) { + const request = new XMLHttpRequest(); + const client = proxyClient; + request.onreadystatechange = function() { + if (this.readyState !== 4) { + return; + } + if (this.status === 0) { + client.setState(ProxyState.NO_PROXY); + } else if (this.status === 200) { + if (this.getResponseHeader("Winscope-Proxy-Version") !== client.VERSION) { + client.setState(ProxyState.INVALID_VERSION); + } else if (onSuccess) { + onSuccess(this, view); + } + } else if (this.status === 403) { + client.setState(ProxyState.UNAUTH); + } else { + if (this.responseType === "text" || !this.responseType) { + client.errorText = this.responseText; + } else if (this.responseType === "arraybuffer") { + client.errorText = String.fromCharCode.apply(null, new Array(this.response)); + } + client.setState(ProxyState.ERROR, client.errorText); + } + }; + request.responseType = type || ""; + request.open(method, client.WINSCOPE_PROXY_URL + path); + const lastKey = client.store.getFromStore("adb.proxyKey"); + if (lastKey !== null) { + client.proxyKey = lastKey; + } + request.setRequestHeader("Winscope-Token", client.proxyKey); + if (jsonRequest) { + const json = JSON.stringify(jsonRequest); + request.setRequestHeader("Content-Type", "application/json;charset=UTF-8"); + request.send(json); + } else { + request.send(); + } + } + + getDevices(view:any) { + proxyRequest.call("GET", ProxyEndpoint.DEVICES, view, proxyRequest.onSuccessGetDevices); + } + + async fetchFiles(dev:string, files: Array, idx: number, view:any) { + await proxyRequest.call("GET", `${ProxyEndpoint.FETCH}${dev}/${files[idx]}/`, view, + proxyRequest.onSuccessUpdateAdbData, "arraybuffer"); + } + + setEnabledConfig(view:any, req: Array) { + proxyRequest.call("POST", `${ProxyEndpoint.ENABLE_CONFIG_TRACE}${view.proxy.selectedDevice}/`, view, null, undefined, req); + } + + setSelectedConfig(endpoint: ProxyEndpoint, view:any, req: configMap) { + proxyRequest.call("POST", `${endpoint}${view.proxy.selectedDevice}/`, view, null, undefined, req); + } + + startTrace(view:any) { + proxyRequest.call("POST", `${ProxyEndpoint.START_TRACE}${view.proxy.selectedDevice}/`, view, function(request:XMLHttpRequest, newView:ProxyConnection) { + newView.keepAliveTrace(newView); + }, undefined, setTraces.reqTraces); + } + + async endTrace(view:any) { + await proxyRequest.call("POST", `${ProxyEndpoint.END_TRACE}${view.proxy.selectedDevice}/`, view, + async function (request:XMLHttpRequest, newView:ProxyConnection) { + await proxyClient.updateAdbData(setTraces.reqTraces, 0, "trace", newView); + }); + } + + keepTraceAlive(view:any) { + this.call("GET", `${ProxyEndpoint.STATUS}${view.proxy.selectedDevice}/`, view, function(request:XMLHttpRequest, newView:ProxyConnection) { + if (request.responseText !== "True") { + newView.endTrace(); + } else if (newView.keep_alive_worker === null) { + newView.keep_alive_worker = setInterval(newView.keepAliveTrace, 1000, newView); + } + }); + } + + async dumpState(view:any) { + await proxyRequest.call("POST", `${ProxyEndpoint.DUMP}${view.proxy.selectedDevice}/`, view, + async function(request:XMLHttpRequest, newView:ProxyConnection) { + await proxyClient.updateAdbData(setTraces.reqDumps, 0, "dump", newView); + }, undefined, setTraces.reqDumps); + } + + onSuccessGetDevices = function(request: XMLHttpRequest, view: ProxyClient) { + const client = proxyClient; + try { + client.devices = JSON.parse(request.responseText); + const last = client.store.getFromStore("adb.lastDevice"); + if (last && client.devices[last] && + client.devices[last].authorised) { + client.selectDevice(last); + } else { + if (client.refresh_worker === null) { + client.refresh_worker = setInterval(client.getDevices, 1000); + } + client.setState(ProxyState.DEVICES); + } + } catch (err) { + console.error(err); + client.errorText = request.responseText; + client.setState(ProxyState.ERROR, client.errorText); + } + }; + + onSuccessSetAvailableTraces = function(request:XMLHttpRequest, view:SetTraces) { + try { + view.DYNAMIC_TRACES = TRACES["default"]; + if(request.responseText == "true") { + view.appendOptionalTraces(view, "arc"); + } + } catch(err) { + proxyClient.setState(ProxyState.ERROR, request.responseText); + } + }; + + onSuccessUpdateAdbData = async (request: XMLHttpRequest, view: ProxyConnection) => { + const idx = proxyClient.adbParams.idx; + const files = proxyClient.adbParams.files; + const traceType = proxyClient.adbParams.traceType; + try { + const enc = new TextDecoder("utf-8"); + const resp = enc.decode(request.response); + const filesByType = JSON.parse(resp); + + for (const filetype in filesByType) { + const files = filesByType[filetype]; + for (const encodedFileBuffer of files) { + const buffer = Uint8Array.from(atob(encodedFileBuffer), (c) => c.charCodeAt(0)); + const blob = new Blob([buffer]); + proxyClient.adbData.push(blob); + } + } + if (idx < files.length - 1) { + proxyClient.updateAdbData(files, idx + 1, traceType, view); + } else { + setTraces.dataReady = true; + } + } catch (error) { + proxyClient.setState(ProxyState.ERROR, request.responseText); + } + }; +} +export const proxyRequest = new ProxyRequest(); + +interface AdbParams { + files: Array, + idx: number, + traceType: string +} + +// stores all the changing variables from proxy and sets up calls from ProxyRequest +export class ProxyClient { + readonly WINSCOPE_PROXY_URL = "http://localhost:5544"; + readonly VERSION = "0.8"; + state: ProxyState = ProxyState.CONNECTING; + stateChangeListeners: {(param:ProxyState, errorText:string): void;}[] = []; + refresh_worker: NodeJS.Timer | null = null; + devices: Device = {}; + selectedDevice = ""; + errorText = ""; + adbData: Array = []; + proxyKey = ""; + lastDevice = ""; + store = new PersistentStore(); + adbParams: AdbParams = { + files: [], + idx: -1, + traceType: "", + }; + + setState(state:ProxyState, errorText = "") { + this.state = state; + this.errorText = errorText; + for (const listener of this.stateChangeListeners) { + listener(state, errorText); + } + } + + onProxyChange(fn: (state:ProxyState, errorText:string) => void) { + this.removeOnProxyChange(fn); + this.stateChangeListeners.push(fn); + } + + removeOnProxyChange(removeFn: (state:ProxyState, errorText:string) => void) { + this.stateChangeListeners = this.stateChangeListeners.filter(fn => fn !== removeFn); + } + + getDevices() { + if (this.state !== ProxyState.DEVICES && this.state !== ProxyState.CONNECTING) { + clearInterval(this.refresh_worker!); + this.refresh_worker = null; + return; + } + proxyRequest.getDevices(this); + } + + selectDevice(device_id: string) { + this.selectedDevice = device_id; + this.store.addToStore("adb.lastDevice", device_id); + this.setState(ProxyState.START_TRACE); + } + + async updateAdbData(files:Array, idx:number, traceType:string, view: ProxyConnection) { + this.adbParams.files = files; + this.adbParams.idx = idx; + this.adbParams.traceType = traceType; + await proxyRequest.fetchFiles(this.selectedDevice, files, idx, view); + } +} + +export const proxyClient = new ProxyClient(); diff --git a/tools/winscope-ng/src/trace_collection/proxy_connection.ts b/tools/winscope-ng/src/trace_collection/proxy_connection.ts new file mode 100644 index 000000000..1efb02be2 --- /dev/null +++ b/tools/winscope-ng/src/trace_collection/proxy_connection.ts @@ -0,0 +1,166 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + proxyRequest, + proxyClient, + ProxyState, + ProxyEndpoint +} from "trace_collection/proxy_client"; +import { setTraces } from "./set_traces"; +import { Connection, DeviceProperties } from "./connection"; +import { configMap } from "./trace_collection_utils"; + +export class ProxyConnection implements Connection { + proxy = proxyClient; + keep_alive_worker: any = null; + notConnected = [ + ProxyState.NO_PROXY, + ProxyState.UNAUTH, + ProxyState.INVALID_VERSION, + ]; + loadProgress = 0; + + constructor() { + this.proxy.setState(ProxyState.CONNECTING); + this.proxy.onProxyChange(this.onConnectChange); + const urlParams = new URLSearchParams(window.location.search); + if (urlParams.has("token")) { + this.proxy.proxyKey = urlParams.get("token")!; + } + this.proxy.getDevices(); + } + + public devices() { + return this.proxy.devices; + } + + public adbData() { + return this.proxy.adbData; + } + + public state() { + return this.proxy.state; + } + + public isDevicesState() { + return this.state() === ProxyState.DEVICES; + } + + public isStartTraceState() { + return this.state() === ProxyState.START_TRACE; + } + + public isErrorState() { + return this.state() === ProxyState.ERROR; + } + + public isEndTraceState() { + return this.state() === ProxyState.END_TRACE; + } + + public isLoadDataState() { + return this.state() === ProxyState.LOAD_DATA; + } + + public isConnectingState() { + return this.state() === ProxyState.CONNECTING; + } + + public throwNoTargetsError() { + this.proxy.setState(ProxyState.ERROR, "No targets selected"); + } + + public setProxyKey(key: string) { + this.proxy.proxyKey = key; + this.restart(); + } + + public adbSuccess() { + return !this.notConnected.includes(this.proxy.state); + } + + public selectedDevice(): DeviceProperties { + return this.proxy.devices[this.proxy.selectedDevice]; + } + + public selectedDeviceId(): string { + return this.proxy.selectedDevice; + } + + public restart() { + this.proxy.setState(ProxyState.CONNECTING); + } + + public resetLastDevice() { + this.proxy.store.addToStore("adb.lastDevice", ""); + this.restart(); + } + + public selectDevice(id: string) { + this.proxy.selectDevice(id); + } + + public keepAliveTrace(view:ProxyConnection) { + if (!view.isEndTraceState()) { + clearInterval(view.keep_alive_worker); + view.keep_alive_worker = null; + return; + } + proxyRequest.keepTraceAlive(view); + } + + public startTrace( + reqEnableConfig?: Array, + reqSelectedSfConfig?: configMap, + reqSelectedWmConfig?: configMap + ) { + if (reqEnableConfig) { + proxyRequest.setEnabledConfig(this, reqEnableConfig); + } + if (reqSelectedSfConfig) { + proxyRequest.setSelectedConfig(ProxyEndpoint.SELECTED_SF_CONFIG_TRACE, this, reqSelectedSfConfig); + } + if (reqSelectedWmConfig) { + proxyRequest.setSelectedConfig(ProxyEndpoint.SELECTED_WM_CONFIG_TRACE, this, reqSelectedWmConfig); + } + proxyClient.setState(ProxyState.END_TRACE); + proxyRequest.startTrace(this); + } + + public async endTrace() { + this.proxy.setState(ProxyState.LOAD_DATA); + await proxyRequest.endTrace(this); + } + + public async dumpState() { + if (setTraces.reqDumps.length < 1) { + this.proxy.setState(ProxyState.ERROR, "No targets selected"); + setTraces.dumpError = true; + return; + } + this.proxy.setState(ProxyState.LOAD_DATA); + await proxyRequest.dumpState(this); + } + + public onConnectChange(newState: ProxyState) { + if (newState === ProxyState.CONNECTING) { + proxyClient.getDevices(); + } + if (newState == ProxyState.START_TRACE) { + setTraces.setAvailableTraces(); + } + } +} diff --git a/tools/winscope-ng/src/trace_collection/set_traces.ts b/tools/winscope-ng/src/trace_collection/set_traces.ts new file mode 100644 index 000000000..283dbe333 --- /dev/null +++ b/tools/winscope-ng/src/trace_collection/set_traces.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { TraceConfigurationMap, TRACES } from "./trace_collection_utils"; +import { + proxyRequest, + ProxyEndpoint +} from "trace_collection/proxy_client"; + +export class SetTraces { + DYNAMIC_TRACES = TRACES["default"]; + reqTraces: string[] = []; + reqDumps: string[] = []; + dataReady = false; + dumpError = false; + + DUMPS: TraceConfigurationMap = { + "window_dump": { + name: "Window Manager", + run: true, + }, + "layers_dump": { + name: "Surface Flinger", + run: true, + } + }; + + setAvailableTraces() { + proxyRequest.call("GET", ProxyEndpoint.CHECK_WAYLAND, this, proxyRequest.onSuccessSetAvailableTraces); + } + appendOptionalTraces(view:any, device_key:string) { + for(const key in TRACES[device_key]) { + view.DYNAMIC_TRACES[key] = TRACES[device_key][key]; + } + } +} +export const setTraces = new SetTraces(); diff --git a/tools/winscope-ng/src/trace_collection/trace_collection_utils.ts b/tools/winscope-ng/src/trace_collection/trace_collection_utils.ts new file mode 100644 index 000000000..acc3e072e --- /dev/null +++ b/tools/winscope-ng/src/trace_collection/trace_collection_utils.ts @@ -0,0 +1,190 @@ +export interface TraceConfiguration { + name?: string, + run?: boolean, + isTraceCollection?: boolean, + config?: ConfigurationOptions +} + + +export interface TraceConfigurationMap { + [key: string]: TraceConfiguration +} + +interface ConfigurationOptions { + enableConfigs: Array, + selectionConfigs: Array +} + +export interface EnableConfiguration { + name: string, + key: string, + enabled: boolean, +} + +export interface SelectionConfiguration { + key: string, + name: string, + options: Array, + value: string +} + +export type configMap = { +[key: string]: Array | string; +} + +const wmTraceSelectionConfigs: Array = [ + { + key: "wmbuffersize", + name: "buffer size (KB)", + options: [ + "4000", + "8000", + "16000", + "32000", + ], + value: "4000" + }, + { + key: "tracingtype", + name: "tracing type", + options: [ + "frame", + "transaction", + ], + value: "frame" + }, + { + key: "tracinglevel", + name: "tracing level", + options: [ + "verbose", + "debug", + "critical", + ], + value: "verbose" + }, +]; + +const sfTraceEnableConfigs: Array = [ + { + name: "composition", + key: "composition", + enabled: true + }, + { + name: "metadata", + key: "metadata", + enabled: true + }, + { + name: "hwc", + key: "hwc", + enabled: true + }, + { + name: "trace buffers", + key: "tracebuffers", + enabled: true + } +]; + +const sfTraceSelectionConfigs: Array = [ + { + key: "sfbuffersize", + name: "buffer size (KB)", + options: ["4000","8000","16000","32000"], + value: "4000" + } +]; + +export const traceConfigurations: TraceConfigurationMap = { + "layers_trace": { + name: "Surface Flinger", + run: true, + config: { + enableConfigs: sfTraceEnableConfigs, + selectionConfigs: sfTraceSelectionConfigs, + } + }, + "window_trace": { + name: "Window Manager", + run: true, + config: { + enableConfigs: [], + selectionConfigs: wmTraceSelectionConfigs, + } + }, + "screen_recording": { + name: "Screen Recording", + run: true, + }, + "ime_tracing": { + name: "IME Tracing", + run: true, + isTraceCollection: true, + config: { + enableConfigs: [ + { + name: "Input Method Clients", + key: "ime_trace_clients", + enabled: true, + }, + { + name: "Input Method Service", + key: "ime_trace_service", + enabled: true, + }, + { + name: "Input Method Manager Service", + key: "ime_trace_managerservice", + enabled: true, + }, + ], + selectionConfigs: [] + } + }, + "ime_trace_clients": { + name: "Input Method Clients", + run: true, + }, + "ime_trace_service": { + name: "Input Method Service", + run: true, + }, + "ime_trace_managerservice": { + name: "Input Method Manager Service", + run: true, + }, + "accessibility_trace": { + name: "Accessibility", + run: false, + }, + "transactions": { + name: "Transaction", + run: false, + }, + "proto_log": { + name: "ProtoLog", + run: false, + }, + "wayland_trace": { + name: "Wayland", + run: false, + }, +}; + + +export const TRACES: { [key: string]: TraceConfigurationMap; } = { + "default": { + "window_trace": traceConfigurations["window_trace"], + "accessibility_trace": traceConfigurations["accessibility_trace"], + "layers_trace": traceConfigurations["layers_trace"], + "transactions": traceConfigurations["transactions"], + "proto_log": traceConfigurations["proto_log"], + "screen_recording": traceConfigurations["screen_recording"], + "ime_tracing": traceConfigurations["ime_tracing"], + }, + "arc": { + "wayland_trace": traceConfigurations["wayland_trace"], + }, +};