diff --git a/.gitignore b/.gitignore index 152b238714..1268ac40a4 100644 --- a/.gitignore +++ b/.gitignore @@ -285,5 +285,8 @@ temp-cache.json # Jest JUnit reporter junit.xml +# MSAL Native Auth config files (contains sensitive data) +samples/msal-browser-samples/NativeAuthSample/nativeAuthConfig.json + # ApiExtractor -temp/ \ No newline at end of file +temp/ diff --git a/.pipelines/3p-e2e.yml b/.pipelines/3p-e2e.yml index 2cc60c5c72..3fc729f662 100644 --- a/.pipelines/3p-e2e.yml +++ b/.pipelines/3p-e2e.yml @@ -78,6 +78,7 @@ extends: - "pop" - "customizable-e2e-test" - "ExpressSample" + - "NativeAuthSample" debug: ${{ parameters.debug }} npmInstallTimeout: ${{ parameters.npmInstallTimeout }} - ${{ if eq(parameters.runNodeTests, true) }}: diff --git a/package-lock.json b/package-lock.json index 0f5e8699ba..5efa2b0761 100644 --- a/package-lock.json +++ b/package-lock.json @@ -249,7 +249,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -343,7 +342,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -451,7 +449,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, - "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -652,6 +649,7 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -1328,7 +1326,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -1383,7 +1380,6 @@ "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.1.tgz", "integrity": "sha512-bnINi6nPXbP1XNRaranMFEBZWUfdW/AF16Ql5+ypRxfTvCRTTKrLsMIakyDcayUt2t/RZotmL4kgJwNH5xO+bg==", "dev": true, - "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -1478,7 +1474,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dev": true, - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^0.0.51", @@ -1527,7 +1522,6 @@ "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -1584,7 +1578,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -2154,7 +2147,6 @@ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-15.2.10.tgz", "integrity": "sha512-yxfN8qQpMaukRU5LjFkJBmy85rqrOp86tYVCsf+hmPEFRiXBMUj6xYLeCMcpk3Mt1JtnWGBR34ivGx+7bNeAow==", "dev": true, - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -2272,7 +2264,6 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-15.2.10.tgz", "integrity": "sha512-jdBn3fctkqoNrJn9VLsUHpcCEhCxWSczdsR+BBbD6T0oLl6vMrAVNjPwfBejnlgfWN1KoRU9kgOYsMxa5apIWQ==", "dev": true, - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -2309,7 +2300,6 @@ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-15.2.10.tgz", "integrity": "sha512-mCFIxrs60XicKfA2o42hA7LrQvhybi9BQveWuZn/2iIEOXx7R62Iemz8E21pLWftAZHGxEW3NECfBrY1d3gVmA==", "dev": true, - "peer": true, "dependencies": { "@babel/core": "7.19.3", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -2408,7 +2398,6 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-15.2.10.tgz", "integrity": "sha512-meGGidnitQJGDxYd9/LrqYiVlId+vGaLoiLgJdKBz+o2ZO6OmXQGuNw2VBqf17/Cc0/UjzrOY7+kILNFKkk/WQ==", "dev": true, - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -2443,7 +2432,6 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-15.2.10.tgz", "integrity": "sha512-9tbgVGSJqwfrOzT8aA/kWBLNhJSQ9gUg0CJxwFBSJm8VkBUJrszoBlDsnSvlxx8/W2ejNULKHFTXeUzq0O/+RQ==", "dev": true, - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -2502,6 +2490,7 @@ "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz", "integrity": "sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==", "dev": true, + "peer": true, "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", @@ -2519,6 +2508,7 @@ "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", "dev": true, + "peer": true, "engines": { "node": ">=10" } @@ -2527,13 +2517,15 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@apidevtools/swagger-parser": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.1.tgz", "integrity": "sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==", "dev": true, + "peer": true, "dependencies": { "@apidevtools/json-schema-ref-parser": "11.7.2", "@apidevtools/openapi-schemas": "^2.1.0", @@ -2569,6 +2561,7 @@ "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", "dev": true, + "peer": true, "peerDependencies": { "ajv": "^8.5.0" }, @@ -2582,7 +2575,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@assemblyscript/loader": { "version": "0.10.1", @@ -2606,6 +2600,7 @@ "resolved": "https://registry.npmjs.org/@azure/arm-appservice/-/arm-appservice-13.0.3.tgz", "integrity": "sha512-Vu011o3/bikQNwtjouwmUJud+Z6Brcjij2D0omPWClRGg8i5gBfOYSpDkFGkHbhGlaky4fgvfkxD0uHGq34uYA==", "dev": true, + "peer": true, "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", @@ -2624,6 +2619,7 @@ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", "dev": true, + "peer": true, "dependencies": { "tslib": "^2.2.0" }, @@ -2636,6 +2632,7 @@ "resolved": "https://registry.npmjs.org/@azure/arm-resources/-/arm-resources-5.0.1.tgz", "integrity": "sha512-JbZtIqfEulsIA0rC3zM7jfF4KkOnye9aKcaO/jJqxJRm/gM6lAjEv7sL4njW8D+35l50P1f+UuH5OqN+UKJqNA==", "dev": true, + "peer": true, "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", @@ -2654,6 +2651,7 @@ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", "dev": true, + "peer": true, "dependencies": { "tslib": "^2.2.0" }, @@ -2666,6 +2664,7 @@ "resolved": "https://registry.npmjs.org/@azure/arm-storage/-/arm-storage-17.2.1.tgz", "integrity": "sha512-J2jmTPv8ZraSHDTz9l2Bx8gNL3ktfDDWo2mxWfzarn64O9Fjhb+l85YWyubGy2xUdeGuZPKzvQLltGv8bSu8eQ==", "dev": true, + "peer": true, "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", @@ -2684,6 +2683,7 @@ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", "dev": true, + "peer": true, "dependencies": { "tslib": "^2.2.0" }, @@ -2696,6 +2696,7 @@ "resolved": "https://registry.npmjs.org/@azure/arm-subscriptions/-/arm-subscriptions-5.1.0.tgz", "integrity": "sha512-6BeOF2eQWNLq22ch7xP9RxYnPjtGev54OUCGggKOWoOvmesK7jUZbIyLk8JeXDT21PEl7iyYnxw78gxJ7zBxQw==", "dev": true, + "peer": true, "dependencies": { "@azure/abort-controller": "^1.0.0", "@azure/core-auth": "^1.3.0", @@ -2714,6 +2715,7 @@ "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.1.0.tgz", "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", "dev": true, + "peer": true, "dependencies": { "tslib": "^2.2.0" }, @@ -3079,7 +3081,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -4204,7 +4205,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.26.0.tgz", "integrity": "sha512-B+O2DnPc0iG+YXFqOxv2WNuNU97ToWjOomUQ78DouOENWUaM5sVrmet9mcomUGQFwpJd//gvUagXBSdzO1fRKg==", - "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.25.9" }, @@ -5863,7 +5863,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -5984,7 +5983,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -7725,7 +7723,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -7772,13 +7769,15 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/@exodus/schemasafe/-/schemasafe-1.3.0.tgz", "integrity": "sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@feathersjs/hooks": { "version": "0.6.5", "resolved": "https://registry.npmjs.org/@feathersjs/hooks/-/hooks-0.6.5.tgz", "integrity": "sha512-WtcEoG/imdHRvC3vofGi/OcgH+cjHHhO0AfEeTlsnrKLjVKKBXV6aoIrB2nHZPpE7iW5sA7AZMR6bPD8ytxN+w==", "dev": true, + "peer": true, "engines": { "node": ">= 10" } @@ -8292,6 +8291,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-1.5.2.tgz", "integrity": "sha512-CifrkgQjDkUkWexmgYYNyB5603HhTHI91vLFeQXh6qrTKiCMVASol01Rs1cv6LP/A2WccZSRlJKZhbaBIs/9ZA==", "dev": true, + "peer": true, "dependencies": { "@inquirer/core": "^6.0.0", "@inquirer/type": "^1.1.6", @@ -8308,6 +8308,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -8333,6 +8334,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -8342,6 +8344,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -8351,6 +8354,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -8360,6 +8364,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -8369,6 +8374,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -8381,6 +8387,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8395,6 +8402,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-2.0.17.tgz", "integrity": "sha512-EqzhGryzmGpy2aJf6LxJVhndxYmFs+m8cxXzf8nejb1DE3sabf6mUgBcp4J0jAUEiAcYzqmkqRr7LPFh/WdnXA==", "dev": true, + "peer": true, "dependencies": { "@inquirer/core": "^6.0.0", "@inquirer/type": "^1.1.6", @@ -8409,6 +8417,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -8434,6 +8443,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -8443,6 +8453,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -8452,6 +8463,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -8461,6 +8473,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -8470,6 +8483,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -8482,6 +8496,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8496,6 +8511,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-5.1.2.tgz", "integrity": "sha512-w3PMZH5rahrukn8/I7P9Ihil+twgLTUHDZtJlJyBbUKyPaOSSQjLZkb0PpncVhin1gCaMgOFXy6iNPgcZUoo2w==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -8521,6 +8537,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -8530,6 +8547,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -8539,6 +8557,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -8548,6 +8567,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -8557,6 +8577,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -8569,6 +8590,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8583,6 +8605,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-1.2.15.tgz", "integrity": "sha512-gQ77Ls09x5vKLVNMH9q/7xvYPT6sIs5f7URksw+a2iJZ0j48tVS6crLqm2ugG33tgXHIwiEqkytY60Zyh5GkJQ==", "dev": true, + "peer": true, "dependencies": { "@inquirer/core": "^6.0.0", "@inquirer/type": "^1.1.6", @@ -8598,6 +8621,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -8623,6 +8647,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -8632,6 +8657,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -8641,6 +8667,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -8650,6 +8677,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -8659,6 +8687,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -8671,6 +8700,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8685,6 +8715,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-1.1.16.tgz", "integrity": "sha512-TGLU9egcuo+s7PxphKUCnJnpCIVY32/EwPCLLuu+gTvYiD8hZgx8Z2niNQD36sa6xcfpdLY6xXDBiL/+g1r2XQ==", "dev": true, + "peer": true, "dependencies": { "@inquirer/core": "^6.0.0", "@inquirer/type": "^1.1.6", @@ -8700,6 +8731,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -8725,6 +8757,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -8734,6 +8767,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -8743,6 +8777,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -8752,6 +8787,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -8761,6 +8797,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -8773,6 +8810,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8843,6 +8881,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-1.2.16.tgz", "integrity": "sha512-Ou0LaSWvj1ni+egnyQ+NBtfM1885UwhRCMtsRt2bBO47DoC1dwtCa+ZUNgrxlnCHHF0IXsbQHYtIIjFGAavI4g==", "dev": true, + "peer": true, "dependencies": { "@inquirer/core": "^6.0.0", "@inquirer/type": "^1.1.6", @@ -8857,6 +8896,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -8882,6 +8922,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -8891,6 +8932,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -8900,6 +8942,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -8909,6 +8952,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -8918,6 +8962,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -8930,6 +8975,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -9060,6 +9106,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-1.1.16.tgz", "integrity": "sha512-aZYZVHLUXZ2gbBot+i+zOJrks1WaiI95lvZCn1sKfcw6MtSSlYC8uDX8sTzQvAsQ8epHoP84UNvAIT0KVGOGqw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/core": "^6.0.0", "@inquirer/type": "^1.1.6", @@ -9075,6 +9122,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -9100,6 +9148,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -9109,6 +9158,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -9118,6 +9168,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -9127,6 +9178,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -9136,6 +9188,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -9148,6 +9201,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -9162,6 +9216,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-3.3.2.tgz", "integrity": "sha512-k52mOMRvTUejrqyF1h8Z07chC+sbaoaUYzzr1KrJXyj7yaX7Nrh0a9vktv8TuocRwIJOQMaj5oZEmkspEcJFYQ==", "dev": true, + "peer": true, "dependencies": { "@inquirer/checkbox": "^1.5.2", "@inquirer/confirm": "^2.0.17", @@ -9182,6 +9237,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -9207,6 +9263,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -9216,6 +9273,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -9225,6 +9283,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -9234,6 +9293,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -9243,6 +9303,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -9255,6 +9316,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -9269,6 +9331,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-1.2.16.tgz", "integrity": "sha512-pZ6TRg2qMwZAOZAV6TvghCtkr53dGnK29GMNQ3vMZXSNguvGqtOVc4j/h1T8kqGJFagjyfBZhUPGwNS55O5qPQ==", "dev": true, + "peer": true, "dependencies": { "@inquirer/core": "^6.0.0", "@inquirer/type": "^1.1.6", @@ -9283,6 +9346,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -9308,6 +9372,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -9317,6 +9382,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -9326,6 +9392,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -9335,6 +9402,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -9344,6 +9412,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -9356,6 +9425,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -9488,6 +9558,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-1.3.3.tgz", "integrity": "sha512-RzlRISXWqIKEf83FDC9ZtJ3JvuK1l7aGpretf41BCWYrvla2wU8W8MTRNMiPrPJ+1SIqrRC1nZdZ60hD9hRXLg==", "dev": true, + "peer": true, "dependencies": { "@inquirer/core": "^6.0.0", "@inquirer/type": "^1.1.6", @@ -9504,6 +9575,7 @@ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-6.0.0.tgz", "integrity": "sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==", "dev": true, + "peer": true, "dependencies": { "@inquirer/type": "^1.1.6", "@types/mute-stream": "^0.0.4", @@ -9529,6 +9601,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, + "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -9538,6 +9611,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, + "peer": true, "engines": { "node": ">= 12" } @@ -9547,6 +9621,7 @@ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -9556,6 +9631,7 @@ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", "dev": true, + "peer": true, "engines": { "node": ">=0.12.0" } @@ -9565,6 +9641,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "peer": true, "engines": { "node": ">=14" }, @@ -9577,6 +9654,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, + "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -10367,7 +10445,8 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@jsonjoy.com/base64": { "version": "1.1.2", @@ -11423,6 +11502,7 @@ "resolved": "https://registry.npmjs.org/@microsoft/dev-tunnels-contracts/-/dev-tunnels-contracts-1.1.9.tgz", "integrity": "sha512-OayhehwI+CnO0Wr53e29ZJZWGsNA5yVG7r54qmZSLc5HxA5Cozk4hP7EbYDCXkxh4NbQoT1dhTzC8bkRo+wWXw==", "dev": true, + "peer": true, "dependencies": { "buffer": "^5.2.1", "debug": "^4.1.1", @@ -11434,6 +11514,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -11451,6 +11532,7 @@ "resolved": "https://registry.npmjs.org/@microsoft/dev-tunnels-management/-/dev-tunnels-management-1.1.9.tgz", "integrity": "sha512-wGuFEzvRiWZmDxQMGKEjOKhEIVnLiG6vRUuM9Hwqxpe/kbiyA2WiUyEVpniNPaaw8gDHTf9zJHnPNNj0JiL5mA==", "dev": true, + "peer": true, "dependencies": { "@microsoft/dev-tunnels-contracts": ">1.1.8", "axios": "^1.6.2", @@ -11464,6 +11546,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -11481,6 +11564,7 @@ "resolved": "https://registry.npmjs.org/@microsoft/m365-spec-parser/-/m365-spec-parser-0.2.5.tgz", "integrity": "sha512-nAEd69xK1ZmSDBhogQeGzjaHslo0v/zaTkbd7sbJq21xoW07P4KLu8gkp8qLuxjOGGlaBo5YWXF9vYpkqAyyFg==", "dev": true, + "peer": true, "dependencies": { "@apidevtools/swagger-parser": "^10.1.1", "@microsoft/teams-manifest": "0.1.8", @@ -11498,6 +11582,7 @@ "resolved": "https://registry.npmjs.org/@microsoft/teams-manifest/-/teams-manifest-0.1.8.tgz", "integrity": "sha512-uoXed7wyMIUGXVRQflqWLhD2hpCz+cYhQ06e2s5PW8HIw1AlDBPM3s4+Y+ArJInzMZs0SZ58B66sq0Iy46/WKQ==", "dev": true, + "peer": true, "dependencies": { "@types/fs-extra": "^11.0.1", "@types/node-fetch": "^2.6.9", @@ -11513,6 +11598,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "peer": true, "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -11528,6 +11614,7 @@ "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", "dev": true, + "peer": true, "dependencies": { "@types/jsonfile": "*", "@types/node": "*" @@ -11555,6 +11642,7 @@ "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", "dev": true, + "peer": true, "peerDependencies": { "ajv": "^8.5.0" }, @@ -11569,6 +11657,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "dev": true, + "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -11586,6 +11675,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dev": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -11599,7 +11689,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@microsoft/teams-manifest": { "version": "0.1.5", @@ -11631,7 +11722,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -11701,6 +11791,7 @@ "integrity": "sha512-AowuJwrrUxeF9Bq/frxuy9YZjK/ECk3pi0UBXl3CQLZ4XNWfgWatiFi/UWpyHDLccFs+0Za3nNYATFvgsxEFwQ==", "dev": true, "hasInstallScript": true, + "peer": true, "dependencies": { "@azure/arm-subscriptions": "^5.0.0", "@azure/core-auth": "^1.4.0", @@ -11743,6 +11834,7 @@ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", "dev": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -11752,6 +11844,7 @@ "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", "dev": true, + "peer": true, "dependencies": { "@azure/msal-common": "14.16.0", "jsonwebtoken": "^9.0.0", @@ -11766,6 +11859,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "peer": true, "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -11781,6 +11875,7 @@ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", "dev": true, + "peer": true, "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.0", @@ -11805,6 +11900,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, + "peer": true, "dependencies": { "tslib": "^1.9.0" }, @@ -11816,13 +11912,15 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@microsoft/teamsfx-api": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/@microsoft/teamsfx-api/-/teamsfx-api-0.23.1.tgz", "integrity": "sha512-XmXX2dccOEU3lbYgOlijfwxmkXp6nO88JWx9P1al/1aMgbIeup2Y2H37Vmz2VwfIQC/i75FbbbbwqYjG2skQjQ==", "dev": true, + "peer": true, "dependencies": { "@azure/core-auth": "^1.4.0", "@microsoft/teams-manifest": "0.1.5", @@ -11838,6 +11936,7 @@ "resolved": "https://registry.npmjs.org/@microsoft/teamsfx-core/-/teamsfx-core-2.0.9.tgz", "integrity": "sha512-6zA/vvpHViROP6eDbnjS8PtPyyB4eZGH/cgTiOHeiRHznT9Pkd3rqFvaIHPEDhv2g76llHEk2gTFSqL7QFovAQ==", "dev": true, + "peer": true, "dependencies": { "@apidevtools/swagger-parser": "^10.1.0", "@azure/arm-appservice": "^13.0.0", @@ -11897,6 +11996,7 @@ "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", "dev": true, + "peer": true, "engines": { "node": ">=0.8.0" } @@ -11906,6 +12006,7 @@ "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", "dev": true, + "peer": true, "dependencies": { "@azure/msal-common": "14.16.0", "jsonwebtoken": "^9.0.0", @@ -11920,6 +12021,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -11936,6 +12038,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, + "peer": true, "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", @@ -11952,6 +12055,7 @@ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11972,6 +12076,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -11983,13 +12088,15 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@microsoft/teamsfx-core/node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", "dev": true, + "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -12187,7 +12294,6 @@ "version": "5.16.14", "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.16.14.tgz", "integrity": "sha512-eSXQVCMKU2xc7EcTxe/X/rC9QsV2jUe8eLM3MUCPYbo6V52eCE436akRIvELq/AqZpxx2bwkq7HC0cRhLB+yaw==", - "peer": true, "dependencies": { "@babel/runtime": "^7.23.9", "@mui/core-downloads-tracker": "^5.16.14", @@ -13463,7 +13569,6 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", "dev": true, - "peer": true, "dependencies": { "@octokit/auth-token": "^2.4.4", "@octokit/graphql": "^4.5.8", @@ -14014,7 +14119,6 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -14025,6 +14129,7 @@ "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.10.13.tgz", "integrity": "sha512-a9Ruw3j3qlnB5a/zHRTkruppynxqaeE4H9WNj5eYGRWqw0ZauZ23f4W2ARf3hghF5doozyD+CRtt7XSYuYRI/Q==", "license": "Apache-2.0", + "peer": true, "dependencies": { "debug": "^4.4.3", "extract-zip": "^2.0.1", @@ -14046,6 +14151,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -14063,6 +14169,7 @@ "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", "license": "MIT", + "peer": true, "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" @@ -14077,6 +14184,7 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "license": "MIT", + "peer": true, "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -14109,7 +14217,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz", "integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==", - "peer": true, "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -14647,6 +14754,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -14667,7 +14775,8 @@ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/@rtsao/scc": { "version": "1.1.0", @@ -14708,7 +14817,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz", "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==", "dev": true, - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "json-schema-traverse": "^1.0.0", @@ -15477,7 +15585,8 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@trysound/sax": { "version": "0.2.0", @@ -15574,7 +15683,8 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -15681,7 +15791,6 @@ "version": "8.56.12", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -15940,7 +16049,6 @@ "version": "4.17.12", "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", - "peer": true, "dependencies": { "@types/lodash": "*" } @@ -15969,6 +16077,7 @@ "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", "dev": true, + "peer": true, "dependencies": { "@types/node": "*" } @@ -15977,7 +16086,6 @@ "version": "22.10.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", - "peer": true, "dependencies": { "undici-types": "~6.20.0" } @@ -16051,7 +16159,6 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -16060,7 +16167,6 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -16197,7 +16303,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/ws": { "version": "8.5.14", @@ -16225,6 +16332,7 @@ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "optional": true, + "peer": true, "dependencies": { "@types/node": "*" } @@ -17309,7 +17417,6 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -17491,7 +17598,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -17766,7 +17872,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/array-union": { "version": "2.1.0", @@ -17922,6 +18029,7 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, + "peer": true, "engines": { "node": "*" } @@ -17931,6 +18039,7 @@ "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.0.1" }, @@ -18006,6 +18115,7 @@ "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz", "integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==", "dev": true, + "peer": true, "dependencies": { "tslib": "^2.3.1" } @@ -18143,6 +18253,7 @@ "resolved": "https://registry.npmjs.org/axios-retry/-/axios-retry-3.9.1.tgz", "integrity": "sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w==", "dev": true, + "peer": true, "dependencies": { "@babel/runtime": "^7.15.4", "is-retry-allowed": "^2.2.0" @@ -18165,6 +18276,7 @@ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", "license": "Apache-2.0", + "peer": true, "peerDependencies": { "react-native-b4a": "*" }, @@ -18501,6 +18613,7 @@ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "license": "Apache-2.0", + "peer": true, "peerDependencies": { "bare-abort-controller": "*" }, @@ -18516,6 +18629,7 @@ "integrity": "sha512-GljgCjeupKZJNetTqxKaQArLK10vpmK28or0+RwWjEl5Rk+/xG3wkpmkv+WrcBm3q1BwHKlnhXzR8O37kcvkXQ==", "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", @@ -18541,6 +18655,7 @@ "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", "license": "Apache-2.0", "optional": true, + "peer": true, "engines": { "bare": ">=1.14.0" } @@ -18551,6 +18666,7 @@ "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "bare-os": "^3.0.1" } @@ -18561,6 +18677,7 @@ "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "streamx": "^2.21.0" }, @@ -18583,6 +18700,7 @@ "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "bare-path": "^3.0.0" } @@ -18654,6 +18772,7 @@ "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" } @@ -19039,7 +19158,6 @@ "url": "https://tidelift.com/funding/github/npm/browserslist" } ], - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001449", "electron-to-chromium": "^1.4.284", @@ -19303,7 +19421,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/callsites": { "version": "3.1.0", @@ -19382,6 +19501,7 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, + "peer": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -19400,6 +19520,7 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, + "peer": true, "engines": { "node": ">=4" } @@ -19449,6 +19570,7 @@ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", "dev": true, + "peer": true, "engines": { "node": "*" } @@ -19458,6 +19580,7 @@ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, + "peer": true, "dependencies": { "get-func-name": "^2.0.2" }, @@ -19529,6 +19652,7 @@ "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-10.5.1.tgz", "integrity": "sha512-rlj6OyhKhVTnk4aENcUme3Jl9h+cq4oXu4AzBcvr8RMmT6BR4a3zSNT9dbIfXr9/BS6ibzRyDhowuw4n2GgzsQ==", "license": "Apache-2.0", + "peer": true, "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" @@ -19618,6 +19742,7 @@ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, + "peer": true, "dependencies": { "string-width": "^4.2.0" }, @@ -19946,6 +20071,7 @@ "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", "dev": true, + "peer": true, "dependencies": { "array-timsort": "^1.0.3", "core-util-is": "^1.0.3", @@ -20581,6 +20707,7 @@ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "dev": true, + "peer": true, "engines": { "node": "*" } @@ -20597,7 +20724,8 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/cryptr/-/cryptr-6.3.0.tgz", "integrity": "sha512-TA4byAuorT8qooU9H8YJhBwnqD151i1rcauHfJ3Divg6HmukHB2AYMp0hmjv2873J2alr4t15QqC7zAnWFrtfQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/css-blank-pseudo": { "version": "3.0.3", @@ -21019,6 +21147,7 @@ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 14" } @@ -21182,6 +21311,7 @@ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, + "peer": true, "dependencies": { "type-detect": "^4.0.0" }, @@ -21313,6 +21443,7 @@ "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "license": "MIT", + "peer": true, "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -21403,6 +21534,7 @@ "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.6.1.tgz", "integrity": "sha512-CmnVc+Hek2egPx1PeTFVta2W78xy2K/9Rkf6cC4T59S50tVnzKj+tnx5mmx5lwvCkujZ4uRrpRSuV+IVs3f90Q==", "dev": true, + "peer": true, "dependencies": { "address": "^1.0.1", "debug": "4" @@ -21436,6 +21568,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -22514,7 +22647,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "peer": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -22807,7 +22939,8 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/esbuild": { "version": "0.17.8", @@ -22917,7 +23050,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "peer": true, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.3", @@ -23732,6 +23864,7 @@ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "bare-events": "^2.7.0" } @@ -23821,7 +23954,6 @@ "version": "4.21.2", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -23926,7 +24058,6 @@ "version": "1.18.2", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.2.tgz", "integrity": "sha512-SZjssGQC7TzTs9rpPDuUrR23GNZ9+2+IkA/+IJWmvQilTr5OSliEHGF+D9scbIpdC6yGtTI0/VhaHoVes2AN/A==", - "peer": true, "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.7", @@ -24085,7 +24216,8 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/fast-glob": { "version": "3.3.3", @@ -24127,7 +24259,8 @@ "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==", - "dev": true + "dev": true, + "peer": true }, "node_modules/fast-uri": { "version": "3.0.6", @@ -24419,6 +24552,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "common-path-prefix": "^3.0.0", "pkg-dir": "^8.0.0" @@ -24437,6 +24571,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "find-up-simple": "^1.0.0" }, @@ -24495,6 +24630,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=18" }, @@ -24926,6 +25062,7 @@ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, + "peer": true, "engines": { "node": "*" } @@ -25094,6 +25231,7 @@ "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", "license": "MIT", + "peer": true, "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", @@ -25108,6 +25246,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -25658,6 +25797,7 @@ "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", "dev": true, + "peer": true, "engines": { "node": ">=8" } @@ -26308,7 +26448,8 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/http2-client/-/http2-client-1.3.5.tgz", "integrity": "sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/http2-wrapper": { "version": "1.0.3", @@ -26856,7 +26997,8 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/is-bun-module": { "version": "1.3.0", @@ -27176,6 +27318,7 @@ "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz", "integrity": "sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==", "dev": true, + "peer": true, "engines": { "node": ">=10" }, @@ -27569,8 +27712,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.0.1.tgz", "integrity": "sha512-w+JDABxQCkxbGGxg+a2hUVZyqUS2JKngvIyLGu/xiw2ZwgsoSB0iiecLQsQORSeaKQ6iGrCyWG86RfNDuoA7Lg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/jasmine-spec-reporter": { "version": "5.0.2", @@ -27585,7 +27727,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -27907,7 +28048,6 @@ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", "dev": true, - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -29018,7 +29158,8 @@ "version": "3.7.7", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/js-tokens": { "version": "4.0.0", @@ -29305,6 +29446,7 @@ "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==", "dev": true, + "peer": true, "engines": { "node": "*" } @@ -29409,7 +29551,6 @@ "resolved": "https://registry.npmjs.org/karma/-/karma-6.3.20.tgz", "integrity": "sha512-HRNQhMuKOwKpjYlWiJP0DUrJOh+QjaI/DTaD8b9rEm4Il3tJ8MijutVZH4ts10LuUFst/CedwTS6vieCN8yTSw==", "dev": true, - "peer": true, "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -29749,6 +29890,7 @@ "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", "dev": true, + "peer": true, "dependencies": { "graceful-fs": "^4.1.9" } @@ -29799,7 +29941,6 @@ "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", "dev": true, - "peer": true, "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -30061,14 +30202,12 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "peer": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "peer": true + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" }, "node_modules/lodash-unified": { "version": "1.0.3", @@ -30288,6 +30427,7 @@ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, + "peer": true, "dependencies": { "get-func-name": "^2.0.1" } @@ -30353,6 +30493,7 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -30641,6 +30782,7 @@ "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "dev": true, + "peer": true, "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", @@ -31090,7 +31232,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/mkcert": { "version": "3.2.0", @@ -31329,6 +31472,7 @@ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", "dev": true, + "peer": true, "bin": { "mustache": "bin/mustache" } @@ -31449,6 +31593,7 @@ "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "license": "MIT", + "peer": true, "engines": { "node": ">= 0.4.0" } @@ -31457,7 +31602,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/neverthrow/-/neverthrow-3.2.0.tgz", "integrity": "sha512-AINA32QbYO83L+3CBI6I5lH4LpBSlLwWteJ+uI25s4AQy6g/xz3RZuedmuNo91lLw2rY+AbPEPQdxd7mg1rXoQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/next": { "version": "15.5.9", @@ -31547,7 +31693,6 @@ "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-15.2.2.tgz", "integrity": "sha512-+042GBD35ztxbHywGJloAiDM/s3Ja3TZtQh361TWqd/xza3K5DMUu6VRGLTgMwG7CW1YsqYHWgMZslP1c+ng7A==", "dev": true, - "peer": true, "dependencies": { "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.0", @@ -31847,6 +31992,7 @@ "resolved": "https://registry.npmjs.org/node-fetch-h2/-/node-fetch-h2-2.3.0.tgz", "integrity": "sha512-ofRW94Ab0T4AOh5Fk8t0h8OBWrmjb0SSB20xh1H8YnPV9EJ+f5AMoYSUQ2zgJ4Iq2HAK0I2l5/Nequ8YzFS3Hg==", "dev": true, + "peer": true, "dependencies": { "http2-client": "^1.2.5" }, @@ -32000,13 +32146,15 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/node-readfiles": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/node-readfiles/-/node-readfiles-0.2.0.tgz", "integrity": "sha512-SU00ZarexNlE4Rjdm83vglt5Y9yiQ+XI1XpflWlb7q7UTN1JUItm69xMeiQCTxtTfnzt+83T8Cx+vI2ED++VDA==", "dev": true, + "peer": true, "dependencies": { "es6-promise": "^3.2.1" } @@ -32603,6 +32751,7 @@ "resolved": "https://registry.npmjs.org/oas-kit-common/-/oas-kit-common-1.0.8.tgz", "integrity": "sha512-pJTS2+T0oGIwgjGpw7sIRU8RQMcUoKCDWFLdBqKB2BNmGpbBMH2sdqAaOXUg8OzonZHU0L7vfJu1mJFEiYDWOQ==", "dev": true, + "peer": true, "dependencies": { "fast-safe-stringify": "^2.0.7" } @@ -32612,6 +32761,7 @@ "resolved": "https://registry.npmjs.org/oas-linter/-/oas-linter-3.2.2.tgz", "integrity": "sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==", "dev": true, + "peer": true, "dependencies": { "@exodus/schemasafe": "^1.0.0-rc.2", "should": "^13.2.1", @@ -32626,6 +32776,7 @@ "resolved": "https://registry.npmjs.org/oas-resolver/-/oas-resolver-2.5.6.tgz", "integrity": "sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==", "dev": true, + "peer": true, "dependencies": { "node-fetch-h2": "^2.3.0", "oas-kit-common": "^1.0.8", @@ -32645,6 +32796,7 @@ "resolved": "https://registry.npmjs.org/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz", "integrity": "sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==", "dev": true, + "peer": true, "funding": { "url": "https://github.com/Mermade/oas-kit?sponsor=1" } @@ -32654,6 +32806,7 @@ "resolved": "https://registry.npmjs.org/oas-validator/-/oas-validator-5.0.8.tgz", "integrity": "sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==", "dev": true, + "peer": true, "dependencies": { "call-me-maybe": "^1.0.1", "oas-kit-common": "^1.0.8", @@ -33106,7 +33259,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.21.0.tgz", "integrity": "sha512-Wy+/sdEH9kI3w9civgACwabHbKl+qIOu0uFZ9IMKzX3Jpv9og0ZBJrZExGrPpFAY7rWsXuxs5e7CPPP17A4eYA==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.21.0", "@typescript-eslint/types": "8.21.0", @@ -33313,7 +33465,6 @@ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -33436,7 +33587,6 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", "dev": true, - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", @@ -33709,7 +33859,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", "dev": true, - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -33725,7 +33874,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -33782,6 +33930,7 @@ "resolved": "https://registry.npmjs.org/office-addin-manifest-converter/-/office-addin-manifest-converter-0.4.1.tgz", "integrity": "sha512-2eOdCCYJ5bhCe2p9KKETdg1UNshsKaT0lDU/jNopAg3t7zC1WxwvofTSO/+4Log5L4Re+wUdV8MqrQikZBa7+Q==", "dev": true, + "peer": true, "dependencies": { "@xmldom/xmldom": "^0.8.5", "commander": "^9.0.0", @@ -33796,6 +33945,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", "dev": true, + "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -33805,6 +33955,7 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz", "integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -33822,7 +33973,8 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/office-addin-manifest/node_modules/ansi-styles": { "version": "3.2.1", @@ -33971,6 +34123,7 @@ "resolved": "https://registry.npmjs.org/office-addin-project/-/office-addin-project-0.8.6.tgz", "integrity": "sha512-S93brHVDaMRZIIEibK12m6eTItoKqdy2Ep51qNhU7RaZTjH7n/C66AzdDai06UJb3I3AXfXHnKv/AJL2voGGWA==", "dev": true, + "peer": true, "dependencies": { "adm-zip": "0.5.12", "commander": "^6.2.1", @@ -33990,6 +34143,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, + "peer": true, "engines": { "node": ">= 6" } @@ -33999,6 +34153,7 @@ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, + "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -34013,6 +34168,7 @@ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz", "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==", "dev": true, + "peer": true, "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.0", @@ -34037,6 +34193,7 @@ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, + "peer": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -34046,6 +34203,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, + "peer": true, "dependencies": { "tslib": "^1.9.0" }, @@ -34057,13 +34215,15 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/office-addin-project/node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, + "peer": true, "engines": { "node": ">= 4.0.0" } @@ -34365,6 +34525,7 @@ "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "license": "MIT", + "peer": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", @@ -34384,6 +34545,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -34401,6 +34563,7 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -34414,6 +34577,7 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -34428,6 +34592,7 @@ "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "license": "MIT", + "peer": true, "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -34812,6 +34977,7 @@ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, + "peer": true, "engines": { "node": "*" } @@ -35077,7 +35243,6 @@ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", "license": "Apache-2.0", - "peer": true, "bin": { "playwright-core": "cli.js" }, @@ -35141,7 +35306,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -35982,7 +36146,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -36617,7 +36780,6 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", "dev": true, - "peer": true, "bin": { "prettier": "bin-prettier.js" }, @@ -36796,6 +36958,7 @@ "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "dev": true, + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", @@ -36825,6 +36988,7 @@ "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -36844,6 +37008,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -36861,6 +37026,7 @@ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -36874,6 +37040,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -36883,6 +37050,7 @@ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -36949,6 +37117,7 @@ "integrity": "sha512-pX05JV1mMP+1N0vP3I4DOVwjMdpihv2LxQTtSfw6CUm5F0ZFLUFE/LSZ4yUWHYaM3C11Hdu+sgn7uY7teq5MYw==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@puppeteer/browsers": "2.10.13", "chromium-bidi": "10.5.1", @@ -36969,6 +37138,7 @@ "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.29.1.tgz", "integrity": "sha512-ErJ9qKCK+bdLvBa7QVSQTBSPm8KZbl1yC/WvhrZ0ut27hDf2QBzjDsn1IukzE1i1KtZ7NYGETOV4W1beoo9izA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@puppeteer/browsers": "2.10.13", "chromium-bidi": "10.5.1", @@ -36987,6 +37157,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", + "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -37004,6 +37175,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, @@ -37024,6 +37196,7 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -37233,7 +37406,6 @@ "version": "19.2.1", "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz", "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -37442,7 +37614,6 @@ "version": "19.2.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz", "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -37469,7 +37640,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -38087,7 +38257,6 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -38284,7 +38453,6 @@ "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "peer": true, "dependencies": { "@jest/core": "^27.5.1", "import-local": "^3.0.2", @@ -39470,7 +39638,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -39577,7 +39744,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -40262,6 +40428,7 @@ "resolved": "https://registry.npmjs.org/reftools/-/reftools-1.1.9.tgz", "integrity": "sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==", "dev": true, + "peer": true, "funding": { "url": "https://github.com/Mermade/oas-kit?sponsor=1" } @@ -40417,6 +40584,7 @@ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", "dev": true, + "peer": true, "engines": { "node": ">=0.10" } @@ -40737,7 +40905,6 @@ "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.7" }, @@ -40822,7 +40989,6 @@ "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -41083,7 +41249,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -41493,6 +41658,7 @@ "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", "dev": true, + "peer": true, "dependencies": { "should-equal": "^2.0.0", "should-format": "^3.0.3", @@ -41506,6 +41672,7 @@ "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", "dev": true, + "peer": true, "dependencies": { "should-type": "^1.4.0" } @@ -41515,6 +41682,7 @@ "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", "dev": true, + "peer": true, "dependencies": { "should-type": "^1.3.0", "should-type-adaptors": "^1.0.1" @@ -41524,13 +41692,15 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/should-type-adaptors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", "dev": true, + "peer": true, "dependencies": { "should-type": "^1.3.0", "should-util": "^1.0.0" @@ -41540,7 +41710,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", - "dev": true + "dev": true, + "peer": true }, "node_modules/shx": { "version": "0.3.4", @@ -42520,6 +42691,7 @@ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", "license": "MIT", + "peer": true, "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", @@ -43244,6 +43416,7 @@ "resolved": "https://registry.npmjs.org/swagger2openapi/-/swagger2openapi-7.0.8.tgz", "integrity": "sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==", "dev": true, + "peer": true, "dependencies": { "call-me-maybe": "^1.0.1", "node-fetch": "^2.6.1", @@ -43351,7 +43524,6 @@ "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -43504,6 +43676,7 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "optional": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -43546,7 +43719,8 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "optional": true + "optional": true, + "peer": true }, "node_modules/tailwindcss/node_modules/yaml": { "version": "2.7.0", @@ -43902,6 +44076,7 @@ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "license": "Apache-2.0", + "peer": true, "dependencies": { "b4a": "^1.6.4" } @@ -44217,7 +44392,6 @@ "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "bs-logger": "^0.2.6", "fast-json-stable-stringify": "^2.1.0", @@ -44321,7 +44495,6 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", "devOptional": true, - "peer": true, "dependencies": { "arg": "^4.1.0", "diff": "^4.0.1", @@ -44375,8 +44548,7 @@ "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "peer": true + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -44579,7 +44751,6 @@ "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "peer": true, "engines": { "node": ">=10" }, @@ -44679,7 +44850,8 @@ "version": "2.12.0", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/typedarray-to-buffer": { "version": "3.1.5", @@ -44693,7 +44865,8 @@ "version": "0.10.0", "resolved": "https://registry.npmjs.org/typedi/-/typedi-0.10.0.tgz", "integrity": "sha512-v3UJF8xm68BBj6AF4oQML3ikrfK2c9EmZUyLOfShpJuItAqVBHWP/KtpGinkSsIiP6EZyyb6Z3NXyW9dgS9X1w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/typedoc": { "version": "0.24.8", @@ -44744,7 +44917,6 @@ "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -45369,6 +45541,7 @@ "integrity": "sha512-KxPOq3V2LmfQPP4eqf3Mq/zrT0Dqp2Vmx2Bn285LwVahLc+CsxOM0crBHczm8ijlcjZ0Q5Xd6LW3z3odTPnlrw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 0.10" } @@ -45391,7 +45564,6 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -45890,6 +46062,7 @@ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==", "dev": true, + "peer": true, "engines": { "node": ">=8.0.0 || >=10.0.0" } @@ -45910,7 +46083,6 @@ "version": "3.5.13", "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", - "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", @@ -46055,7 +46227,8 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.3.8.tgz", "integrity": "sha512-21Yi2GhGntMc671vNBCjiAeEVknXjVRoyu+k+9xOMShu+ZQfpGQwnBqbNz/Sv4GXZ6JmutlPAi2nIJcrymAWuQ==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/webidl-conversions": { "version": "7.0.0", @@ -46071,7 +46244,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -46155,7 +46327,6 @@ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", "dev": true, - "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^2.1.1", @@ -47006,7 +47177,6 @@ "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -47106,7 +47276,6 @@ "version": "2.79.2", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -47528,8 +47697,7 @@ "resolved": "https://registry.npmjs.org/xterm/-/xterm-4.19.0.tgz", "integrity": "sha512-c3Cp4eOVsYY5Q839dR5IejghRPpxciGmLWWaP9g+ppfMeBChMeLa1DCA+pmX/jyDZ+zxFOmlJL/82qVdayVoGQ==", "deprecated": "This package is now deprecated. Move to @xterm/xterm instead.", - "dev": true, - "peer": true + "dev": true }, "node_modules/xterm-addon-fit": { "version": "0.5.0", @@ -47800,6 +47968,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -48156,7 +48325,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.12.tgz", "integrity": "sha512-MD0ElviEfAJY8qMOd6/jjSSvtqER2RDAi0lxe6EtUacC1DHCYkaPrKW4vLqY+tmZBg1yf+6n+uS77pXcHHcA3w==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -48171,7 +48339,6 @@ "version": "16.2.14", "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.2.14.tgz", "integrity": "sha512-n6PrGdiVeSTEmM/HEiwIyg6YQUUymZrb5afaNLGFRM5YL0Y8OBqd+XhCjb0OfD/AfgCUtedVEPwNqrfW8KzgGw==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -48223,7 +48390,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.2.12.tgz", "integrity": "sha512-B+WY/cT2VgEaz9HfJitBmgdk4I333XG/ybC98CMC4Wz8E49T8yzivmmxXB3OD6qvjcOB6ftuicl6WBqLbZNg2w==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -48239,7 +48405,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.2.12.tgz", "integrity": "sha512-6SMXUgSVekGM7R6l1Z9rCtUGtlg58GFmgbpMCsGf+VXxP468Njw8rjT2YZkf5aEPxEuRpSHhDYjqz7n14cwCXQ==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -48260,7 +48425,6 @@ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-16.2.12.tgz", "integrity": "sha512-pWSrr152562ujh6lsFZR8NfNc5Ljj+zSTQO44DsuB0tZjwEpnRcjJEgzuhGXr+CoiBf+jTSPZKemtSktDk5aaA==", "dev": true, - "peer": true, "dependencies": { "@babel/core": "7.23.2", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -48375,7 +48539,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.12.tgz", "integrity": "sha512-GLLlDeke/NjroaLYOks0uyzFVo6HyLl7VOm0K1QpLXnYvW63W9Ql/T3yguRZa7tRkOAeFZ3jw+1wnBD4O8MoUA==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -48391,7 +48554,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-16.2.12.tgz", "integrity": "sha512-1Eao89hlBgLR3v8tU91vccn21BBKL06WWxl7zLpQmG6Hun+2jrThgOE4Pf3os4fkkbH4Apj0tWL2fNIWe/blbw==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -48473,7 +48635,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.12.tgz", "integrity": "sha512-NnH7ju1iirmVEsUq432DTm0nZBGQsBrU40M3ZeVHMQ2subnGiyUs3QyzDz8+VWLL/T5xTxWLt9BkDn65vgzlIQ==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -48530,7 +48691,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.5", @@ -48835,6 +48995,7 @@ "os": [ "aix" ], + "peer": true, "engines": { "node": ">=12" } @@ -49242,7 +49403,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.16.tgz", "integrity": "sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==", "dev": true, - "peer": true, "dependencies": { "undici-types": "~6.19.2" } @@ -49455,6 +49615,7 @@ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -49497,6 +49658,7 @@ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=16" } @@ -49813,6 +49975,7 @@ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "optional": true, + "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -49846,6 +50009,7 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -50066,6 +50230,7 @@ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "semver": "^6.0.0" }, @@ -50082,6 +50247,7 @@ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "optional": true, + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -50111,6 +50277,7 @@ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -50137,6 +50304,7 @@ "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "minipass": "^7.0.3" }, @@ -50150,6 +50318,7 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -50169,6 +50338,7 @@ "integrity": "sha512-VTJ7Qtge52+1subkhmF5nOqLNbVutA8/igJ0A5vH6Mgpb8Z/3HeZomtD1SHzZF5Dqp+p+QPHE548FWYu1MdMSQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.0", @@ -50228,6 +50398,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -50244,6 +50415,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -50260,6 +50432,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -50276,6 +50449,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -50292,6 +50466,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -50308,6 +50483,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -50324,6 +50500,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -50340,6 +50517,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50356,6 +50534,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50372,6 +50551,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50388,6 +50568,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50404,6 +50585,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50420,6 +50602,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50436,6 +50619,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50452,6 +50636,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50468,6 +50653,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -50484,6 +50670,7 @@ "os": [ "netbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -50500,6 +50687,7 @@ "os": [ "openbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -50516,6 +50704,7 @@ "os": [ "sunos" ], + "peer": true, "engines": { "node": ">=12" } @@ -50532,6 +50721,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -50548,6 +50738,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -50564,6 +50755,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -50574,6 +50766,7 @@ "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", @@ -50597,7 +50790,8 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "samples/msal-angular-samples/angular-b2c-sample/node_modules/ng-packagr/node_modules/esbuild": { "version": "0.19.12", @@ -50606,6 +50800,7 @@ "dev": true, "hasInstallScript": true, "optional": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -50644,6 +50839,7 @@ "integrity": "sha512-Zmc4hk6FibJZBcTx5/8K/4jT3/oG1vkGTEeKJUQFCUQKimD6Q7+adp/bdVQyYJFolMKaXkQnVZdV4O5ZaTYmyQ==", "dev": true, "optional": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -50657,6 +50853,7 @@ "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, "optional": true, + "peer": true, "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -50675,6 +50872,7 @@ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -50689,6 +50887,7 @@ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "optional": true, + "peer": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -50701,7 +50900,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "samples/msal-angular-samples/angular-b2c-sample/node_modules/ng-packagr/node_modules/minipass": { "version": "7.1.2", @@ -50709,6 +50909,7 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -50719,6 +50920,7 @@ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "optional": true, + "peer": true, "dependencies": { "p-try": "^2.0.0" }, @@ -50735,6 +50937,7 @@ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "optional": true, + "peer": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -50748,6 +50951,7 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -50758,6 +50962,7 @@ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "find-up": "^4.0.0" }, @@ -50909,7 +51114,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -51227,8 +51431,7 @@ "samples/msal-angular-samples/angular-b2c-sample/node_modules/tslib": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", - "peer": true + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" }, "samples/msal-angular-samples/angular-b2c-sample/node_modules/vite": { "version": "4.5.5", @@ -51236,7 +51439,6 @@ "integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", @@ -51313,7 +51515,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, - "peer": true, "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", @@ -51360,7 +51561,6 @@ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", "dev": true, - "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -51569,7 +51769,6 @@ "version": "0.13.3", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.3.tgz", "integrity": "sha512-MKPbmZie6fASC/ps4dkmIhaT5eonHkEt6eAy80K42tAm0G2W+AahLJjbfi6X9NPdciOE9GRFTTM8u2IiF6O3ww==", - "peer": true, "dependencies": { "tslib": "^2.3.0" } @@ -51883,7 +52082,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.2.12.tgz", "integrity": "sha512-MD0ElviEfAJY8qMOd6/jjSSvtqER2RDAi0lxe6EtUacC1DHCYkaPrKW4vLqY+tmZBg1yf+6n+uS77pXcHHcA3w==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -51950,7 +52148,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/common/-/common-16.2.12.tgz", "integrity": "sha512-B+WY/cT2VgEaz9HfJitBmgdk4I333XG/ybC98CMC4Wz8E49T8yzivmmxXB3OD6qvjcOB6ftuicl6WBqLbZNg2w==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -51966,7 +52163,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-16.2.12.tgz", "integrity": "sha512-6SMXUgSVekGM7R6l1Z9rCtUGtlg58GFmgbpMCsGf+VXxP468Njw8rjT2YZkf5aEPxEuRpSHhDYjqz7n14cwCXQ==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -51987,7 +52183,6 @@ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-16.2.12.tgz", "integrity": "sha512-pWSrr152562ujh6lsFZR8NfNc5Ljj+zSTQO44DsuB0tZjwEpnRcjJEgzuhGXr+CoiBf+jTSPZKemtSktDk5aaA==", "dev": true, - "peer": true, "dependencies": { "@babel/core": "7.23.2", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -52102,7 +52297,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/core/-/core-16.2.12.tgz", "integrity": "sha512-GLLlDeke/NjroaLYOks0uyzFVo6HyLl7VOm0K1QpLXnYvW63W9Ql/T3yguRZa7tRkOAeFZ3jw+1wnBD4O8MoUA==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -52118,7 +52312,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-16.2.12.tgz", "integrity": "sha512-1Eao89hlBgLR3v8tU91vccn21BBKL06WWxl7zLpQmG6Hun+2jrThgOE4Pf3os4fkkbH4Apj0tWL2fNIWe/blbw==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -52200,7 +52393,6 @@ "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.12.tgz", "integrity": "sha512-NnH7ju1iirmVEsUq432DTm0nZBGQsBrU40M3ZeVHMQ2subnGiyUs3QyzDz8+VWLL/T5xTxWLt9BkDn65vgzlIQ==", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -52257,7 +52449,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", "dev": true, - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.22.5", @@ -52562,6 +52753,7 @@ "os": [ "aix" ], + "peer": true, "engines": { "node": ">=12" } @@ -53172,6 +53364,7 @@ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -53225,6 +53418,7 @@ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=16" } @@ -53623,8 +53817,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.5.0.tgz", "integrity": "sha512-9PMzyvhtocxb3aXJVOPqBDswdgyAeSB81QnLop4npOpbqnheaTEwPc9ZloQeVswugPManznQBjD8kWDTjlnHuw==", - "dev": true, - "peer": true + "dev": true }, "samples/msal-angular-samples/angular-modules-sample/node_modules/jsdom": { "version": "16.7.0", @@ -53710,7 +53903,6 @@ "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", "dev": true, - "peer": true, "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -53749,7 +53941,6 @@ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", "dev": true, - "peer": true, "dependencies": { "jasmine-core": "^4.1.0" }, @@ -53843,6 +54034,7 @@ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "semver": "^6.0.0" }, @@ -53859,6 +54051,7 @@ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "optional": true, + "peer": true, "bin": { "semver": "bin/semver.js" } @@ -53898,6 +54091,7 @@ "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "minipass": "^7.0.3" }, @@ -53911,6 +54105,7 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -53942,6 +54137,7 @@ "integrity": "sha512-VTJ7Qtge52+1subkhmF5nOqLNbVutA8/igJ0A5vH6Mgpb8Z/3HeZomtD1SHzZF5Dqp+p+QPHE548FWYu1MdMSQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "@rollup/plugin-json": "^6.0.0", "@rollup/plugin-node-resolve": "^15.0.0", @@ -54001,6 +54197,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -54017,6 +54214,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -54033,6 +54231,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -54049,6 +54248,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -54065,6 +54265,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -54081,6 +54282,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -54097,6 +54299,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -54113,6 +54316,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54129,6 +54333,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54145,6 +54350,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54161,6 +54367,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54177,6 +54384,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54193,6 +54401,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54209,6 +54418,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54225,6 +54435,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54241,6 +54452,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -54257,6 +54469,7 @@ "os": [ "netbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -54273,6 +54486,7 @@ "os": [ "openbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -54289,6 +54503,7 @@ "os": [ "sunos" ], + "peer": true, "engines": { "node": ">=12" } @@ -54305,6 +54520,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -54321,6 +54537,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -54337,6 +54554,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -54347,6 +54565,7 @@ "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "@npmcli/fs": "^3.1.0", "fs-minipass": "^3.0.0", @@ -54370,7 +54589,8 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "samples/msal-angular-samples/angular-modules-sample/node_modules/ng-packagr/node_modules/esbuild": { "version": "0.19.12", @@ -54379,6 +54599,7 @@ "dev": true, "hasInstallScript": true, "optional": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -54417,6 +54638,7 @@ "integrity": "sha512-Zmc4hk6FibJZBcTx5/8K/4jT3/oG1vkGTEeKJUQFCUQKimD6Q7+adp/bdVQyYJFolMKaXkQnVZdV4O5ZaTYmyQ==", "dev": true, "optional": true, + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -54430,6 +54652,7 @@ "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", "dev": true, "optional": true, + "peer": true, "dependencies": { "commondir": "^1.0.1", "make-dir": "^3.0.2", @@ -54448,6 +54671,7 @@ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "optional": true, + "peer": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -54462,6 +54686,7 @@ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "dev": true, "optional": true, + "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -54483,6 +54708,7 @@ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "optional": true, + "peer": true, "dependencies": { "p-locate": "^4.1.0" }, @@ -54495,7 +54721,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "optional": true + "optional": true, + "peer": true }, "samples/msal-angular-samples/angular-modules-sample/node_modules/ng-packagr/node_modules/minimatch": { "version": "9.0.5", @@ -54503,6 +54730,7 @@ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "optional": true, + "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -54519,6 +54747,7 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -54529,6 +54758,7 @@ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "optional": true, + "peer": true, "dependencies": { "p-try": "^2.0.0" }, @@ -54545,6 +54775,7 @@ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "optional": true, + "peer": true, "dependencies": { "p-limit": "^2.2.0" }, @@ -54558,6 +54789,7 @@ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "optional": true, + "peer": true, "engines": { "node": ">=8" } @@ -54568,6 +54800,7 @@ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "optional": true, + "peer": true, "dependencies": { "find-up": "^4.0.0" }, @@ -54643,6 +54876,7 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", "optional": true, + "peer": true, "dependencies": { "entities": "^4.5.0" }, @@ -54719,7 +54953,6 @@ "url": "https://github.com/sponsors/ai" } ], - "peer": true, "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", @@ -55046,8 +55279,7 @@ "samples/msal-angular-samples/angular-modules-sample/node_modules/tslib": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", - "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", - "peer": true + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" }, "samples/msal-angular-samples/angular-modules-sample/node_modules/vite": { "version": "4.5.5", @@ -55055,7 +55287,6 @@ "integrity": "sha512-ifW3Lb2sMdX+WU91s3R0FyQlAyLxOzCSCP37ujw0+r5POeHPwe6udWVIElKQq8gk3t7b8rkmvqC6IHBpCff4GQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.18.10", "postcss": "^8.4.27", @@ -55132,7 +55363,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dev": true, - "peer": true, "dependencies": { "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", @@ -55179,7 +55409,6 @@ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", "dev": true, - "peer": true, "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -55397,7 +55626,6 @@ "version": "0.13.3", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.3.tgz", "integrity": "sha512-MKPbmZie6fASC/ps4dkmIhaT5eonHkEt6eAy80K42tAm0G2W+AahLJjbfi6X9NPdciOE9GRFTTM8u2IiF6O3ww==", - "peer": true, "dependencies": { "tslib": "^2.3.0" } @@ -55549,7 +55777,6 @@ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.0.4.tgz", "integrity": "sha512-s0kRcEply2A1ThvFmb0+o+hEpAbPn08lpK8xjWZryM4cMrwjgsUE0OZHZPBANP4I1xT7Z82l+fmQbH+vX48EyA==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -55755,7 +55982,6 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.0.4.tgz", "integrity": "sha512-fWgxe2rgSKgI36ummBYnBN4YUrmp4CHbfEG3RMeJho/vhHKguk2/o6BgL9zvnKybvbWmuaqbkHogi+y0LeJ8Ww==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -55772,7 +55998,6 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.0.4.tgz", "integrity": "sha512-1bP3P8Ll/KUYMPiE6TDjkMXkqCDVgSUAUsVCgzAxz4mcMuc9PnlbhQazpWHCkCDIjGFZ5XIAsS49V7tfaTbLDw==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -55786,7 +56011,6 @@ "integrity": "sha512-2FP1WMRexAMcDPNE3YO3zB++sCgND9O/qJC5rgKbAebpbmOrCDMUBRlftkwiLT+UhTM9PjhTtAGtK7C+2iwx1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "7.27.4", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -56022,7 +56246,6 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.0.4.tgz", "integrity": "sha512-JhSl3B6CrJ9kegLffgWVFGF4D4bWLV/9r8R0+h78vU+ppdPFPWDha7WnirF31cPIg3pBzy6wn103Kcy9Ri5M5w==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -56048,7 +56271,6 @@ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.0.4.tgz", "integrity": "sha512-bFTMgJSHiLr80ELymRykZW6o5QroDlk+g5AFFiY9yxM8I0DV5YpCNBefv8GiuWubE+Lw6LkQ/HMYeXYJMTue3A==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -56084,7 +56306,6 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.0.4.tgz", "integrity": "sha512-hMJYvtZlNPh4Tt6JrnK+vmBmHWok04EkuJwyPcPhlle1u6/LihuCj4suELLqCanX9EzyNgvyKnws0i6JE/qh8Q==", "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.3.0" }, @@ -57423,7 +57644,6 @@ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "peer": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -57577,6 +57797,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=20" } @@ -57606,6 +57827,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=4" } @@ -57855,7 +58077,6 @@ "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", "dev": true, - "peer": true, "dependencies": { "@colors/colors": "1.5.0", "body-parser": "^1.19.0", @@ -57953,6 +58174,7 @@ "dev": true, "license": "Apache-2.0", "optional": true, + "peer": true, "dependencies": { "copy-anything": "^2.0.1", "parse-node-version": "^1.0.1", @@ -57981,6 +58203,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "bin": { "mime": "cli.js" }, @@ -57995,6 +58218,7 @@ "dev": true, "license": "BSD-3-Clause", "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -58379,6 +58603,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@ampproject/remapping": "^2.3.0", "@rollup/plugin-json": "^6.1.0", @@ -58430,6 +58655,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -58447,6 +58673,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">= 14.18.0" }, @@ -58462,6 +58689,7 @@ "dev": true, "license": "LGPL-3.0-only", "optional": true, + "peer": true, "dependencies": { "magic-string": "^0.30.17" }, @@ -58911,7 +59139,6 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -59010,6 +59237,7 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -59029,7 +59257,8 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "samples/msal-angular-samples/angular-standalone-sample/node_modules/tmp": { "version": "0.2.5", @@ -59062,7 +59291,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -59113,7 +59341,6 @@ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -59232,8 +59459,7 @@ "samples/msal-angular-samples/angular-standalone-sample/node_modules/zone.js": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.0.tgz", - "integrity": "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==", - "peer": true + "integrity": "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==" }, "samples/msal-browser-samples/ExpressSample": { "name": "express-sample", @@ -60595,6 +60821,7 @@ "@azure/msal-browser": "^4.0.0", "@types/express": "^4.17.3", "@types/jest": "^29.5.0", + "e2e-test-utils": "file:../../e2eTestUtils", "jest": "^29.5.0", "jest-junit": "^16.0.0", "jose": "^2.0.7", @@ -61057,7 +61284,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", "dev": true, - "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -61351,7 +61577,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", "dev": true, - "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -61761,7 +61986,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", "dev": true, - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -61831,7 +62055,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.74.tgz", "integrity": "sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==", "dev": true, - "peer": true, "dependencies": { "undici-types": "~5.26.4" } diff --git a/samples/msal-browser-samples/NativeAuthSample/jest.config.cjs b/samples/msal-browser-samples/NativeAuthSample/jest.config.cjs index 73910c5cc6..33f9f1f3ba 100644 --- a/samples/msal-browser-samples/NativeAuthSample/jest.config.cjs +++ b/samples/msal-browser-samples/NativeAuthSample/jest.config.cjs @@ -1,12 +1,8 @@ module.exports = { - displayName: "Native Auth Sample App", + displayName: "Native Auth Sample App E2E tests", globals: { __PORT__: 30670, __STARTCMD__: "npm start -- --port 30670", }, - testMatch: ["/test/**/*.spec.ts"], - preset: "../../../../e2eTestUtils/jest-puppeteer-utils/jest-preset.js", - transform: { - "^.+\\.ts?$": "ts-jest", - }, + preset: "../../e2eTestUtils/jest-puppeteer-utils/jest-preset.js", }; diff --git a/samples/msal-browser-samples/NativeAuthSample/package.json b/samples/msal-browser-samples/NativeAuthSample/package.json index 086b24c944..e7b806f303 100644 --- a/samples/msal-browser-samples/NativeAuthSample/package.json +++ b/samples/msal-browser-samples/NativeAuthSample/package.json @@ -18,10 +18,16 @@ "@azure/msal-browser": "^4.0.0", "@types/express": "^4.17.3", "@types/jest": "^29.5.0", + "e2e-test-utils": "file:../../e2eTestUtils", "jest": "^29.5.0", "jest-junit": "^16.0.0", "jose": "^2.0.7", "ts-jest": "^29.1.0", "typescript": "^4.9.5" + }, + "jest-junit": { + "suiteNameTemplate": "Native Auth Sample Tests", + "outputDirectory": ".", + "outputName": "test-results.xml" } } diff --git a/samples/msal-browser-samples/NativeAuthSample/test/configUtils.ts b/samples/msal-browser-samples/NativeAuthSample/test/configUtils.ts new file mode 100644 index 0000000000..3a89ebd545 --- /dev/null +++ b/samples/msal-browser-samples/NativeAuthSample/test/configUtils.ts @@ -0,0 +1,175 @@ +/** + * Test configuration for Native Auth Sample E2E tests + * Contains tenant information, proxy settings, timeouts, and test user data + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +/** + * Default test configuration + * Update these values based on your test environment + */ +export const testConfig = { + tenant: { + name: "MSIDLABCIAM6", + id: "fe362aec-5d43-45d1-b730-9755e60dc3b9", + labKeyVaultName: "MSIDLABCIAM6" + }, + + proxy: { + port: 30001, + enabled: true + }, + + timeouts: { + standard: 45000, // 45 seconds + auth: 60000, // 60 seconds + test: 120000 // 120 seconds + }, + + screenshots: { + enabled: true, + baseFolderName: "./screenshots" + } +}; + + +/** + * Utility functions + */ +export const getTenantInfo = () => ({ + name: testConfig.tenant.name, + id: testConfig.tenant.id +}); + +export const getProxyPort = () => testConfig.proxy.port; + +export const getLabKeyVaultName = () => testConfig.tenant.labKeyVaultName; + +/** + * Constants for nativeAuthConfig.json keys + * These represent the existing keys in the configuration file + */ +export const NATIVE_AUTH_CONFIG_KEYS = { + // Root level + NATIVE_AUTH: 'native_auth', + + // Native auth configuration keys + EMAIL_PASSWORD_CLIENT_ID: 'native_auth.email_password_client_id', + EMAIL_CODE_CLIENT_ID: 'native_auth.email_code_client_id', + EMAIL_PASSWORD_ATTRIBUTES_CLIENT_ID: 'native_auth.email_password_attributes_client_id', + EMAIL_CODE_ATTRIBUTES_CLIENT_ID: 'native_auth.email_code_attributes_client_id', + TENANT_SUBDOMAIN: 'native_auth.tenant_subdomain', + TENANT_ID: 'native_auth.tenant_id', + SIGN_IN_EMAIL_PASSWORD_USERNAME: 'native_auth.sign_in_email_password_username', + SIGN_IN_EMAIL_CODE_USERNAME: 'native_auth.sign_in_email_code_username', + RESET_PASSWORD_USERNAME: 'native_auth.reset_password_username', + PASSWORD_SIGN_IN_EMAIL_CODE: 'native_auth.password_sign_in_email_code', + PASSWORD_PROVIDER: 'native_auth.password_provider', + KEYVAULT_URL: 'native_auth.keyvault_url' +} as const; + +/** + * Utility function to parse nativeAuthConfig.json and read values based on keys + */ +export class NativeAuthConfigParser { + private static configCache: any = null; + private static readonly CONFIG_PATH = path.join(__dirname, '..', 'nativeAuthConfig.json'); + + /** + * Load and parse the nativeAuthConfig.json file + * @returns Parsed configuration object + */ + private static loadConfig(): any { + if (!this.configCache) { + try { + const configData = fs.readFileSync(this.CONFIG_PATH, 'utf8'); + this.configCache = JSON.parse(configData); + } catch (error) { + throw new Error(`Failed to load native auth config from ${this.CONFIG_PATH}: ${error}`); + } + } + return this.configCache; + } + + /** + * Get a value from the native auth configuration using dot notation + * @param key - The key to retrieve (supports dot notation like 'native_auth.email_password_client_id') + * @returns The value associated with the key + */ + static getValue(key: string): any { + const config = this.loadConfig(); + const keys = key.split('.'); + let value: any = config; + + for (const k of keys) { + if (value && typeof value === 'object' && k in value) { + value = value[k]; + } else { + throw new Error(`Key '${key}' not found in native auth configuration`); + } + } + + return value; + } + +} + +/** + * Convenience function to get a value from native auth config + * @param key - The key to retrieve (supports dot notation) + * @returns The value associated with the key + */ +export const getNativeAuthConfigValue = (key: string): any => { + return NativeAuthConfigParser.getValue(key); +}; + +/** + * Parsed native auth configuration values + * These are the actual values from nativeAuthConfig.json + */ +export const nativeAuthConfig = { + emailPasswordClientId: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.EMAIL_PASSWORD_CLIENT_ID), + emailCodeClientId: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.EMAIL_CODE_CLIENT_ID), + emailPasswordAttributesClientId: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.EMAIL_PASSWORD_ATTRIBUTES_CLIENT_ID), + emailCodeAttributesClientId: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.EMAIL_CODE_ATTRIBUTES_CLIENT_ID), + tenantSubdomain: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.TENANT_SUBDOMAIN), + tenantId: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.TENANT_ID), + signInEmailPasswordUsername: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.SIGN_IN_EMAIL_PASSWORD_USERNAME), + signInEmailCodeUsername: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.SIGN_IN_EMAIL_CODE_USERNAME), + resetPasswordUsername: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.RESET_PASSWORD_USERNAME), + passwordSignInEmailCode: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.PASSWORD_SIGN_IN_EMAIL_CODE), + passwordProvider: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.PASSWORD_PROVIDER), + keyvaultUrl: getNativeAuthConfigValue(NATIVE_AUTH_CONFIG_KEYS.KEYVAULT_URL) +}; + +/** + * Test data for negative test cases and other test-specific scenarios + * These should not be replaced with real configuration values + */ +export const testData = { + // Negative test case emails + invalidUserEmail: "test123@test", + nonRegisteredEmail: "non-registered@test.com", + incorrectPassword: "incorrect-password", + invalidPassword: "invalid-password!", + invalidOtpCode: "12345678" +}; + + +/** + * Get test users object with real account information + * @returns Object containing test user accounts + */ +export const getTestUsers = () => ({ + signInEmailPassword: nativeAuthConfig.signInEmailPasswordUsername, + signInEmailCode: nativeAuthConfig.signInEmailCodeUsername, + resetPassword: nativeAuthConfig.resetPasswordUsername, +}); + +/** + * Get test data for negative test cases + * @returns Object containing test data for negative scenarios + */ +export const getTestData = () => testData; diff --git a/samples/msal-browser-samples/NativeAuthSample/test/emailProviderUtils.ts b/samples/msal-browser-samples/NativeAuthSample/test/emailProviderUtils.ts new file mode 100644 index 0000000000..1c0de1cd1d --- /dev/null +++ b/samples/msal-browser-samples/NativeAuthSample/test/emailProviderUtils.ts @@ -0,0 +1,256 @@ +/** + * Email provider utility functions for mail.tm + */ +const BASE = "https://api.mail.tm"; + +/** + * MailTmClient class to interact with mail.tm API + */ +export class MailTmClient { + address: string | null = null; + password: string | null = null; + token: string | null = null; + domain: string | null = null; + + constructor(password?: string) { + if (password) { + this.password = password; + } else { + this.password = null; + } + } + + /** + * Fetch available domain from Mail.tm API + */ + async fetchDomain(): Promise { + try { + const response = await fetch(`${BASE}/domains`, { + headers: { "Content-Type": "application/json" } + }); + + if (response.ok) { + const data = await response.json(); + if (data["hydra:member"] && data["hydra:member"].length > 0) { + const domain = data["hydra:member"][0]["domain"]; + this.domain = domain; + console.log(`Fetched domain: ${domain}`); + return domain; + } else { + throw new Error("No domains available in response"); + } + } else { + throw new Error(`Error fetching domains: ${response.status} - ${response.statusText}`); + } + } catch (error) { + console.error("Error during domain fetch:", error); + throw new Error(`Failed to fetch domain: ${error}`); + } + } + + /** + * Create a new Mail.tm account with retry logic + */ + async createInbox(address?: string, password?: string, maxRetries = 5, delaySeconds = 5): Promise<{ address: string; password: string }> { + const usePassword = password || this.password; + if (!usePassword) { + throw new Error("No password found in configuration."); + } + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + let useAddress = address; + if (!useAddress) { + // Fetch domain if not already available + if (!this.domain) { + await this.fetchDomain(); + } + + const currentTime = Date.now(); + useAddress = `test${currentTime}@${this.domain}`; + } + + this.address = useAddress; + this.password = usePassword; + + console.log(`Creating account (attempt ${attempt}/${maxRetries})! Email: ${this.address}`); + + const res = await fetch(`${BASE}/accounts`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + address: this.address, + password: this.password + }) + }); + + if (res.status === 201) { + console.log(`Account successfully created! Email: ${this.address}`); + return { address: this.address, password: this.password }; + } else { + const errorText = await res.text(); + throw new Error(`Failed to create account: ${res.status} - ${errorText}`); + } + } catch (error) { + console.error(`Error during account creation on attempt ${attempt}`); + + // If this is the last attempt, throw the error + if (attempt === maxRetries) { + throw new Error(`Failed to create mail.tm inbox after ${maxRetries} attempts: ${error}`); + } + + // Wait before retrying + console.log(`Waiting ${delaySeconds} seconds before retry...`); + await new Promise((resolve) => setTimeout(resolve, delaySeconds * 1000)); + } + } + + throw new Error(`Failed to create mail.tm inbox after ${maxRetries} attempts`); + } + + /** + * Login to Mail.tm and get authentication token + */ + async login(address: string, password: string): Promise { + try { + const res = await fetch(`${BASE}/token`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + address: address, + password: password + }) + }); + + if (res.ok) { + const json = await res.json(); + const token = json.token; + this.token = token; + this.address = address; + this.password = password; + console.log("Authentication token received."); + return token; + } else { + const errorText = await res.text(); + throw new Error(`Failed to get token: ${res.status} - ${errorText}`); + } + } catch (error) { + console.error("Error during login:", error); + throw new Error(`Failed to login to mail.tm: ${error}`); + } + } + + /** + * Get message source and extract OTP code + */ + private async getMessageSource(messageId: string): Promise { + if (!this.token) { + throw new Error("Authentication token required"); + } + + try { + const sourceUrl = `${BASE}/sources/${messageId}`; + const response = await fetch(sourceUrl, { + headers: { + "Authorization": `Bearer ${this.token}`, + "Content-Type": "application/json" + } + }); + + if (response.ok) { + const data = await response.json(); + + if (data.data) { + const messageData = data.data; + + // Use regex to find the verification code (e.g., "Account verification code: 17354003") + const match = messageData.match(/Account verification code:\s*(\d+)/); + if (match) { + const otpCode = match[1]; + return otpCode; + } else { + console.log("OTP code not found in message data."); + return null; + } + } else { + console.log("'data' field not found in message source response."); + return null; + } + } else { + const errorText = await response.text(); + console.error(`Failed to fetch message source. Status: ${response.status}, Response: ${errorText}`); + return null; + } + } catch (error) { + console.error("Error while fetching message source:", error); + return null; + } + } + + /** + * Read messages and extract OTP code + */ + async readOtpCode(maxRetries = 3, delaySeconds = 10): Promise { + if (!this.token) { + throw new Error("Call login() before reading messages"); + } + + console.log("Checking email for messages (this may take a moment)..."); + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + console.log(`Attempt ${attempt}/${maxRetries} to fetch messages...`); + + const res = await fetch(`${BASE}/messages?page=1`, { + headers: { + "Authorization": `Bearer ${this.token}`, + "Content-Type": "application/json" + } + }); + + if (res.ok) { + const json = await res.json(); + const msgs = json["hydra:member"]; + + if (msgs && msgs.length > 0) { + // Log email details + const email = msgs[0]; + console.log(`Email Subject: ${email.subject}`); + console.log(`Email Intro: ${email.intro}`); + console.log(`Message ID: ${email.id}`); + console.log(`Updated At: ${email.updatedAt}`); + + // Get OTP code from message source + const otpCode = await this.getMessageSource(email.id); + if (otpCode) { + console.log(`OTP Code Found: ${otpCode}`); + return otpCode; + } else { + console.log(`No OTP code found in message ${email.id} on attempt ${attempt}`); + } + } else { + console.log(`No messages found on attempt ${attempt}`); + } + } else { + console.error(`Failed to fetch emails on attempt ${attempt}. Status: ${res.status}`); + } + + // If not the last attempt, wait before retrying + if (attempt < maxRetries) { + console.log(`Waiting ${delaySeconds} seconds before next attempt...`); + await new Promise((resolve) => setTimeout(resolve, delaySeconds * 1000)); + } + } catch (error) { + console.error(`Error on attempt ${attempt}:`, error); + + // If not the last attempt, wait before retrying + if (attempt < maxRetries) { + console.log(`Waiting ${delaySeconds} seconds before next attempt...`); + await new Promise((resolve) => setTimeout(resolve, delaySeconds * 1000)); + } + } + } + + throw new Error(`Failed to find OTP code after ${maxRetries} attempts`); + } +} diff --git a/samples/msal-browser-samples/NativeAuthSample/test/proxyUtils.ts b/samples/msal-browser-samples/NativeAuthSample/test/proxyUtils.ts new file mode 100644 index 0000000000..2185ea78a5 --- /dev/null +++ b/samples/msal-browser-samples/NativeAuthSample/test/proxyUtils.ts @@ -0,0 +1,75 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import { spawn, ChildProcess } from "child_process"; +import path from "path"; + +/** + * Starts a CORS proxy server for testing purposes + * + * @param {string} domain - The domain for the CORS proxy (e.g., "MSIDLABCIAM6") + * @param {string} tenantId - The tenant ID for the CORS proxy + * @param {number} port - The port to run the CORS proxy on + * @returns {Promise} A Promise that resolves with the proxy server process + */ +export function startCorsProxy( + domain: string = "MSIDLABCIAM6", + tenantId: string = "fe362aec-5d43-45d1-b730-9755e60dc3b9", + port: number = 30001 +): Promise { + return new Promise((resolve, reject) => { + try { + // Start the CORS proxy server + const corsProcess = spawn( + "node", + [ + path.join(__dirname, "../cors.js"), + "-d", + domain, + "-t", + tenantId, + "-p", + port.toString(), + ], + { + stdio: "inherit", + cwd: path.join(__dirname, ".."), + } + ); + + // Set up error handling + corsProcess.on('error', (err) => { + console.error('Failed to start CORS proxy:', err); + reject(err); + }); + + // Wait a bit to ensure the proxy is up before resolving the promise + setTimeout(() => { + console.log(`CORS proxy started on port ${port} for domain ${domain}`); + resolve(corsProcess); + }, 2000); + + } catch (error) { + console.error('Error starting CORS proxy:', error); + reject(error); + } + }); +} + +/** + * Stops the CORS proxy server + * + * @param {ChildProcess | null} corsProcess - The CORS proxy process to stop + */ +export function stopCorsProxy(corsProcess: ChildProcess | null): void { + if (corsProcess) { + try { + corsProcess.kill(); + console.log('CORS proxy stopped'); + } catch (error) { + console.error('Error stopping CORS proxy:', error); + } + } +} diff --git a/samples/msal-browser-samples/NativeAuthSample/test/resetpassword.spec.ts b/samples/msal-browser-samples/NativeAuthSample/test/resetpassword.spec.ts new file mode 100644 index 0000000000..fe6f876225 --- /dev/null +++ b/samples/msal-browser-samples/NativeAuthSample/test/resetpassword.spec.ts @@ -0,0 +1,772 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import * as puppeteer from "puppeteer"; +import { + Screenshot, + createFolder, + getBrowser, + pcaInitializedPoller, + BrowserCacheUtils, + getHomeUrl, +} from "e2e-test-utils"; +import { ChildProcess } from "child_process"; +import path = require("path"); +import { startCorsProxy, stopCorsProxy } from "./proxyUtils"; +import { MailTmClient } from "./emailProviderUtils"; + +import { + testConfig, + getTenantInfo, + getProxyPort, + nativeAuthConfig, + testData, +} from "./configUtils"; + +// Use configuration instead of hardcoded values +const SCREENSHOT_BASE_FOLDER_NAME = path.join( + __dirname, + testConfig.screenshots.baseFolderName, + "/resetpassword" +); +const AUTH_TIMEOUT = testConfig.timeouts.auth; +let sampleHomeUrl = ""; + +describe("Native Auth Sample - Reset Password Tests", () => { + let context: puppeteer.BrowserContext; + let page: puppeteer.Page; + let BrowserCache: BrowserCacheUtils; + let browser: puppeteer.Browser; + let resetPasswordEmailWithOtp: string = ""; + let resetPasswordUsername: string = ""; + let corsProcess: ChildProcess; + let resetPasswordClient: MailTmClient; + + beforeAll(async () => { + // Start the CORS proxy server using configuration values + const tenantInfo = getTenantInfo(); + corsProcess = await startCorsProxy( + tenantInfo.name, + tenantInfo.id, + getProxyPort() + ); + + if (testConfig.screenshots.enabled) { + createFolder(SCREENSHOT_BASE_FOLDER_NAME); + } + browser = await getBrowser(); + sampleHomeUrl = getHomeUrl(); + + // Use configuration for test user emails from JSON config + resetPasswordUsername = nativeAuthConfig.resetPasswordUsername; + resetPasswordEmailWithOtp = nativeAuthConfig.signInEmailCodeUsername; + + // Initialize email client for reset password account + resetPasswordClient = new MailTmClient(); + await resetPasswordClient.login( + resetPasswordUsername, + nativeAuthConfig.passwordProvider + ); + }); + + afterAll(async () => { + await context?.close(); + await browser?.close(); + // Stop the CORS proxy server using the utility function + stopCorsProxy(corsProcess); + }); + + beforeEach(async () => { + context = await browser.createBrowserContext(); + page = await context.newPage(); + + BrowserCache = new BrowserCacheUtils( + page, + "sessionStorage" // Based on Native Auth Sample configuration + ); // Navigate to the Native Auth Sample home page and wait for network idle to ensure full page load + }); + + afterEach(async () => { + // Clear storage after each test + await page.evaluate(() => { + window.sessionStorage.clear(); + window.localStorage.clear(); + }); + await page.close(); + }); + + describe("Reset Password Flow - Email + Password - Positive", () => { + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?usePwdConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + // Verify reset password button is visible on the navigation bar + const showResetPasswordButton = await page.$( + "#showResetPasswordBtn" + ); + expect(showResetPasswordButton).toBeTruthy(); + + // Click reset password button on the navigation bar + await page.click("#showResetPasswordBtn"); + + // Verify reset password card is visible + const resetPasswordCard = await page.$("#resetPasswordCard"); + expect(resetPasswordCard).toBeTruthy(); + + // Verify reset password form elements are present + const resetPasswordEmailInput = await page.$("#resetPasswordEmail"); + const resetPasswordButton = await page.$("#resetPasswordBtn"); + expect(resetPasswordEmailInput).toBeTruthy(); + expect(resetPasswordButton).toBeTruthy(); + + // Verify the form is visible + const isResetPasswordCardVisible = await page.evaluate(() => { + const card = document.getElementById("resetPasswordCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isResetPasswordCardVisible).toBe(true); + }); + + it( + "User requests reset inputs emails, receives code, sets new valid password, completes reset, auto-signs in", + async () => { + const testName = "resetPasswordSuccessfulFlow"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter reset password email and click reset button + await page.waitForSelector("#resetPasswordEmail", { + visible: true, + }); + await page.type("#resetPasswordEmail", resetPasswordUsername); + + // Make sure reset password button is visible and clickable + await page.waitForSelector("#resetPasswordBtn", { + visible: true, + }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const resetPasswordButton = + document.getElementById("resetPasswordBtn"); + if (resetPasswordButton) { + resetPasswordButton.click(); + } else { + throw new Error( + "Reset Password button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot( + page, + "resetPasswordButtonClicked" + ); + + // Wait for OTP input card to appear + await page.waitForSelector("#resetPasswordCodeCard", { + visible: true, + timeout: 35000, + }); + await screenshot.takeScreenshot(page, "otpInputDisplayed"); + + // Get OTP code from email + console.log( + "Retrieving OTP code from email for reset password..." + ); + const otpCode = await resetPasswordClient.readOtpCode(); + console.log("Reset password OTP code retrieved:", otpCode); + + // Enter OTP and submit - ensure OTP field is fully visible first + await page.waitForSelector("#resetPasswordCode", { + visible: true, + }); + + // Clear any existing content and type the OTP code + await page.click("#resetPasswordCode", { clickCount: 3 }); + await page.type("#resetPasswordCode", otpCode); + await screenshot.takeScreenshot(page, "otpCodeEntered"); + + // Wait for the submit button to be visible and enabled + await page.waitForSelector("#submitResetPasswordCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + + // Submit the OTP code + await page.evaluate(() => { + const submitButton = + document.getElementById("submitResetPasswordCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error( + "Submit OTP button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot(page, "otpSubmitted"); + + // Wait for new password input card to appear + await page.waitForSelector("#resetPasswordNewPasswordCard", { + visible: true, + timeout: 40000, + }); + await screenshot.takeScreenshot( + page, + "newPasswordInputDisplayed" + ); + + // Enter new password + await page.waitForSelector("#resetPasswordNewPassword", { + visible: true, + }); + await page.type( + "#resetPasswordNewPassword", + nativeAuthConfig.passwordSignInEmailCode + ); + await screenshot.takeScreenshot(page, "newPasswordEntered"); + + // Submit new password + await page.waitForSelector( + "#submitResetPasswordNewPasswordBtn:enabled", + { + visible: true, + timeout: 15000, + } + ); + + await page.evaluate(() => { + const submitButton = document.getElementById( + "submitResetPasswordNewPasswordBtn" + ); + if (submitButton) { + submitButton.click(); + } else { + throw new Error( + "Submit new password button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot(page, "newPasswordSubmitted"); + + // Wait for successful completion + await page.waitForFunction( + () => { + const authStatusBanner = + document.getElementById("authStatusBanner"); + const isCompleted = + authStatusBanner && ( + authStatusBanner.textContent?.includes( + "Signed in" + )); + return isCompleted; + }, + { timeout: 35000 } + ); + + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + + await screenshot.takeScreenshot(page, "resetPasswordCompleted"); + }, + AUTH_TIMEOUT + ); + }); + + describe("Reset Password Flow - Email + Password - Negative", () => { + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?usePwdConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + // Verify reset password button is visible on the navigation bar + const showResetPasswordButton = await page.$( + "#showResetPasswordBtn" + ); + expect(showResetPasswordButton).toBeTruthy(); + + // Click reset password button on the navigation bar + await page.click("#showResetPasswordBtn"); + + // Verify reset password card is visible + const resetPasswordCard = await page.$("#resetPasswordCard"); + expect(resetPasswordCard).toBeTruthy(); + + // Verify reset password form elements are present + const resetPasswordEmailInput = await page.$("#resetPasswordEmail"); + const resetPasswordButton = await page.$("#resetPasswordBtn"); + expect(resetPasswordEmailInput).toBeTruthy(); + expect(resetPasswordButton).toBeTruthy(); + + // Verify the form is visible + const isResetPasswordCardVisible = await page.evaluate(() => { + const card = document.getElementById("resetPasswordCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isResetPasswordCardVisible).toBe(true); + }); + + it( + "User submits non-existing email, receives account not found error", + async () => { + const testName = "resetPasswordWithNonExistingUsername"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter username in the reset password form and click reset password button + await page.waitForSelector("#resetPasswordEmail", { + visible: true, + }); + await page.type( + "#resetPasswordEmail", + "non-existemail@test.com" + ); + + // Make sure reset password button is visible and clickable + await page.waitForSelector("#resetPasswordBtn", { + visible: true, + }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const resetPasswordButton = + document.getElementById("resetPasswordBtn"); + if (resetPasswordButton) { + resetPasswordButton.click(); + } else { + throw new Error( + "Reset Password button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot( + page, + "resetPasswordButtonClicked" + ); + + // Wait for error message to appear + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { + visible: true, + timeout: 20000, + }); + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + + // Verify error banner content + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain("user_not_found"); + }, + AUTH_TIMEOUT + ); + + it( + "User submits existing email, but email does not linked to any password (registered as email + OTP)", + async () => { + const testName = "resetPasswordWithOtpUsername"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter username in the reset password form and click reset password button + await page.waitForSelector("#resetPasswordEmail", { + visible: true, + }); + await page.type( + "#resetPasswordEmail", + resetPasswordEmailWithOtp + ); + + // Make sure reset password button is visible and clickable + await page.waitForSelector("#resetPasswordBtn", { + visible: true, + }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const resetPasswordButton = + document.getElementById("resetPasswordBtn"); + if (resetPasswordButton) { + resetPasswordButton.click(); + } else { + throw new Error( + "Reset Password button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot( + page, + "resetPasswordButtonClicked" + ); + + // Wait for error message to appear + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { + visible: true, + timeout: 15000, + }); + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + + // Verify error banner content + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain("does not support native credential recovery"); + }, + AUTH_TIMEOUT + ); + + it( + "User submits existing email, and submit incorrect code", + async () => { + const testName = "resetPasswordWithIncorrectOtp"; + let screenshot: Screenshot | undefined; + + screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter username in the reset password form and click reset password button + await page.waitForSelector("#resetPasswordEmail", { + visible: true, + }); + await page.type("#resetPasswordEmail", resetPasswordUsername); + // Make sure reset password button is visible and clickable + await page.waitForSelector("#resetPasswordBtn", { + visible: true, + }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const resetPasswordButton = + document.getElementById("resetPasswordBtn"); + if (resetPasswordButton) { + resetPasswordButton.click(); + } else { + throw new Error( + "Reset Password button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot( + page, + "resetPasswordButtonClicked" + ); + + // Wait for code input card to appear + await page.waitForSelector("#resetPasswordCodeCard", { + visible: true, + timeout: 45000, + }); + await screenshot.takeScreenshot(page, "resetPasswordCodeCard"); + + // Enter code and submit - ensure code field is fully visible first + await page.waitForSelector("#resetPasswordCode", { + visible: true, + }); + await page.type("#resetPasswordCode", "12345678"); // Enter incorrect code + await screenshot.takeScreenshot( + page, + "resetPasswordCodeEntered" + ); + await page.click("#submitResetPasswordCodeBtn"); + await screenshot.takeScreenshot( + page, + "submitCodeButtonClicked" + ); + + // Wait for error message to appear + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { + visible: true, + timeout: 15000, + }); + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + + // Verify error banner content + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain( + "Error: invalid_grant: AADSTS50181: Unable to validate the otp" + ); + }, + AUTH_TIMEOUT + ); + + it( + "User submits existing email, receives code, creates invalid password (doesn't meet password complexity requirements), receives requirements error", + async () => { + const testName = "resetPasswordComplexityError"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter reset password email and click reset button + await page.waitForSelector("#resetPasswordEmail", { + visible: true, + }); + await page.type("#resetPasswordEmail", resetPasswordUsername); + + // Make sure reset password button is visible and clickable + await page.waitForSelector("#resetPasswordBtn", { + visible: true, + }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const resetPasswordButton = + document.getElementById("resetPasswordBtn"); + if (resetPasswordButton) { + resetPasswordButton.click(); + } else { + throw new Error( + "Reset Password button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot( + page, + "resetPasswordButtonClicked" + ); + + // Wait for OTP input card to appear + await page.waitForSelector("#resetPasswordCodeCard", { + visible: true, + timeout: 45000, + }); + await screenshot.takeScreenshot(page, "otpInputDisplayed"); + + // Get OTP code from email + console.log( + "Retrieving OTP code from email for reset password..." + ); + const otpCode = await resetPasswordClient.readOtpCode(); + console.log("Reset password OTP code retrieved:", otpCode); + + // Enter OTP and submit - ensure OTP field is fully visible first + await page.waitForSelector("#resetPasswordCode", { + visible: true, + }); + + // Clear any existing content and type the OTP code + await page.click("#resetPasswordCode", { clickCount: 3 }); + await page.type("#resetPasswordCode", otpCode); + await screenshot.takeScreenshot(page, "otpCodeEntered"); + + // Wait for the submit button to be visible and enabled + await page.waitForSelector("#submitResetPasswordCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + + // Submit the OTP code + await page.evaluate(() => { + const submitButton = + document.getElementById("submitResetPasswordCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error( + "Submit OTP button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot(page, "otpSubmitted"); + + // Wait for new password input card to appear + await page.waitForSelector("#resetPasswordNewPasswordCard", { + visible: true, + timeout: 35000, + }); + await screenshot.takeScreenshot( + page, + "newPasswordInputDisplayed" + ); + + // Enter a weak password that doesn't meet complexity requirements + await page.waitForSelector("#resetPasswordNewPassword", { + visible: true, + }); + await page.type( + "#resetPasswordNewPassword", + testData.invalidPassword + ); + await screenshot.takeScreenshot(page, "weakPasswordEntered"); + + // Submit weak password + await page.waitForSelector( + "#submitResetPasswordNewPasswordBtn:enabled", + { + visible: true, + timeout: 15000, + } + ); + + await page.evaluate(() => { + const submitButton = document.getElementById( + "submitResetPasswordNewPasswordBtn" + ); + if (submitButton) { + submitButton.click(); + } else { + throw new Error( + "Submit new password button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot(page, "weakPasswordSubmitted"); + + // Wait for the error banner to appear with password complexity requirements + await page.waitForSelector("#errorBanner", { + visible: true, + timeout: 20000, + }); + await screenshot.takeScreenshot( + page, + "passwordComplexityErrorDisplayed" + ); + + // Verify error banner content contains password complexity requirements + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain("password"); // Should contain password-related error + + // Verify we're still on the new password input form + const newPasswordCard = await page.$( + "#resetPasswordNewPasswordCard" + ); + expect(newPasswordCard).toBeTruthy(); + + const isVisible = await page.evaluate(() => { + const card = document.getElementById( + "resetPasswordNewPasswordCard" + ); + return ( + card && window.getComputedStyle(card).display !== "none" + ); + }); + expect(isVisible).toBe(true); + + await screenshot.takeScreenshot(page, "testCompleted"); + }, + AUTH_TIMEOUT + ); + }); + + describe("Reset Password Flow - Redirect", () => { + beforeEach(async () => { + // Use useRedirectConfig=true to ensure the app initializes with redirect-only challenge types + await page.goto( + sampleHomeUrl + `?usePwdConfig=true&useRedirectConfig=true` + ); + + // Wait for the application to initialize with a longer timeout + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + + // Verify reset password button is visible on the navigation bar + const showResetPasswordButton = await page.$( + "#showResetPasswordBtn" + ); + expect(showResetPasswordButton).toBeTruthy(); + + // Click reset password button on the navigation bar + await page.click("#showResetPasswordBtn"); + + // Verify reset password card is visible + const resetPasswordCard = await page.$("#resetPasswordCard"); + expect(resetPasswordCard).toBeTruthy(); + + // Verify reset password form elements are present + const resetPasswordEmailInput = await page.$("#resetPasswordEmail"); + const resetPasswordButton = await page.$("#resetPasswordBtn"); + expect(resetPasswordEmailInput).toBeTruthy(); + expect(resetPasswordButton).toBeTruthy(); + + // Verify the form is visible + const isResetPasswordCardVisible = await page.evaluate(() => { + const card = document.getElementById("resetPasswordCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isResetPasswordCardVisible).toBe(true); + }); + + it( + "User email is registered with email OTP auth method, which is not supported by the developer (redirect flow)", + async () => { + const testName = "resetPasswordRedirect"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter username in the reset password form and click reset password button + await page.waitForSelector("#resetPasswordEmail", { + visible: true, + }); + await page.type( + "#resetPasswordEmail", + resetPasswordEmailWithOtp + ); + + // Make sure reset password button is visible and clickable + await page.waitForSelector("#resetPasswordBtn", { + visible: true, + }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const resetPasswordButton = + document.getElementById("resetPasswordBtn"); + if (resetPasswordButton) { + resetPasswordButton.click(); + } else { + throw new Error( + "Reset Password button not found in the DOM" + ); + } + }); + await screenshot.takeScreenshot( + page, + "resetPasswordButtonClicked" + ); + + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { + visible: true, + timeout: 15000, + }); + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + + // Verify error banner content + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain( + "Password Reset Error: Error: invalid_request: AADSTS500222:" + ); + }, + AUTH_TIMEOUT + ); + }); +}); diff --git a/samples/msal-browser-samples/NativeAuthSample/test/signin.spec.ts b/samples/msal-browser-samples/NativeAuthSample/test/signin.spec.ts new file mode 100644 index 0000000000..bab0fd5db7 --- /dev/null +++ b/samples/msal-browser-samples/NativeAuthSample/test/signin.spec.ts @@ -0,0 +1,851 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import * as puppeteer from "puppeteer"; +import { + Screenshot, + createFolder, + getBrowser, + pcaInitializedPoller, + BrowserCacheUtils, + getHomeUrl, +} from "e2e-test-utils"; +import { ChildProcess } from "child_process"; +import path = require("path"); +import { startCorsProxy, stopCorsProxy } from "./proxyUtils"; +import { MailTmClient } from "./emailProviderUtils"; +import { + testConfig, + getTenantInfo, + getProxyPort, + getTestData, + nativeAuthConfig +} from "./configUtils"; + +// Use configuration instead of hardcoded values +const SCREENSHOT_BASE_FOLDER_NAME = path.join(__dirname, testConfig.screenshots.baseFolderName, "/signin"); +const AUTH_TIMEOUT = testConfig.timeouts.auth; +let sampleHomeUrl = ""; + +describe("Native Auth Sample - Sign In Tests", () => { + let context: puppeteer.BrowserContext; + let page: puppeteer.Page; + let BrowserCache: BrowserCacheUtils; + let browser: puppeteer.Browser; + let signInEmailUsername: string = ""; + let accountPwd: string = ""; + let signInEmailOtpUsername: string = ""; + let corsProcess: ChildProcess; + let invalidTestUsers: any; // Store test data for all tests + let emailOtpClient: MailTmClient; + + beforeAll(async () => { + // Start the CORS proxy server using configuration values + const tenantInfo = getTenantInfo(); + corsProcess = await startCorsProxy( + tenantInfo.name, + tenantInfo.id, + getProxyPort() + ); + + if (testConfig.screenshots.enabled) { + createFolder(SCREENSHOT_BASE_FOLDER_NAME); + } + browser = await getBrowser(); + sampleHomeUrl = getHomeUrl(); + + // Set up email usernames from configuration for reuse across all tests + signInEmailUsername = nativeAuthConfig.signInEmailPasswordUsername; + signInEmailOtpUsername = nativeAuthConfig.signInEmailCodeUsername; + accountPwd = nativeAuthConfig.passwordSignInEmailCode; + + // Fetch test data once for all tests + invalidTestUsers = getTestData(); + }); + + afterAll(async () => { + await context?.close(); + await browser?.close(); + // Stop the CORS proxy server using the utility function + stopCorsProxy(corsProcess); + }); + + beforeEach(async () => { + context = await browser.createBrowserContext(); + page = await context.newPage(); + + BrowserCache = new BrowserCacheUtils( + page, + "sessionStorage" // Based on Native Auth Sample configuration + ); + }); + + afterEach(async () => { + // Clear storage after each test + await page.evaluate(() => { + window.sessionStorage.clear(); + window.localStorage.clear(); + }); + await page.close(); + }); + + describe("Sign In Flow - Email + Password - Positive", () => { + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?usePwdConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); + + // Verify that no user signed in initially + const authStatusBanner = await page.$eval( + "#authStatusBanner", + (el) => el.textContent + ); + expect(authStatusBanner).toContain("No user signed in"); + + // Verify sign-in button is visible on the navigation bar + const showSignInBtn = await page.$("#showSignInBtn"); + expect(showSignInBtn).toBeTruthy(); + + // Click sign-in button on the navigation bar + await page.click("#showSignInBtn"); + + // Verify sign-in card is visible + const signInCard = await page.$("#signInCard"); + expect(signInCard).toBeTruthy(); + + // Verify sign-in form elements are present + const usernameInput = await page.$("#username"); + const signInButton = await page.$("#signInBtn"); + expect(usernameInput).toBeTruthy(); + expect(signInButton).toBeTruthy(); + + // Verify the form is visible + const isSignInCardVisible = await page.evaluate(() => { + const card = document.getElementById("signInCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isSignInCardVisible).toBe(true); + }); + + it("User inputs registered email and password, signs in successfully", async () => { + const testName = "signInFormDisplay"; + let screenshot: Screenshot | undefined; + + if (testConfig.screenshots.enabled) { + screenshot = new Screenshot(`${SCREENSHOT_BASE_FOLDER_NAME}/${testName}`); + } + + // Enter username in the sign-in form and click sign-in button + await page.waitForSelector("#username", { visible: true }); + await page.type("#username", signInEmailUsername); + + // Make sure sign-in button is visible and clickable + await page.waitForSelector("#signInBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signInButton = document.getElementById("signInBtn"); + if (signInButton) { + signInButton.click(); + } else { + throw new Error("Sign in button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "signInButtonClicked"); + } + + // Wait for password input card to appear + await page.waitForSelector("#passwordInputCard"); + if (screenshot) { + await screenshot.takeScreenshot(page, "passwordInputDisplayed"); + } + + // Enter password and submit - ensure password field is fully visible first + await page.waitForSelector("#signInPassword", { + visible: true, + }); + await page.type("#signInPassword", accountPwd); + if (screenshot) { + await screenshot.takeScreenshot(page, "passwordInputEntered"); + } + + // Wait for the submit button to be visible and enabled + await page.waitForSelector("#submitPasswordBtn:enabled", { + visible: true, + timeout: 15000, + }); + // Use evaluate to ensure a clean click operation rather than direct page.click() + await page.evaluate(() => { + const submitButton = + document.getElementById("submitPasswordBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "passwordSubmitted"); + } + + // Wait for successful sign-in (check for both auth status banner and account info) + // Use a more reliable indicator with longer timeout since authentication can take time + await page.waitForFunction( + () => { + // Check auth status banner + const authStatusBanner = + document.getElementById("authStatusBanner"); + const isSignedIn = + authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 30000 } // Increase timeout for more reliability + ); + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + if (screenshot) { + await screenshot.takeScreenshot(page, "signInSuccessful"); + } + }, AUTH_TIMEOUT); + + }); + + describe("Sign In Flow - Email + Password - Negative", () => { + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?usePwdConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); + + // Verify that no user signed in initially + const authStatusBanner = await page.$eval( + "#authStatusBanner", + (el) => el.textContent + ); + expect(authStatusBanner).toContain("No user signed in"); + + // Verify sign-in button is visible on the navigation bar + const showSignInBtn = await page.$("#showSignInBtn"); + expect(showSignInBtn).toBeTruthy(); + + // Click sign-in button on the navigation bar + await page.click("#showSignInBtn"); + + // Verify sign-in card is visible + const signInCard = await page.$("#signInCard"); + expect(signInCard).toBeTruthy(); + + // Verify sign-in form elements are present + const usernameInput = await page.$("#username"); + const signInButton = await page.$("#signInBtn"); + expect(usernameInput).toBeTruthy(); + expect(signInButton).toBeTruthy(); + + // Verify the form is visible + const isSignInCardVisible = await page.evaluate(() => { + const card = document.getElementById("signInCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isSignInCardVisible).toBe(true); + }); + + it("User inputs non-registered email, receives account not found error", async () => { + const testName = "signInWithNonRegisteredUsername"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter username in the sign-in form and click sign-in button + const nonRegisteredEmail = invalidTestUsers.nonRegisteredEmail; + await page.type("#username", nonRegisteredEmail); + await page.click("#signInBtn"); + await screenshot.takeScreenshot(page, "signInButtonClicked"); + + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { + visible: true, + timeout: 15000, + }); + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + + // Verify error banner content + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain("Error: user_not_found"); + + // Verify that the user is still not signed in + const authStatusBanner = await page.$eval( + "#authStatusBanner", + (el) => el.textContent + ); + expect(authStatusBanner).toContain("No user signed in"); + }, AUTH_TIMEOUT); + + it("User inputs registered email, provides incorrect password, receives error", async () => { + const testName = "signInWithIncorrectPassword"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter username in the sign-in form and click sign-in button + await page.type("#username", signInEmailUsername); + await page.click("#signInBtn"); + await screenshot.takeScreenshot(page, "signInButtonClicked"); + + // Wait for password input card to appear + await page.waitForSelector("#passwordInputCard"); + await screenshot.takeScreenshot(page, "passwordInputDisplayed"); + + // Enter incorrect password and submit + await page.waitForSelector("#signInPassword", { + visible: true, + }); + await page.type("#signInPassword", invalidTestUsers.incorrectPassword); + await screenshot.takeScreenshot( + page, + "incorrectPasswordEntered" + ); + + // Wait for the submit button to be visible and enabled + await page.waitForSelector("#submitPasswordBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.click("#submitPasswordBtn"); + await screenshot.takeScreenshot( + page, + "incorrectPasswordSubmitted" + ); + + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { + visible: true, + timeout: 15000, + }); + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + + // Verify error banner content + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain("Sign-in Error:"); + + // Verify that the user is still not signed in + const authStatusBanner = await page.$eval( + "#authStatusBanner", + (el) => el.textContent + ); + expect(authStatusBanner).toContain("No user signed in"); + + // Verify we're still on the password input form + const passwordInputCard = await page.$("#passwordInputCard"); + expect(passwordInputCard).toBeTruthy(); + + const isVisible = await page.evaluate(() => { + const card = document.getElementById("passwordInputCard"); + return ( + card && window.getComputedStyle(card).display !== "none" + ); + }); + expect(isVisible).toBe(true); + + // Try dismissing the error banner + await page.click("#dismissErrorBtn"); + + // Verify error banner is hidden + const errorBannerVisible = await page.evaluate(() => { + const banner = document.getElementById("errorBanner"); + return banner + ? window.getComputedStyle(banner).display !== "none" + : false; + }); + expect(errorBannerVisible).toBe(false); + }, AUTH_TIMEOUT); + + it("User signs in with account A when account A has already signed in", async () => { + const testName = "signInFormErrorWithSameAccount"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter username in the sign-in form and click sign-in button + await page.waitForSelector("#username", { visible: true }); + await page.type("#username", signInEmailUsername); + + // Make sure sign-in button is visible and clickable + await page.waitForSelector("#signInBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signInButton = document.getElementById("signInBtn"); + if (signInButton) { + signInButton.click(); + } else { + throw new Error("Sign in button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "signInButtonClicked"); + + // Wait for password input card to appear + await page.waitForSelector("#passwordInputCard"); + await screenshot.takeScreenshot(page, "passwordInputDisplayed"); + + // Enter password and submit - ensure password field is fully visible first + await page.waitForSelector("#signInPassword", { + visible: true, + }); + await page.type("#signInPassword", accountPwd); + await screenshot.takeScreenshot(page, "passwordInputEntered"); + + // Wait for the submit button to be visible and enabled + await page.waitForSelector("#submitPasswordBtn:enabled", { + visible: true, + timeout: 15000, + }); + // Use evaluate to ensure a clean click operation rather than direct page.click() + await page.evaluate(() => { + const submitButton = + document.getElementById("submitPasswordBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "passwordSubmitted"); + + // Wait for successful sign-in (check for both auth status banner and account info) + // Use a more reliable indicator with longer timeout since authentication can take time + await page.waitForFunction( + () => { + // Check auth status banner + const authStatusBanner = + document.getElementById("authStatusBanner"); + const isSignedIn = + authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 30000 } // Increase timeout for more reliability + ); + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + + // Now sign in with same account + // Click sign-in button again + await page.click("#showSignInBtn"); + // Verify sign-in card is visible + const signInCard = await page.$("#signInCard"); + expect(signInCard).toBeTruthy(); + // Enter account B username in the sign-in form + await page.waitForSelector("#username", { visible: true }); + await page.type("#username", signInEmailUsername); // Using account B email + // Make sure sign-in button is visible and clickable + await page.waitForSelector("#signInBtn", { visible: true }); + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signInButton = document.getElementById("signInBtn"); + if (signInButton) { + signInButton.click(); + } else { + throw new Error("Sign in button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "signInButtonClicked"); + + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { + visible: true, + timeout: 15000, + }); + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + + // Verify error banner content + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain( + "Error: user_already_signed_in:" + ); + }, AUTH_TIMEOUT); + + }); + + describe("Sign In Flow - Email + OTP - Positive", () => { + beforeAll(async () => { + + // Initialize email client for OTP account + emailOtpClient = new MailTmClient(); + await emailOtpClient.login( + signInEmailOtpUsername, + nativeAuthConfig.passwordProvider + ); + }); + + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?useOtpConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + + // Verify that no user signed in initially + const authStatusBanner = await page.$eval( + "#authStatusBanner", + (el) => el.textContent + ); + expect(authStatusBanner).toContain("No user signed in"); + + // Verify sign-in button is visible on the navigation bar + const showSignInBtn = await page.$("#showSignInBtn"); + expect(showSignInBtn).toBeTruthy(); + + // Click sign-in button on the navigation bar + await page.click("#showSignInBtn"); + + // Verify sign-in card is visible + const signInCard = await page.$("#signInCard"); + expect(signInCard).toBeTruthy(); + + // Verify sign-in form elements are present + const usernameInput = await page.$("#username"); + const signInButton = await page.$("#signInBtn"); + expect(usernameInput).toBeTruthy(); + expect(signInButton).toBeTruthy(); + + // Verify the form is visible + const isSignInCardVisible = await page.evaluate(() => { + const card = document.getElementById("signInCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isSignInCardVisible).toBe(true); + }); + + it("User inputs registered email, then receives OTP, verifies successfully", async () => { + const testName = "emailOtpSignInSuccessful"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter email in the sign-in form and click sign-in button + await page.waitForSelector("#username", { visible: true }); + await page.type("#username", signInEmailOtpUsername); + + // Make sure sign-in button is visible and clickable + await page.waitForSelector("#signInBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signInButton = document.getElementById("signInBtn"); + if (signInButton) { + signInButton.click(); + } else { + throw new Error("Sign in button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "signInButtonClicked"); + + // Wait for OTP input card to appear + await page.waitForSelector("#codeVerificationCard", { + visible: true, + }); + await screenshot.takeScreenshot(page, "otpInputDisplayed"); + + // Get OTP code from email + console.log("Retrieving OTP code from email..."); + const otpCode = await emailOtpClient.readOtpCode(); + console.log("OTP code retrieved:", otpCode); + + // Enter OTP and submit - ensure OTP field is fully visible first + await page.waitForSelector("#verificationCode", { + visible: true, + }); + + // Clear any existing content and type the OTP code + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", otpCode); + await screenshot.takeScreenshot(page, "otpCodeEntered"); + + // Wait for the submit button to be visible and enabled + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + + // Submit the OTP code + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "otpSubmitted"); + + // Wait for successful sign-in + await page.waitForFunction( + () => { + const authStatusBanner = document.getElementById("authStatusBanner"); + const isSignedIn = authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 45000 } + ); + + // Verify tokens and authentication + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + await screenshot.takeScreenshot(page, "signInSuccessful"); + }, AUTH_TIMEOUT); + + it("User inputs registered email, enters incorrect OTP code, requests new OTP, enters valid code, signs in successfully", async () => { + const testName = "emailOtpSignInWithRetry"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Phase 1: Enter email and initiate sign-in + await page.waitForSelector("#username", { visible: true }); + await page.type("#username", signInEmailOtpUsername); + + await page.waitForSelector("#signInBtn", { visible: true }); + await page.evaluate(() => { + const signInButton = document.getElementById("signInBtn"); + if (signInButton) { + signInButton.click(); + } else { + throw new Error("Sign in button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "1_signInButtonClicked"); + + // Phase 2: Wait for OTP input card to appear + await page.waitForSelector("#codeVerificationCard", { + visible: true, + }); + await screenshot.takeScreenshot(page, "2_otpInputDisplayed"); + + // Phase 3: Enter incorrect OTP and handle error + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", "12345678"); // Incorrect OTP + await screenshot.takeScreenshot(page, "3_incorrectOtpEntered"); + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "4_incorrectOtpSubmitted"); + + // Wait for error banner to appear + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + await screenshot.takeScreenshot(page, "5_errorBannerDisplayed"); + + // Verify error message + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + expect(errorMessage).toContain("Sign-in Error:"); + + // Dismiss error banner + const dismissBtn = await page.$("#dismissErrorBtn"); + if (dismissBtn) { + await page.click("#dismissErrorBtn"); + await screenshot.takeScreenshot(page, "6_errorDismissed"); + } + + // Phase 4: Resend OTP code + await page.waitForSelector("#resendCodeBtn", { visible: true }); + await page.click("#resendCodeBtn"); + await screenshot.takeScreenshot(page, "7_resendCodeClicked"); + + // Wait a moment for resend to process + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Phase 5: Get new OTP code from email + console.log("Retrieving new OTP code from email after resend..."); + const otpCode = await emailOtpClient.readOtpCode(); + console.log("New OTP code retrieved:", otpCode); + + // Phase 6: Enter correct OTP and submit + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", otpCode); + await screenshot.takeScreenshot(page, "8_correctOtpEntered"); + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "9_correctOtpSubmitted"); + + // Phase 7: Wait for successful sign-in + await page.waitForFunction( + () => { + const authStatusBanner = document.getElementById("authStatusBanner"); + const isSignedIn = authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 35000 } + ); + await screenshot.takeScreenshot(page, "10_signInSuccessful"); + + // Phase 8: Verify tokens and authentication state + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + + // Verify user is actually signed in + const finalAuthStatus = await page.$eval("#authStatusBanner", (el) => el.textContent); + expect(finalAuthStatus).toContain("Signed in"); + + await screenshot.takeScreenshot(page, "11_signInFlowCompleted"); + + console.log("Email OTP sign-in with retry completed successfully"); + }, AUTH_TIMEOUT); + + }); + + describe("Sign In Flow - Email + OTP Redirect", () => { + beforeEach(async () => { + // Use useRedirectConfig=true to ensure the app initializes with redirect-only challenge types + await page.goto( + sampleHomeUrl + `?useOtpConfig=true&useRedirectConfig=true` + ); + // Wait for the application to initialize with a longer timeout + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + + // Verify that no user signed in initially + const authStatusBanner = await page.$eval( + "#authStatusBanner", + (el) => el.textContent + ); + expect(authStatusBanner).toContain("No user signed in"); + + // Take a screenshot of the initialized state + const setupScreenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/setup` + ); + await setupScreenshot.takeScreenshot(page, "appInitialized"); + + // Verify sign-in button is visible on the navigation bar + const showSignInBtn = await page.$("#showSignInBtn"); + expect(showSignInBtn).toBeTruthy(); + + // Click sign-in button on the navigation bar + await page.click("#showSignInBtn"); + + // Verify sign-in card is visible + const signInCard = await page.$("#signInCard"); + expect(signInCard).toBeTruthy(); + + // Verify sign-in form elements are present + const usernameInput = await page.$("#username"); + const signInButton = await page.$("#signInBtn"); + expect(usernameInput).toBeTruthy(); + expect(signInButton).toBeTruthy(); + }); + + it( + "User email is registered with email OTP auth method, which is not supported by the developer (redirect flow)", + async () => { + const testName = "emailOtpSignInRedirect"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter email in the sign-in form and click sign-in button + await page.waitForSelector("#username", { visible: true }); + await page.type("#username", signInEmailOtpUsername); + + // Make sure sign-in button is visible and clickable + await page.waitForSelector("#signInBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signInButton = document.getElementById("signInBtn"); + if (signInButton) { + signInButton.click(); + } else { + throw new Error("Sign in button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "signInButtonClicked"); + + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { visible: true, timeout: 20000 }); + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + + // Verify error banner content + const errorMessage = await page.$eval( + "#errorMessage", + (el) => el.textContent + ); + expect(errorMessage).toContain( + "redirect" + ); + }, + AUTH_TIMEOUT + ); + }); +}); diff --git a/samples/msal-browser-samples/NativeAuthSample/test/signout.spec.ts b/samples/msal-browser-samples/NativeAuthSample/test/signout.spec.ts new file mode 100644 index 0000000000..6b0daeeb68 --- /dev/null +++ b/samples/msal-browser-samples/NativeAuthSample/test/signout.spec.ts @@ -0,0 +1,203 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import * as puppeteer from "puppeteer"; +import { + Screenshot, + createFolder, + getBrowser, + pcaInitializedPoller, + BrowserCacheUtils, + getHomeUrl, +} from "e2e-test-utils"; +import { ChildProcess } from "child_process"; +import path = require("path"); +import { startCorsProxy, stopCorsProxy } from "./proxyUtils"; + +import { + testConfig, + getTenantInfo, + getProxyPort, + nativeAuthConfig +} from "./configUtils"; + +// Use configuration instead of hardcoded values +const SCREENSHOT_BASE_FOLDER_NAME = path.join(__dirname, testConfig.screenshots.baseFolderName, "/signout"); +const AUTH_TIMEOUT = testConfig.timeouts.auth; +let sampleHomeUrl = ""; + +describe("Native Auth Sample - Sign Out Tests", () => { + let context: puppeteer.BrowserContext; + let page: puppeteer.Page; + let BrowserCache: BrowserCacheUtils; + let browser: puppeteer.Browser; + let signInEmailUsername: string = ""; + let accountPwd: string = ""; + let corsProcess: ChildProcess; + + beforeAll(async () => { + // Start the CORS proxy server using configuration values + const tenantInfo = getTenantInfo(); + corsProcess = await startCorsProxy( + tenantInfo.name, + tenantInfo.id, + getProxyPort() + ); + + if (testConfig.screenshots.enabled) { + createFolder(SCREENSHOT_BASE_FOLDER_NAME); + } + browser = await getBrowser(); + sampleHomeUrl = getHomeUrl(); + + // Use configuration for test user emails from JSON config + signInEmailUsername = nativeAuthConfig.signInEmailPasswordUsername; + accountPwd = nativeAuthConfig.passwordSignInEmailCode; + }); + + afterAll(async () => { + await context?.close(); + await browser?.close(); + stopCorsProxy(corsProcess); + }); + + + beforeEach(async () => { + context = await browser.createBrowserContext(); + page = await context.newPage(); + + BrowserCache = new BrowserCacheUtils( + page, + "sessionStorage" // Based on Native Auth Sample configuration + ); // Navigate to the Native Auth Sample home page and wait for network idle to ensure full page load + }); + + afterEach(async () => { + // Clear storage after each test + await page.evaluate(() => { + window.sessionStorage.clear(); + window.localStorage.clear(); + }); + await page.close(); + }); + + describe("Sign Out Flow - Email + Password", () => { + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?usePwdConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + + // Verify that no user signed in initially + const authStatusBanner = await page.$eval("#authStatusBanner", (el) => el.textContent); + expect(authStatusBanner).toContain("No user signed in"); + + // Verify sign-in button is visible on the navigation bar + const showSignInBtn = await page.$("#showSignInBtn"); + expect(showSignInBtn).toBeTruthy(); + + // Click sign-in button on the navigation bar + await page.click("#showSignInBtn"); + + // Verify sign-in card is visible + const signInCard = await page.$("#signInCard"); + expect(signInCard).toBeTruthy(); + + // Verify sign-in form elements are present + const usernameInput = await page.$("#username"); + const signInButton = await page.$("#signInBtn"); + expect(usernameInput).toBeTruthy(); + expect(signInButton).toBeTruthy(); + + // Verify the form is visible + const isSignInCardVisible = await page.evaluate(() => { + const card = document.getElementById("signInCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isSignInCardVisible).toBe(true); + }); + + it("User sign-in with username and correct password, then sign out", async () => { + const testName = "signInFormDisplay"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Enter username in the sign-in form and click sign-in button + await page.waitForSelector("#username", { visible: true }); + await page.type("#username", signInEmailUsername); + + // Make sure sign-in button is visible and clickable + await page.waitForSelector("#signInBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signInButton = document.getElementById("signInBtn"); + if (signInButton) { + signInButton.click(); + } else { + throw new Error("Sign in button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "signInButtonClicked"); + + // Wait for password input card to appear + await page.waitForSelector("#passwordInputCard"); + await screenshot.takeScreenshot(page, "passwordInputDisplayed"); + + // Enter password and submit - ensure password field is fully visible first + await page.waitForSelector("#signInPassword", { visible: true }); + await page.type("#signInPassword", accountPwd); + await screenshot.takeScreenshot(page, "passwordInputEntered"); + + // Wait for the submit button to be visible and enabled + await page.waitForSelector("#submitPasswordBtn:enabled", { visible: true, timeout: 15000 }); + // Use evaluate to ensure a clean click operation rather than direct page.click() + await page.evaluate(() => { + const submitButton = document.getElementById("submitPasswordBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "passwordSubmitted"); + + // Wait for successful sign-in (check for both auth status banner and account info) + // Use a more reliable indicator with longer timeout since authentication can take time + await page.waitForFunction( + () => { + // Check auth status banner + const authStatusBanner = document.getElementById("authStatusBanner"); + const isSignedIn = authStatusBanner && authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 30000 } // Increase timeout for more reliability + ); + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect( + await BrowserCache.getAccountFromCache() + ).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + await screenshot.takeScreenshot(page, "signInSuccessful"); + + // click sign-out button to end the session + await page.click("#navSignOutBtn"); + await screenshot.takeScreenshot(page, "signOutButtonClicked"); + // Wait for the sign-out confirmation + const authStatusBanner = await page.$eval("#authStatusBanner", (el) => el.textContent); + expect(authStatusBanner).toContain("No user signed in"); + }, AUTH_TIMEOUT); + }); + +}); diff --git a/samples/msal-browser-samples/NativeAuthSample/test/signup.spec.ts b/samples/msal-browser-samples/NativeAuthSample/test/signup.spec.ts new file mode 100644 index 0000000000..9189445693 --- /dev/null +++ b/samples/msal-browser-samples/NativeAuthSample/test/signup.spec.ts @@ -0,0 +1,1195 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. + */ + +import * as puppeteer from "puppeteer"; +import { + Screenshot, + createFolder, + getBrowser, + pcaInitializedPoller, + BrowserCacheUtils, + getHomeUrl, +} from "e2e-test-utils"; +import { ChildProcess } from "child_process"; +import path = require("path"); +import { startCorsProxy, stopCorsProxy } from "./proxyUtils"; +import { MailTmClient } from "./emailProviderUtils"; + +import { + testConfig, + getTenantInfo, + getProxyPort, + nativeAuthConfig, + testData +} from "./configUtils"; + +// Use configuration instead of hardcoded values +const SCREENSHOT_BASE_FOLDER_NAME = path.join(__dirname, testConfig.screenshots.baseFolderName, "/signup"); +const AUTH_TIMEOUT = testConfig.timeouts.auth; +let sampleHomeUrl = ""; + +describe("Native Auth Sample - Sign Up Tests", () => { + let context: puppeteer.BrowserContext; + let page: puppeteer.Page; + let BrowserCache: BrowserCacheUtils; + let browser: puppeteer.Browser; + let testFirstName: string = ""; + let testLastName: string = ""; + let existingPwdEmail: string = ""; + let corsProcess: ChildProcess; + + beforeAll(async () => { + // Start the CORS proxy server using configuration values + const tenantInfo = getTenantInfo(); + corsProcess = await startCorsProxy( + tenantInfo.name, + tenantInfo.id, + getProxyPort() + ); + + if (testConfig.screenshots.enabled) { + createFolder(SCREENSHOT_BASE_FOLDER_NAME); + } + browser = await getBrowser(); + sampleHomeUrl = getHomeUrl(); + existingPwdEmail = nativeAuthConfig.signInEmailPasswordUsername; + testFirstName = "TestFirstName"; + testLastName = "TestLastName"; + }); + + afterAll(async () => { + await context?.close(); + await browser?.close(); + // Stop the CORS proxy server using the utility function + stopCorsProxy(corsProcess); + }); + + beforeEach(async () => { + context = await browser.createBrowserContext(); + page = await context.newPage(); + + BrowserCache = new BrowserCacheUtils( + page, + "sessionStorage" + ); + }); + + afterEach(async () => { + // Clear storage after each test + await page.evaluate(() => { + window.sessionStorage.clear(); + window.localStorage.clear(); + }); + await page.close(); + }); + + describe("Sign Up Flow - Email + Password - Positive", () => { + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?usePwdConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + // Verify sign-up button is visible on the navigation bar + const showSignUpBtn = await page.$("#showSignUpBtn"); + expect(showSignUpBtn).toBeTruthy(); + + // Click sign-up button on the navigation bar + await page.click("#showSignUpBtn"); + + // Verify sign-up card is visible + const signUpCard = await page.$("#signUpCard"); + expect(signUpCard).toBeTruthy(); + + // Verify sign-up form elements are present + const usernameInput = await page.$("#signUpUsername"); + const signUpButton = await page.$("#signUpBtn"); + expect(usernameInput).toBeTruthy(); + expect(signUpButton).toBeTruthy(); + + // Verify the form is visible + const isSignUpCardVisible = await page.evaluate(() => { + const card = document.getElementById("signUpCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isSignUpCardVisible).toBe(true); + }); + + it("User inputs new email and user attributes, verifies code, creates password meeting requirements, completes sign up flow, then automatically sign-in", async () => { + const testName = "signUpSuccessFlow"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Create a new email inbox using password_provider + const emailClient = new MailTmClient(nativeAuthConfig.passwordProvider); + const { address: signUpEmail } = await emailClient.createInbox(); + console.log(`Created email for signup: ${signUpEmail}`); + + // Enter user details in the sign-up form + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", signUpEmail); + + // Click sign-up button + await page.waitForSelector("#signUpBtn", { visible: true }); + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "signUpButtonClicked"); + + // Wait for OTP verification card to appear + await page.waitForSelector("#codeVerificationCard", { + visible: true, + timeout: 45000, + }); + await screenshot.takeScreenshot(page, "otpVerificationDisplayed"); + + // Login to the email account and then get OTP code + console.log("Logging into email account..."); + await emailClient.login(signUpEmail, nativeAuthConfig.passwordProvider); + console.log("Retrieving OTP code from email..."); + const otpCode = await emailClient.readOtpCode(); + console.log("OTP code retrieved:", otpCode); + + // Enter and submit OTP code + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", otpCode); + await screenshot.takeScreenshot(page, "otpCodeEntered"); + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "otpSubmitted"); + + // Wait for password input card (if required) + await page.waitForSelector("#signUpPasswordCard", { + visible: true, + timeout: 35000, + }); + await screenshot.takeScreenshot(page, "passwordInputDisplayed"); + + // Enter password using config value + await page.waitForSelector("#signUpPassword", { visible: true }); + await page.type("#signUpPassword", nativeAuthConfig.passwordSignInEmailCode); + await screenshot.takeScreenshot(page, "passwordEntered"); + + // Submit password + await page.waitForSelector("#submitSignUpPasswordBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitSignUpPasswordBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit password button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "passwordSubmitted"); + + // Wait for successful signup completion + await page.waitForFunction( + () => { + const authStatusBanner = document.getElementById("authStatusBanner"); + const isSignedIn = authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 35000 } + ); + + // Verify tokens and authentication + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + + await screenshot.takeScreenshot(page, "signUpCompleted"); + }, + AUTH_TIMEOUT + ); + + it("User inputs new email, enters incorrect OTP, resends code, verifies correct code, creates password, completes signup with auto sign-in", async () => { + const testName = "signUpSuccessFlowWithResend"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Create a new email inbox using password_provider + const emailClient = new MailTmClient(nativeAuthConfig.passwordProvider); + const { address: signUpEmail } = await emailClient.createInbox(); + console.log(`Created email for positive signup: ${signUpEmail}`); + + // Phase 1: Enter user details and initiate signup + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", signUpEmail); + + // Click sign-up button + await page.waitForSelector("#signUpBtn", { visible: true }); + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "1_signUpButtonClicked"); + + // Wait for OTP verification card to appear + await page.waitForSelector("#codeVerificationCard", { + visible: true, + timeout: 45000, + }); + await screenshot.takeScreenshot(page, "2_otpVerificationDisplayed"); + + // Phase 2: Enter incorrect OTP and handle error + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", "12345678"); // Incorrect OTP + await screenshot.takeScreenshot(page, "3_incorrectOtpEntered"); + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "4_incorrectOtpSubmitted"); + + // Wait for error banner to appear + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + await screenshot.takeScreenshot(page, "5_errorBannerDisplayed"); + + // Verify error message + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + expect(errorMessage).toContain("Sign-up Error: Error: invalid_grant: AADSTS50181: Unable to validate the otp"); + + // Dismiss error banner + const dismissBtn = await page.$("#dismissErrorBtn"); + if (dismissBtn) { + await page.click("#dismissErrorBtn"); + await screenshot.takeScreenshot(page, "6_errorDismissed"); + } + + // Phase 3: Resend verification code + await page.waitForSelector("#resendCodeBtn", { visible: true }); + await page.click("#resendCodeBtn"); + await screenshot.takeScreenshot(page, "7_resendCodeClicked"); + + // Wait a moment for resend to process + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Phase 4: Login to email and get the new OTP code + console.log("Logging into email account after resend..."); + await emailClient.login(signUpEmail, nativeAuthConfig.passwordProvider); + console.log("Retrieving OTP code from email after resend..."); + const otpCode = await emailClient.readOtpCode(); + console.log("OTP code retrieved after resend:", otpCode); + + // Phase 5: Enter correct OTP and submit + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", otpCode); + await screenshot.takeScreenshot(page, "8_correctOtpEntered"); + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "9_correctOtpSubmitted"); + + // Phase 6: Wait for password input card and create password + await page.waitForSelector("#signUpPasswordCard", { + visible: true, + timeout: 35000, + }); + await screenshot.takeScreenshot(page, "10_passwordInputDisplayed"); + + // Enter password meeting requirements using config value + await page.waitForSelector("#signUpPassword", { visible: true }); + await page.type("#signUpPassword", nativeAuthConfig.passwordSignInEmailCode); + await screenshot.takeScreenshot(page, "11_passwordEntered"); + + // Submit password + await page.waitForSelector("#submitSignUpPasswordBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitSignUpPasswordBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit password button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "12_passwordSubmitted"); + + // Phase 7: Wait for successful signup completion and automatic sign-in + await page.waitForFunction( + () => { + const authStatusBanner = document.getElementById("authStatusBanner"); + const isSignedIn = authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 35000 } + ); + await screenshot.takeScreenshot(page, "13_automaticSignInCompleted"); + + // Phase 8: Verify tokens and authentication state + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + + // Verify user is actually signed in + const finalAuthStatus = await page.$eval("#authStatusBanner", (el) => el.textContent); + expect(finalAuthStatus).toContain("Signed in"); + + await screenshot.takeScreenshot(page, "14_signUpFlowCompleted"); + + console.log("Positive signup flow completed successfully with automatic sign-in"); + }, AUTH_TIMEOUT); + + }); + + describe("Sign Up Flow - Email + Password - Negative", () => { + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?usePwdConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + // Verify sign-up button is visible on the navigation bar + const showSignUpBtn = await page.$("#showSignUpBtn"); + expect(showSignUpBtn).toBeTruthy(); + + // Click sign-up button on the navigation bar + await page.click("#showSignUpBtn"); + + // Verify sign-up card is visible + const signUpCard = await page.$("#signUpCard"); + expect(signUpCard).toBeTruthy(); + + // Verify sign-up form elements are present + const usernameInput = await page.$("#signUpUsername"); + const signUpButton = await page.$("#signUpBtn"); + expect(usernameInput).toBeTruthy(); + expect(signUpButton).toBeTruthy(); + + // Verify the form is visible + const isSignUpCardVisible = await page.evaluate(() => { + const card = document.getElementById("signUpCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isSignUpCardVisible).toBe(true); + }); + + it("User inputs invalid format email address, receives email validation error", async () => { + const testName = "signUpWithInvalidEmailFormat"; + let screenshot: Screenshot | undefined; + + if (testConfig.screenshots.enabled) { + screenshot = new Screenshot(`${SCREENSHOT_BASE_FOLDER_NAME}/${testName}`); + } + + // Use invalid email format - missing TLD as specified + const invalidEmail = "test-1733090331456-k8x9mq@example"; + console.log(`Using invalid format email: ${invalidEmail}`); + + // Enter user details in the sign-up form + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", invalidEmail); + + // Make sure sign-up button is visible and clickable + await page.waitForSelector("#signUpBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "signUpButtonClicked"); + } + + // Wait for error message to appear + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + if (screenshot) { + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + } + + // Verify error banner content - expect specific AADSTS90100 error for invalid email format + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + expect(errorMessage).toContain("Sign-up Error: Error: invalid_request: AADSTS90100: username parameter is empty or not valid"); + }, AUTH_TIMEOUT); + + it("User inputs existing email (registered with email + Password), receives user existed error.", async () => { + const testName = "signUpWithExistingUsername"; + let screenshot: Screenshot | undefined; + + if (testConfig.screenshots.enabled) { + screenshot = new Screenshot(`${SCREENSHOT_BASE_FOLDER_NAME}/${testName}`); + } + + // Enter username in the sign-up form and click sign-up button + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", existingPwdEmail); + + // Make sure sign-up button is visible and clickable + await page.waitForSelector("#signUpBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "signUpButtonClicked"); + } + + // Wait for code input card to appear + await page.waitForSelector("#codeVerificationCard"); + if (screenshot) { + await screenshot.takeScreenshot(page, "codeVerificationCardDisplayed"); + } + + // Wait for error message to appear + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + if (screenshot) { + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + } + + // Verify error banner content + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + expect(errorMessage).toContain(" Error: user_already_exists: AADSTS1003037"); + }, AUTH_TIMEOUT); + + it("User enters username, attributes to start sign-up flow, and enter the incorrect otp", async () => { + const testName = "signUpWithInvalidOtp"; + let screenshot: Screenshot | undefined; + + if (testConfig.screenshots.enabled) { + screenshot = new Screenshot(`${SCREENSHOT_BASE_FOLDER_NAME}/${testName}`); + } + + // Use mock email - no real email service needed for negative test + const signUpEmail = generateMockEmail(); + console.log(`Using mock email for negative signup test: ${signUpEmail}`); + + // Enter user details in the sign-up form + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", signUpEmail); + + // Make sure sign-up button is visible and clickable + await page.waitForSelector("#signUpBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "signUpButtonClicked"); + } + + // Wait for code input card to appear + await page.waitForSelector("#codeVerificationCard"); + if (screenshot) { + await screenshot.takeScreenshot(page, "codeVerificationCardDisplayed"); + } + + // Enter code and submit - ensure code field is fully visible first + await page.waitForSelector("#verificationCode", { visible: true }); + await page.type("#verificationCode", "12345678"); // Enter incorrect code + if (screenshot) { + await screenshot.takeScreenshot(page, "verificationCodeEntered"); + } + await page.click("#submitCodeBtn"); + if (screenshot) { + await screenshot.takeScreenshot(page, "submitCodeButtonClicked"); + } + // Wait for error message to appear + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + if (screenshot) { + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + } + + // Verify error banner content + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + expect(errorMessage).toContain("Sign-up Error: Error: invalid_grant: AADSTS50181: Unable to validate the otp"); + }, AUTH_TIMEOUT); + + it("User inputs new email, verifies code, creates invalid password (does not meet requirements), receives sign up error", async () => { + const testName = "signUpWithInvalidPassword"; + let screenshot: Screenshot | undefined; + + if (testConfig.screenshots.enabled) { + screenshot = new Screenshot(`${SCREENSHOT_BASE_FOLDER_NAME}/${testName}`); + } + + // Create a new email inbox using password_provider + const emailClient = new MailTmClient(nativeAuthConfig.passwordProvider); + const { address: signUpEmail } = await emailClient.createInbox(); + console.log(`Created email for invalid password test: ${signUpEmail}`); + + // Phase 1: Enter user details and initiate signup + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", signUpEmail); + + // Click sign-up button + await page.waitForSelector("#signUpBtn", { visible: true }); + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "1_signUpButtonClicked"); + } + + // Phase 2: Wait for OTP verification card to appear + await page.waitForSelector("#codeVerificationCard", { + visible: true, + timeout: 45000, + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "2_otpVerificationDisplayed"); + } + + // Phase 3: Login to email and get OTP code + console.log("Logging into email account..."); + await emailClient.login(signUpEmail, nativeAuthConfig.passwordProvider); + console.log("Retrieving OTP code from email..."); + const otpCode = await emailClient.readOtpCode(); + console.log("OTP code retrieved:", otpCode); + + // Phase 4: Enter correct OTP and submit + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", otpCode); + if (screenshot) { + await screenshot.takeScreenshot(page, "3_correctOtpEntered"); + } + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "4_otpSubmitted"); + } + + // Phase 5: Wait for password input card to appear + await page.waitForSelector("#signUpPasswordCard", { + visible: true, + timeout: 35000, + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "5_passwordInputDisplayed"); + } + + // Phase 6: Enter invalid password (from test data) + console.log(`Using invalid password: ${testData.invalidPassword}`); + + await page.waitForSelector("#signUpPassword", { visible: true }); + await page.type("#signUpPassword", testData.invalidPassword); + if (screenshot) { + await screenshot.takeScreenshot(page, "6_invalidPasswordEntered"); + } + + // Submit invalid password + await page.waitForSelector("#submitSignUpPasswordBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitSignUpPasswordBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit password button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "7_invalidPasswordSubmitted"); + } + + // Phase 7: Wait for error banner to appear + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + if (screenshot) { + await screenshot.takeScreenshot(page, "8_passwordErrorBannerDisplayed"); + } + + // Phase 8: Verify error banner content for password validation + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + // Expect password requirement error message + expect(errorMessage).toMatch(/(password|requirement|length|complexity|invalid)/i); + }, AUTH_TIMEOUT); + + it("User signs in with existing email, then tries to sign up with same email, receives error to sign out first", async () => { + const testName = "signUpAfterSignInSameUser"; + let screenshot: Screenshot | undefined; + + if (testConfig.screenshots.enabled) { + screenshot = new Screenshot(`${SCREENSHOT_BASE_FOLDER_NAME}/${testName}`); + } + + // Phase 1: First sign in with existing user + await page.goto(sampleHomeUrl + `?usePwdConfig=true`); + await pcaInitializedPoller(page, AUTH_TIMEOUT); + + // Navigate to sign in form + await page.click("#showSignInBtn"); + await page.waitForSelector("#username", { visible: true }); + await page.type("#username", nativeAuthConfig.signInEmailPasswordUsername); + await page.click("#signInBtn"); + if (screenshot) { + await screenshot.takeScreenshot(page, "1_signInInitiated"); + } + + // Enter password for sign in + await page.waitForSelector("#passwordInputCard"); + await page.waitForSelector("#signInPassword", { visible: true }); + await page.type("#signInPassword", nativeAuthConfig.passwordSignInEmailCode); + await page.evaluate(() => { + const submitButton = document.getElementById("submitPasswordBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "2_signInCompleted"); + } + + // Wait for successful sign-in + await page.waitForFunction( + () => { + const authStatusBanner = document.getElementById("authStatusBanner"); + const isSignedIn = authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 30000 } + ); + if (screenshot) { + await screenshot.takeScreenshot(page, "3_userSignedIn"); + } + + // Phase 2: Now try to sign up with the same email while signed in + await page.click("#showSignUpBtn"); + await page.waitForSelector("#signUpCard"); + + // Fill sign-up form with same user details + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", nativeAuthConfig.signInEmailPasswordUsername); // Same email + if (screenshot) { + await screenshot.takeScreenshot(page, "4_signUpFormFilled"); + } + + // Submit sign-up form + await page.waitForSelector("#signUpBtn", { visible: true }); + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "5_signUpAttempted"); + } + + // Phase 3: Verify error message about needing to sign out first + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + if (screenshot) { + await screenshot.takeScreenshot(page, "6_errorBannerDisplayed"); + } + + // Verify error banner content - expect message about being already signed in + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + expect(errorMessage).toMatch(/(sign out|already signed|logged in|user already signed)/i); + }, AUTH_TIMEOUT); + + }); + + describe("Sign Up Flow - Email + OTP - Positive", () => { + beforeEach(async () => { + await page.goto(sampleHomeUrl + `?useOtpConfig=true`); + + // Wait for the application to initialize + await pcaInitializedPoller(page, AUTH_TIMEOUT); + + // Verify sign-up button is visible on the navigation bar + const showSignUpBtn = await page.$("#showSignUpBtn"); + expect(showSignUpBtn).toBeTruthy(); + + // Click sign-up button on the navigation bar + await page.click("#showSignUpBtn"); + + // Verify sign-up card is visible + const signUpCard = await page.$("#signUpCard"); + expect(signUpCard).toBeTruthy(); + + // Verify sign-up form elements are present + const usernameInput = await page.$("#signUpUsername"); + const signUpButton = await page.$("#signUpBtn"); + expect(usernameInput).toBeTruthy(); + expect(signUpButton).toBeTruthy(); + + // Verify the form is visible + const isSignUpCardVisible = await page.evaluate(() => { + const card = document.getElementById("signUpCard"); + return card && window.getComputedStyle(card).display !== "none"; + }); + expect(isSignUpCardVisible).toBe(true); + }); + + it("User enters new email and user attributes, verifies code successfully, completes sign up flow, then automatically sign-in", async () => { + const testName = "signUpOtpSuccessful"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Create a new email inbox using password_provider with retry logic + const emailClient = new MailTmClient(nativeAuthConfig.passwordProvider); + const { address: signUpEmail } = await emailClient.createInbox(); + console.log(`Created email for OTP signup: ${signUpEmail}`); + + // Phase 1: Enter user details and initiate signup + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", signUpEmail); + + // Click sign-up button + await page.waitForSelector("#signUpBtn", { visible: true }); + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "1_signUpButtonClicked"); + + // Phase 2: Wait for OTP verification card (no password step expected) + await page.waitForSelector("#codeVerificationCard", { + visible: true, + timeout: 45000, + }); + await screenshot.takeScreenshot(page, "2_otpVerificationDisplayed"); + + // Phase 3: Login to email and get OTP code + console.log("Logging into email account..."); + await emailClient.login(signUpEmail, nativeAuthConfig.passwordProvider); + console.log("Retrieving OTP code from email..."); + const otpCode = await emailClient.readOtpCode(); + console.log("OTP code retrieved:", otpCode); + + // Phase 4: Enter correct OTP and submit + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", otpCode); + await screenshot.takeScreenshot(page, "3_correctOtpEntered"); + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "4_otpSubmitted"); + + // Phase 5: Wait for automatic sign-in (no password step in OTP-only flow) + await page.waitForFunction( + () => { + const authStatusBanner = document.getElementById("authStatusBanner"); + const isSignedIn = authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 35000 } + ); + await screenshot.takeScreenshot(page, "5_automaticSignInCompleted"); + + // Phase 6: Verify tokens and authentication state + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + + // Verify user is actually signed in + const finalAuthStatus = await page.$eval("#authStatusBanner", (el) => el.textContent); + expect(finalAuthStatus).toContain("Signed in"); + + await screenshot.takeScreenshot(page, "6_signUpOtpCompleted"); + + console.log("OTP signup flow completed successfully with automatic sign-in"); + }, AUTH_TIMEOUT); + + it("User enters new email and user attributes, uses invalid OTP, requests new code, completes sign up flow, then automatically sign-in", async () => { + const testName = "signUpOtpWithResend"; + const screenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/${testName}` + ); + + // Create a new email inbox using password_provider with retry logic + const emailClient = new MailTmClient(nativeAuthConfig.passwordProvider); + const { address: signUpEmail } = await emailClient.createInbox(); + console.log(`Created email for OTP signup with resend: ${signUpEmail}`); + + // Phase 1: Enter user details and initiate signup + await page.waitForSelector("#signUpFirstName", { visible: true }); + await page.waitForSelector("#signUpLastName", { visible: true }); + await page.waitForSelector("#signUpUsername", { visible: true }); + + await page.type("#signUpFirstName", testFirstName); + await page.type("#signUpLastName", testLastName); + await page.type("#signUpUsername", signUpEmail); + + // Click sign-up button + await page.waitForSelector("#signUpBtn", { visible: true }); + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "1_signUpButtonClicked"); + + // Phase 2: Wait for OTP verification card + await page.waitForSelector("#codeVerificationCard", { + visible: true, + timeout: 45000, + }); + await screenshot.takeScreenshot(page, "2_otpVerificationDisplayed"); + + // Phase 3: Enter incorrect OTP and handle error + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", testData.invalidOtpCode); // Incorrect OTP + await screenshot.takeScreenshot(page, "3_incorrectOtpEntered"); + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "4_incorrectOtpSubmitted"); + + // Wait for error banner to appear + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + await screenshot.takeScreenshot(page, "5_errorBannerDisplayed"); + + // Verify error message + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + expect(errorMessage).toContain("Sign-up Error: Error: invalid_grant: AADSTS50181: Unable to validate the otp"); + + // Dismiss error banner + const dismissBtn = await page.$("#dismissErrorBtn"); + if (dismissBtn) { + await page.click("#dismissErrorBtn"); + await screenshot.takeScreenshot(page, "6_errorDismissed"); + } + + // Phase 4: Resend verification code + await page.waitForSelector("#resendCodeBtn", { visible: true }); + await page.click("#resendCodeBtn"); + await screenshot.takeScreenshot(page, "7_resendCodeClicked"); + + // Wait a moment for resend to process + await new Promise(resolve => setTimeout(resolve, 2000)); + + // Phase 5: Login to email and get the new OTP code + console.log("Logging into email account after resend..."); + await emailClient.login(signUpEmail, nativeAuthConfig.passwordProvider); + console.log("Retrieving OTP code from email after resend..."); + const otpCode = await emailClient.readOtpCode(); + console.log("OTP code retrieved after resend:", otpCode); + + // Phase 6: Enter correct OTP and submit + await page.waitForSelector("#verificationCode", { visible: true }); + await page.click("#verificationCode", { clickCount: 3 }); + await page.type("#verificationCode", otpCode); + await screenshot.takeScreenshot(page, "8_correctOtpEntered"); + + await page.waitForSelector("#submitCodeBtn:enabled", { + visible: true, + timeout: 15000, + }); + await page.evaluate(() => { + const submitButton = document.getElementById("submitCodeBtn"); + if (submitButton) { + submitButton.click(); + } else { + throw new Error("Submit OTP button not found in the DOM"); + } + }); + await screenshot.takeScreenshot(page, "9_correctOtpSubmitted"); + + // Phase 7: Wait for automatic sign-in completion (no password step in OTP-only flow) + await page.waitForFunction( + () => { + const authStatusBanner = document.getElementById("authStatusBanner"); + const isSignedIn = authStatusBanner && + authStatusBanner.textContent?.includes("Signed in"); + return isSignedIn; + }, + { timeout: 35000 } + ); + await screenshot.takeScreenshot(page, "10_automaticSignInCompleted"); + + // Phase 8: Verify tokens and authentication state + const tokenStore = await BrowserCache.getTokens(); + expect(tokenStore.idTokens).toHaveLength(1); + expect(tokenStore.accessTokens).toHaveLength(1); + expect(tokenStore.refreshTokens).toHaveLength(1); + expect(await BrowserCache.getAccountFromCache()).toBeDefined(); + expect( + await BrowserCache.accessTokenForScopesExists( + tokenStore.accessTokens, + ["openid", "profile", "user.read"] + ) + ).toBeTruthy(); + + // Verify user is actually signed in + const finalAuthStatus = await page.$eval("#authStatusBanner", (el) => el.textContent); + expect(finalAuthStatus).toContain("Signed in"); + + await screenshot.takeScreenshot(page, "11_signUpOtpFlowCompleted"); + + console.log("OTP signup flow with resend completed successfully with automatic sign-in"); + }, AUTH_TIMEOUT); + + }); + + describe("Sign Up Flow - Redirect", () => { + beforeEach(async () => { + // Use useRedirectConfig=true to ensure the app initializes with redirect-only challenge types + await page.goto(sampleHomeUrl + `?useOtpConfig=true&useRedirectConfig=true`); + + // Wait for the application to initialize with a longer timeout + await pcaInitializedPoller(page, AUTH_TIMEOUT); // Increase timeout for more stability + + // Verify that no user signed in initially + const authStatusBanner = await page.$eval("#authStatusBanner", (el) => el.textContent); + expect(authStatusBanner).toContain("No user signed in"); + + // Take a screenshot of the initialized state + if (testConfig.screenshots.enabled) { + const setupScreenshot = new Screenshot( + `${SCREENSHOT_BASE_FOLDER_NAME}/setup` + ); + await setupScreenshot.takeScreenshot(page, "appInitialized"); + } + + // Verify sign-up button is visible on the navigation bar + const showSignUpBtn = await page.$("#showSignUpBtn"); + expect(showSignUpBtn).toBeTruthy(); + + // Click sign-up button on the navigation bar + await page.click("#showSignUpBtn"); + + // Verify sign-up card is visible + const signUpCard = await page.$("#signUpCard"); + expect(signUpCard).toBeTruthy(); + + // Verify sign-up form elements are present + const usernameInput = await page.$("#signUpUsername"); + const signUpButton = await page.$("#signUpBtn"); + expect(usernameInput).toBeTruthy(); + expect(signUpButton).toBeTruthy(); + }); + + it("User email is registered with email OTP auth method, which is not supported by the developer (redirect flow)", async () => { + const testName = "SignUpOtpWithRedirect"; + let screenshot: Screenshot | undefined; + + if (testConfig.screenshots.enabled) { + screenshot = new Screenshot(`${SCREENSHOT_BASE_FOLDER_NAME}/${testName}`); + } + + // Use mock email - only testing redirect error behavior, no real email needed + const testEmail = generateMockEmail(); + console.log(`Using mock email for redirect test: ${testEmail}`); + + // Enter email in the sign-up form and click sign-up button + await page.waitForSelector("#signUpUsername", { visible: true }); + await page.type("#signUpUsername", testEmail); + + // Make sure sign-up button is visible and clickable + await page.waitForSelector("#signUpBtn", { visible: true }); + + // Use evaluate to click to avoid potential click issues + await page.evaluate(() => { + const signUpButton = document.getElementById("signUpBtn"); + if (signUpButton) { + signUpButton.click(); + } else { + throw new Error("Sign up button not found in the DOM"); + } + }); + if (screenshot) { + await screenshot.takeScreenshot(page, "signUpButtonClicked"); + } + + // Wait for the error banner to appear with increased timeout + await page.waitForSelector("#errorBanner", { visible: true, timeout: 15000 }); + if (screenshot) { + await screenshot.takeScreenshot(page, "errorBannerDisplayed"); + } + + // Verify error banner content + const errorMessage = await page.$eval("#errorMessage", (el) => el.textContent); + expect(errorMessage).toContain("redirect"); + + }, AUTH_TIMEOUT); + }); +}); + +/** + * Generate a mock email address for testing + * Returns a unique, realistic email that will pass validation but doesn't require real email service + * Perfect for negative tests and form validation where no actual email verification is needed + */ +function generateMockEmail(): string { + const timestamp = Date.now(); + const randomSuffix = Math.random().toString(36).substring(7); + return `test-${timestamp}-${randomSuffix}@example.com`; +}