diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea841cf95b..819593506b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: branches: - main - v11.0.0 + - feat/credential-mgr-integration # Add any long-lived feature branches here pull_request: workflow_dispatch: diff --git a/cspell-dictionary.txt b/cspell-dictionary.txt index adce52de71..15767ef3ad 100644 --- a/cspell-dictionary.txt +++ b/cspell-dictionary.txt @@ -29,6 +29,7 @@ autoloaded autoscale autovacuum awscli +badheaders barsize baseport bindkey diff --git a/package-lock.json b/package-lock.json index c223768484..34b17bfb60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,11 @@ "version": "11.4.0", "license": "ISC", "dependencies": { - "@heroku-cli/command": "^12.3.3", + "@heroku-cli/command": "12.4.0", "@heroku-cli/notifications": "^1.2.6", "@heroku-cli/schema": "^1.0.25", "@heroku/buildpack-registry": "^1.0.1", - "@heroku/heroku-cli-util": "^10.8.0", + "@heroku/heroku-cli-util": "10.8.1", "@heroku/http-call": "^5.5.1", "@heroku/mcp-server": "^1.2.0", "@heroku/socksv5": "^0.0.9", @@ -210,23 +210,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@apm-js-collab/code-transformer": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.8.2.tgz", - "integrity": "sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==", - "license": "Apache-2.0" - }, - "node_modules/@apm-js-collab/tracing-hooks": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.3.1.tgz", - "integrity": "sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==", - "license": "Apache-2.0", - "dependencies": { - "@apm-js-collab/code-transformer": "^0.8.0", - "debug": "^4.4.1", - "module-details-from-path": "^1.0.4" - } - }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", @@ -2287,18 +2270,21 @@ } }, "node_modules/@heroku-cli/command": { - "version": "12.3.3", - "resolved": "https://registry.npmjs.org/@heroku-cli/command/-/command-12.3.3.tgz", - "integrity": "sha512-JXgzzpJS6WdNf51FqaqzjA2ydNUw6fnGCFod8CbHofTKYgveIdTUQxXQJdInLKeRLPIoogEOZs1/inJTsT9zkg==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/@heroku-cli/command/-/command-12.4.0.tgz", + "integrity": "sha512-nFzAnFezznL7rSDLbG6y9q2C65pkErOb5fQBgkcODbtl76ZjXHfoETi21AbAi/NXOf3N2HaPTaJVSlynLRSkTA==", "license": "ISC", "dependencies": { "@heroku/http-call": "^5.4.0", + "@heroku/js-blanket": "^1.0.0", "@oclif/core": "^4.3.0", + "@sentry/node": "^10.45.0", "ansis": "^4", "debug": "^4.4.0", + "execa": "^9.6.1", "inquirer": "^12.11.1", - "netrc-parser": "^3.1.6", "open": "^11.0.0", + "tsheredoc": "^1.0.1", "yargs-parser": "^20.2.9", "yargs-unparser": "^2.0.0" }, @@ -2466,12 +2452,12 @@ "license": "MIT" }, "node_modules/@heroku/heroku-cli-util": { - "version": "10.8.0", - "resolved": "https://registry.npmjs.org/@heroku/heroku-cli-util/-/heroku-cli-util-10.8.0.tgz", - "integrity": "sha512-/P6eQPU6TaI/BY9+56NnAOPl9VSCo19kwk0CC2um2xDMiMA38Eyzz+wSo0sNxHeSLAIoDPM6n40VagVxwsN/tQ==", + "version": "10.8.1", + "resolved": "https://registry.npmjs.org/@heroku/heroku-cli-util/-/heroku-cli-util-10.8.1.tgz", + "integrity": "sha512-tlC5lyyPazN7axWceDHb5N+D8MIk3o37BnAsjlWYo0fAeUOXWD0BibgAD/OpnaaM476OgtQaI7jjDj0BPCf2TQ==", "license": "ISC", "dependencies": { - "@heroku-cli/command": "^12.2.0", + "@heroku-cli/command": "^12.4.0", "@heroku/http-call": "^5.5.0", "@oclif/core": "^4.3.0", "@oclif/table": "0.5.2", @@ -2590,6 +2576,18 @@ "node": ">=16.0.0" } }, + "node_modules/@heroku/js-blanket": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@heroku/js-blanket/-/js-blanket-1.0.0.tgz", + "integrity": "sha512-75fV7zmehSgHuXXfTqRZdbJn2wADi1U6Ext/A4WFcRrsiUeHnD0peCMZQixfyCrvMPA/06+QoUnJlnHIBMkJFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@heroku/mcp-server": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@heroku/mcp-server/-/mcp-server-1.2.0.tgz", @@ -3943,9 +3941,9 @@ "license": "MIT" }, "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", + "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", "license": "Apache-2.0", "engines": { "node": ">=8.0.0" @@ -3963,22 +3961,10 @@ "node": ">=14" } }, - "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.5.1.tgz", - "integrity": "sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, "node_modules/@opentelemetry/core": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.1.tgz", - "integrity": "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.1.tgz", + "integrity": "sha512-QAqIj32AtK6+pEVNG7EOVxHdE06RP+FM5qpiEJ4RtDcFIqKUZHYhl7/7UY5efhwmwNAg7j8QbJVBLxMerc0+gw==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -4061,1731 +4047,29 @@ "version": "1.25.1", "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz", "integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@opentelemetry/instrumentation": { - "version": "0.52.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", - "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.52.1", - "@types/shimmer": "^1.0.2", - "import-in-the-middle": "^1.8.1", - "require-in-the-middle": "^7.1.1", - "semver": "^7.5.2", - "shimmer": "^1.2.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.58.0.tgz", - "integrity": "sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-amqplib/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-amqplib/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.54.0.tgz", - "integrity": "sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.27.0", - "@types/connect": "3.4.38" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-connect/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader": { - "version": "0.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.28.0.tgz", - "integrity": "sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-dataloader/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-express/-/instrumentation-express-0.59.0.tgz", - "integrity": "sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-express/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-express/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-express/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.30.0.tgz", - "integrity": "sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-fs/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-fs/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-fs/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.54.0.tgz", - "integrity": "sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-generic-pool/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.58.0.tgz", - "integrity": "sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-graphql/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-graphql/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.57.0.tgz", - "integrity": "sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-hapi/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-hapi/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.211.0.tgz", - "integrity": "sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.5.0", - "@opentelemetry/instrumentation": "0.211.0", - "@opentelemetry/semantic-conventions": "^1.29.0", - "forwarded-parse": "2.1.2" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/core": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz", - "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-http/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.59.0.tgz", - "integrity": "sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/redis-common": "^0.38.2", - "@opentelemetry/semantic-conventions": "^1.33.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-ioredis/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.20.0.tgz", - "integrity": "sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.30.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-kafkajs/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex": { - "version": "0.55.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.55.0.tgz", - "integrity": "sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.1" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-knex/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-knex/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-knex/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.59.0.tgz", - "integrity": "sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.36.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-koa/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-koa/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-koa/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer": { - "version": "0.55.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.55.0.tgz", - "integrity": "sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-lru-memoizer/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.64.0.tgz", - "integrity": "sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-mongodb/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.57.0.tgz", - "integrity": "sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-mongoose/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.57.0.tgz", - "integrity": "sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0", - "@types/mysql": "2.15.27" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-mysql/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.57.0.tgz", - "integrity": "sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0", - "@opentelemetry/sql-common": "^0.41.2" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-mysql2/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg": { - "version": "0.63.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.63.0.tgz", - "integrity": "sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.34.0", - "@opentelemetry/sql-common": "^0.41.2", - "@types/pg": "8.15.6", - "@types/pg-pool": "2.0.7" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-pg/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-pg/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-pg/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis": { - "version": "0.59.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-redis/-/instrumentation-redis-0.59.0.tgz", - "integrity": "sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/redis-common": "^0.38.2", - "@opentelemetry/semantic-conventions": "^1.27.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-redis/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-redis/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-redis/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.30.0.tgz", - "integrity": "sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.33.0", - "@types/tedious": "^4.0.14" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-tedious/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@opentelemetry/instrumentation-tedious/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.21.0.tgz", - "integrity": "sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/semantic-conventions": "^1.24.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.7.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" + "license": "Apache-2.0", + "engines": { + "node": ">=14" } }, - "node_modules/@opentelemetry/instrumentation-undici/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", + "node_modules/@opentelemetry/instrumentation": { + "version": "0.52.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz", + "integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==", + "license": "Apache-2.0", "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" + "@opentelemetry/api-logs": "0.52.1", + "@types/shimmer": "^1.0.2", + "import-in-the-middle": "^1.8.1", + "require-in-the-middle": "^7.1.1", + "semver": "^7.5.2", + "shimmer": "^1.2.1" }, "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, "node_modules/@opentelemetry/otlp-exporter-base": { @@ -5984,15 +4268,6 @@ "node": ">=14" } }, - "node_modules/@opentelemetry/redis-common": { - "version": "0.38.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz", - "integrity": "sha512-1BCcU93iwSRZvDAgwUxC/DV4T/406SkMfxGqu5ojc3AvNI+I9GhV7v0J1HljsczuuhcnFLYqD5VmwVXfCGHzxA==", - "license": "Apache-2.0", - "engines": { - "node": "^18.19.0 || >=20.6.0" - } - }, "node_modules/@opentelemetry/resources": { "version": "1.30.1", "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", @@ -6245,29 +4520,14 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", - "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", + "version": "1.41.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", + "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", "license": "Apache-2.0", "engines": { "node": ">=14" } }, - "node_modules/@opentelemetry/sql-common": { - "version": "0.41.2", - "resolved": "https://registry.npmjs.org/@opentelemetry/sql-common/-/sql-common-0.41.2.tgz", - "integrity": "sha512-4mhWm3Z8z+i508zQJ7r6Xi7y4mmoJpdvH0fZPFRkWrdp5fq7hhZ2HhYokEOLkfqSMgPR4Z9EyB3DBkbKGOqZiQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "^2.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -6320,78 +4580,6 @@ "node": ">=12" } }, - "node_modules/@prisma/instrumentation": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@prisma/instrumentation/-/instrumentation-7.2.0.tgz", - "integrity": "sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/instrumentation": "^0.207.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.8" - } - }, - "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/api-logs": { - "version": "0.207.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.207.0.tgz", - "integrity": "sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@prisma/instrumentation/node_modules/@opentelemetry/instrumentation": { - "version": "0.207.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.207.0.tgz", - "integrity": "sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.207.0", - "import-in-the-middle": "^2.0.0", - "require-in-the-middle": "^8.0.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "node_modules/@prisma/instrumentation/node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", - "license": "MIT" - }, - "node_modules/@prisma/instrumentation/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^2.2.0", - "module-details-from-path": "^1.0.4" - } - }, - "node_modules/@prisma/instrumentation/node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -6470,64 +4658,38 @@ "license": "MIT" }, "node_modules/@sentry/core": { - "version": "10.38.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.38.0.tgz", - "integrity": "sha512-1pubWDZE5y5HZEPMAZERP4fVl2NH3Ihp1A+vMoVkb3Qc66Diqj1WierAnStlZP7tCx0TBa0dK85GTW/ZFYyB9g==", + "version": "10.55.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.55.0.tgz", + "integrity": "sha512-XUyoNtDSYCvgJnoNzlh+YeAXfIPhCRIXbhWqqM3GQ3AFtZICi85lkyfsrwXEl9wzlPGYnU+Eg8F4tOfScx+FcQ==", "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@sentry/node": { - "version": "10.38.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.38.0.tgz", - "integrity": "sha512-wriyDtWDAoatn8EhOj0U4PJR1WufiijTsCGALqakOHbFiadtBJANLe6aSkXoXT4tegw59cz1wY4NlzHjYksaPw==", + "version": "10.55.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.55.0.tgz", + "integrity": "sha512-+fB/ByoHVWPLGgoafYciiMatTNyX1FHj1bsqZBN+Pw3McbuEU1nwCPLt9zuyZZiWlQtXKsyuACS4ZhXnID5l8A==", "license": "MIT", "dependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^2.5.0", - "@opentelemetry/core": "^2.5.0", - "@opentelemetry/instrumentation": "^0.211.0", - "@opentelemetry/instrumentation-amqplib": "0.58.0", - "@opentelemetry/instrumentation-connect": "0.54.0", - "@opentelemetry/instrumentation-dataloader": "0.28.0", - "@opentelemetry/instrumentation-express": "0.59.0", - "@opentelemetry/instrumentation-fs": "0.30.0", - "@opentelemetry/instrumentation-generic-pool": "0.54.0", - "@opentelemetry/instrumentation-graphql": "0.58.0", - "@opentelemetry/instrumentation-hapi": "0.57.0", - "@opentelemetry/instrumentation-http": "0.211.0", - "@opentelemetry/instrumentation-ioredis": "0.59.0", - "@opentelemetry/instrumentation-kafkajs": "0.20.0", - "@opentelemetry/instrumentation-knex": "0.55.0", - "@opentelemetry/instrumentation-koa": "0.59.0", - "@opentelemetry/instrumentation-lru-memoizer": "0.55.0", - "@opentelemetry/instrumentation-mongodb": "0.64.0", - "@opentelemetry/instrumentation-mongoose": "0.57.0", - "@opentelemetry/instrumentation-mysql": "0.57.0", - "@opentelemetry/instrumentation-mysql2": "0.57.0", - "@opentelemetry/instrumentation-pg": "0.63.0", - "@opentelemetry/instrumentation-redis": "0.59.0", - "@opentelemetry/instrumentation-tedious": "0.30.0", - "@opentelemetry/instrumentation-undici": "0.21.0", - "@opentelemetry/resources": "^2.5.0", - "@opentelemetry/sdk-trace-base": "^2.5.0", - "@opentelemetry/semantic-conventions": "^1.39.0", - "@prisma/instrumentation": "7.2.0", - "@sentry/core": "10.38.0", - "@sentry/node-core": "10.38.0", - "@sentry/opentelemetry": "10.38.0", - "import-in-the-middle": "^2.0.6", - "minimatch": "^9.0.0" + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/core": "^2.6.1", + "@opentelemetry/instrumentation": "^0.214.0", + "@opentelemetry/sdk-trace-base": "^2.6.1", + "@opentelemetry/semantic-conventions": "^1.40.0", + "@sentry/core": "10.55.0", + "@sentry/node-core": "10.55.0", + "@sentry/opentelemetry": "10.55.0", + "import-in-the-middle": "^3.0.0" }, "engines": { "node": ">=18" } }, "node_modules/@sentry/node/node_modules/@opentelemetry/api-logs": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz", - "integrity": "sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==", + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", + "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -6536,14 +4698,35 @@ "node": ">=8.0.0" } }, + "node_modules/@sentry/node/node_modules/@opentelemetry/exporter-trace-otlp-http": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.218.0.tgz", + "integrity": "sha512-8dqezsmPhtKitIK/eTipZhYl9EX2/gNQ5zUMhaz3uxEURwfkNf8IPvo6yNfrzbxdtpAOybS/+h7wmIWYqFSpiw==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, "node_modules/@sentry/node/node_modules/@opentelemetry/instrumentation": { - "version": "0.211.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.211.0.tgz", - "integrity": "sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==", + "version": "0.214.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.214.0.tgz", + "integrity": "sha512-MHqEX5Dk59cqVah5LiARMACku7jXSVk9iVDWOea4x3cr7VfdByeDCURK6o1lntT1JS/Tsovw01UJrBhN3/uC5w==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.211.0", - "import-in-the-middle": "^2.0.0", + "@opentelemetry/api-logs": "0.214.0", + "import-in-the-middle": "^3.0.0", "require-in-the-middle": "^8.0.0" }, "engines": { @@ -6553,13 +4736,67 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@sentry/node/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.218.0.tgz", + "integrity": "sha512-ZwqpkNL5W7RyGJPDZ9g06DvKp8KFTWPJPN12anpMQYSKpTSU0z3EIZuPq9vPGpS8siFyOqDYDAuCwlNO9FqgbA==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-transformer": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@sentry/node/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.218.0.tgz", + "integrity": "sha512-CFaKH87WAzjuJ4awowTTLzUvMfaRfiOFG5+qm5S5ncyalRtN4ecQ+YmuANJSCrVPuvZFEkUgKhBPBndxi3rHsQ==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@sentry/node/node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/api-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.218.0.tgz", + "integrity": "sha512-fmEWp5kXlGEc3i/lR698Hz41DfGyN4Tbe4g7L1AxSc7fF8Xeh/FQ9Quqpa9dVA413Q1Ad43QOLzU4JoXgbFPWw==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/@sentry/node/node_modules/@opentelemetry/resources": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.1.tgz", - "integrity": "sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.1.tgz", + "integrity": "sha512-DeT6KKolmC4e/dRQvMQ/RwlnzhaqeiFOXY5ngoOPJ07GgVVKxZOg9EcrNZb5aTzUn+iCrJldAgOfQm1O/QfPAQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.5.1", + "@opentelemetry/core": "2.7.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -6569,14 +4806,66 @@ "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, + "node_modules/@sentry/node/node_modules/@opentelemetry/sdk-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.218.0.tgz", + "integrity": "sha512-QvnNdugatFTVCJXH0Mcu7GOOJSylA9j127kIezOE4YwTI4YbowRons2K4WZTv5FMS8T4q9P0NdaRHdkSmeAIag==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@sentry/node/node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/api-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.218.0.tgz", + "integrity": "sha512-fmEWp5kXlGEc3i/lR698Hz41DfGyN4Tbe4g7L1AxSc7fF8Xeh/FQ9Quqpa9dVA413Q1Ad43QOLzU4JoXgbFPWw==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@sentry/node/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, "node_modules/@sentry/node/node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.5.1.tgz", - "integrity": "sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.5.1", - "@opentelemetry/resources": "2.5.1", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -6587,27 +4876,45 @@ } }, "node_modules/@sentry/node/node_modules/@sentry/node-core": { - "version": "10.38.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.38.0.tgz", - "integrity": "sha512-ErXtpedrY1HghgwM6AliilZPcUCoNNP1NThdO4YpeMq04wMX9/GMmFCu46TnCcg6b7IFIOSr2S4yD086PxLlHQ==", + "version": "10.55.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.55.0.tgz", + "integrity": "sha512-M8XMMIk9Y0PGZoEt37Oe5dQCdqDdJlBcwLXidpz/s5k4QtJvCO/BbtcivcuKI2htw5FwxJkSrHUzRvT36tlDpg==", "license": "MIT", "dependencies": { - "@apm-js-collab/tracing-hooks": "^0.3.1", - "@sentry/core": "10.38.0", - "@sentry/opentelemetry": "10.38.0", - "import-in-the-middle": "^2.0.6" + "@sentry/core": "10.55.0", + "@sentry/opentelemetry": "10.55.0", + "import-in-the-middle": "^3.0.0" }, "engines": { "node": ">=18" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/resources": "^1.30.1 || ^2.1.0", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", "@opentelemetry/semantic-conventions": "^1.39.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/core": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-http": { + "optional": true + }, + "@opentelemetry/instrumentation": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "@opentelemetry/semantic-conventions": { + "optional": true + } } }, "node_modules/@sentry/node/node_modules/cjs-module-lexer": { @@ -6617,15 +4924,18 @@ "license": "MIT" }, "node_modules/@sentry/node/node_modules/import-in-the-middle": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-2.0.6.tgz", - "integrity": "sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz", + "integrity": "sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA==", "license": "Apache-2.0", "dependencies": { "acorn": "^8.15.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" + }, + "engines": { + "node": ">=18" } }, "node_modules/@sentry/node/node_modules/require-in-the-middle": { @@ -6642,19 +4952,18 @@ } }, "node_modules/@sentry/opentelemetry": { - "version": "10.38.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.38.0.tgz", - "integrity": "sha512-YPVhWfYmC7nD3EJqEHGtjp4fp5LwtAbE5rt9egQ4hqJlYFvr8YEz9sdoqSZxO0cZzgs2v97HFl/nmWAXe52G2Q==", + "version": "10.55.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.55.0.tgz", + "integrity": "sha512-0+YrNmVNrttki4rWP4DW+UTt5MziepwDLNBde39tgc3cGCcy5fLSdDfhb4JfTaE5TXt4kd5XrkgvS/sDgm3RZg==", "license": "MIT", "dependencies": { - "@sentry/core": "10.38.0" + "@sentry/core": "10.55.0" }, "engines": { "node": ">=18" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.1.0", "@opentelemetry/core": "^1.30.1 || ^2.1.0", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0", "@opentelemetry/semantic-conventions": "^1.39.0" @@ -7693,15 +6002,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -7885,15 +6185,6 @@ "@types/node": "*" } }, - "node_modules/@types/mysql": { - "version": "2.15.27", - "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.27.tgz", - "integrity": "sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/node": { "version": "22.16.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.5.tgz", @@ -7940,26 +6231,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/pg": { - "version": "8.15.6", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.6.tgz", - "integrity": "sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/pg-pool": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@types/pg-pool/-/pg-pool-2.0.7.tgz", - "integrity": "sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==", - "license": "MIT", - "dependencies": { - "@types/pg": "*" - } - }, "node_modules/@types/phoenix": { "version": "1.6.7", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", @@ -8072,15 +6343,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/tedious": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/@types/tedious/-/tedious-4.0.14.tgz", - "integrity": "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/through": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", @@ -14130,12 +12392,6 @@ "node": ">= 0.6" } }, - "node_modules/forwarded-parse": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", - "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", - "license": "MIT" - }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", @@ -17260,6 +15516,7 @@ "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.2" @@ -20619,37 +18876,6 @@ "node": ">= 14.16" } }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz", - "integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/phoenix": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/phoenix/-/phoenix-1.8.3.tgz", @@ -20740,45 +18966,6 @@ "node": ">= 0.4" } }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz", - "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/powershell-utils": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", @@ -24088,6 +22275,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4" diff --git a/package.json b/package.json index 865738eb58..e6034d60e6 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "bin": "./bin/run.js", "bugs": "https://github.com/heroku/cli/issues", "dependencies": { - "@heroku-cli/command": "^12.3.3", + "@heroku-cli/command": "12.4.0", "@heroku-cli/notifications": "^1.2.6", "@heroku-cli/schema": "^1.0.25", "@heroku/buildpack-registry": "^1.0.1", - "@heroku/heroku-cli-util": "^10.8.0", + "@heroku/heroku-cli-util": "10.8.1", "@heroku/http-call": "^5.5.1", "@heroku/mcp-server": "^1.2.0", "@heroku/socksv5": "^0.0.9", diff --git a/src/commands/accounts/add.ts b/src/commands/accounts/add.ts index 0fbad2d38a..e9aadc0b99 100644 --- a/src/commands/accounts/add.ts +++ b/src/commands/accounts/add.ts @@ -7,32 +7,29 @@ import AccountsModule from '../../lib/accounts/accounts.js' export default class Add extends Command { static args = { - name: Args.string({description: 'name of Heroku account to add', required: true}), + name: Args.string({description: 'alias for Heroku account to add', required: true}), } - static description = 'add a Heroku account to your cache' + static description = 'add the current Heroku account to your accounts cache' static example = `${color.command('heroku accounts:add my-account')}` async run() { const {args} = await this.parse(Add) const {name} = args - const logInMessage = 'You must be logged in to run this command.' + const accounts = await AccountsModule.list() - if (AccountsModule.list().some(a => a.name === name)) { + if (accounts.some(account => account.name === name)) { ux.error(`${name} already exists`) } const {body: account} = await this.heroku.get('/account') - const email = account.email || '' + const email = account.email! - const token = this.heroku.auth || '' - - if (token === '') { - ux.error(logInMessage) + const existingAlias = accounts.find(account => account.name && account.username === email) + if (existingAlias) { + ux.error(`Account ${email} already has an alias of ${existingAlias.name}.`) } - if (email === '') { - ux.error(logInMessage) - } + const token = this.heroku.auth! AccountsModule.add(name, email, token) } diff --git a/src/commands/accounts/current.ts b/src/commands/accounts/current.ts index 7b88043c6f..273ce71447 100644 --- a/src/commands/accounts/current.ts +++ b/src/commands/accounts/current.ts @@ -11,11 +11,12 @@ export default class Current extends Command { static promptFlagActive = false async run() { - const accountName = await AccountsModule.current() + const accountName = await AccountsModule.current(this.heroku) if (accountName) { hux.styledHeader(`Current account is ${color.user(accountName)}`) } else { - ux.error(`You haven't set an account. Run ${color.code('heroku accounts:add ')} to add an account to your cache or ${color.code('heroku accounts:set ')} to set the current account.`) + ux.error(`You haven't set an account.\n + Run ${color.code('heroku accounts:add ')} to add an account to your cache or ${color.code('heroku accounts:set ')} to set the current account.`) } } } diff --git a/src/commands/accounts/index.ts b/src/commands/accounts/index.ts index 9824f4132f..ae2558cc18 100644 --- a/src/commands/accounts/index.ts +++ b/src/commands/accounts/index.ts @@ -11,16 +11,17 @@ export default class AccountsIndex extends Command { static promptFlagActive = false async run() { - const accounts = accountsModule.list() + const accounts = await accountsModule.list() if (accounts.length === 0) { ux.error('You don\'t have any accounts in your cache.') } + const current = await accountsModule.current(this.heroku) for (const account of accounts) { - if (account.name === await accountsModule.current()) { - ux.stdout(`* ${account.name}`) + if (account.name === current || account.username === current) { + ux.stdout(`* ${account.name ?? account.username}`) } else { - ux.stdout(` ${account.name}`) + ux.stdout(` ${account.name ?? account.username}`) } } } diff --git a/src/commands/accounts/remove.ts b/src/commands/accounts/remove.ts index 4f976fcce7..fe1df2459c 100644 --- a/src/commands/accounts/remove.ts +++ b/src/commands/accounts/remove.ts @@ -15,14 +15,19 @@ export default class Remove extends Command { const {args} = await this.parse(Remove) const {name} = args - if (!AccountsModule.list().some(a => a.name === name)) { + const accounts = await AccountsModule.list() + const account = accounts.find(a => a.name === name || a.username === name) + + if (!account) { ux.error(`${name} doesn't exist in your accounts cache.`) } - if (await AccountsModule.current() === name) { - ux.error(`${name} is the current account.`) + const currentAccount = await AccountsModule.current(this.heroku) + // Check both alias (name) and email (username) against current account + if (currentAccount === name || currentAccount === account.username) { + ux.error(`${name} is the current account. To log out, run ${color.command('heroku logout')}.`) } - AccountsModule.remove(name) + await AccountsModule.remove(name) } } diff --git a/src/commands/accounts/set.ts b/src/commands/accounts/set.ts index 98eb1bdda3..a4eceef774 100644 --- a/src/commands/accounts/set.ts +++ b/src/commands/accounts/set.ts @@ -6,19 +6,22 @@ import AccountsModule from '../../lib/accounts/accounts.js' export default class Set extends Command { static args = { - name: Args.string({description: 'name of account to set', required: true}), + name: Args.string({description: 'name or username of account to set', required: true}), } - static description = 'set the current Heroku account from your cache' + static description = 'set the current Heroku account from your accounts cache' static example = `${color.command('heroku accounts:set my-account')}` async run() { const {args} = await this.parse(Set) const {name} = args - if (!AccountsModule.list().some(a => a.name === name)) { - ux.error(`${name} does not exist in your accounts cache.`) + const accounts = await AccountsModule.list() + const account = accounts.find(account => account.name === name || account.username === name) + + if (!account) { + ux.error(`${name} doesn't exist in your accounts cache.`) } - AccountsModule.set(name) + await AccountsModule.set(account, this.config.dataDir) } } diff --git a/src/commands/apps/create.ts b/src/commands/apps/create.ts index dcc93d0067..e04b2b962e 100644 --- a/src/commands/apps/create.ts +++ b/src/commands/apps/create.ts @@ -90,6 +90,7 @@ async function configureGitRemote(context: Interfaces.ParserOutput, app: Heroku. const remoteUrl = git.httpGitUrl(app.name || '') if (!context.flags['no-remote'] && git.inGitRepo()) { await git.createRemote(context.flags.remote || 'heroku', remoteUrl) + await git.configureCredentialHelper() } return remoteUrl diff --git a/src/commands/auth/login.ts b/src/commands/auth/login.ts index 88b9e9156f..90844d9138 100644 --- a/src/commands/auth/login.ts +++ b/src/commands/auth/login.ts @@ -2,6 +2,8 @@ import {Command, flags} from '@heroku-cli/command' import * as Heroku from '@heroku-cli/schema' import * as color from '@heroku/heroku-cli-util/color' +import Git from '../../lib/git/git.js' + export default class Login extends Command { static aliases = ['login'] static description = 'login with your Heroku credentials' @@ -19,6 +21,15 @@ export default class Login extends Command { await this.heroku.login({browser: flags.browser, expiresIn: flags['expires-in'], method}) const {body: account} = await this.heroku.get('/account', {retryAuth: false}) this.log(`Logged in as ${color.user(account.email!)}`) + + const git = new Git() + try { + await git.configureCredentialHelper() + await git.eraseCredentials() + } catch { + // ignore + } + await this.config.runHook('recache', {type: 'login'}) } } diff --git a/src/commands/auth/logout.ts b/src/commands/auth/logout.ts index a52814ca48..2cd439a9b8 100644 --- a/src/commands/auth/logout.ts +++ b/src/commands/auth/logout.ts @@ -1,7 +1,9 @@ - import {Command} from '@heroku-cli/command' import {ux} from '@oclif/core/ux' +import AccountsModule from '../../lib/accounts/accounts.js' +import Git from '../../lib/git/git.js' + export default class Logout extends Command { static aliases = ['logout'] static baseFlags = Command.baseFlagsWithoutPrompt() @@ -9,10 +11,24 @@ export default class Logout extends Command { static promptFlagActive = false async run() { - this.parse(Logout) + await this.parse(Logout) ux.action.start('Logging out') + const cachedNetrcAccount = await AccountsModule.currentNetrc() await this.heroku.logout() + + const git = new Git() + try { + await git.removeCredentialHelper() + await git.eraseCredentials() + } catch { + // ignore + } + + if (cachedNetrcAccount) { + await AccountsModule.remove(cachedNetrcAccount) + } + await this.config.runHook('recache', {type: 'logout'}) ux.action.stop() } diff --git a/src/commands/auth/whoami.ts b/src/commands/auth/whoami.ts index 72dcaf375d..fa9c25f032 100644 --- a/src/commands/auth/whoami.ts +++ b/src/commands/auth/whoami.ts @@ -18,7 +18,7 @@ export default class AuthWhoami extends Command { const {body: account} = await this.heroku.get('/account', {retryAuth: false}) this.log(account.email) } catch (error: any) { - if (error.statusCode === 401) this.notloggedin() + if (error.http.statusCode === 401) this.notloggedin() throw error } } diff --git a/src/commands/ci/config/unset.ts b/src/commands/ci/config/unset.ts index 6ded223497..f807413a6b 100644 --- a/src/commands/ci/config/unset.ts +++ b/src/commands/ci/config/unset.ts @@ -35,7 +35,7 @@ export default class CiConfigUnset extends Command { await ux.action.start(`Unsetting ${Object.keys(vars).join(', ')}`) - setPipelineConfigVars(this.heroku, pipeline.id, vars) + await setPipelineConfigVars(this.heroku, pipeline.id, vars) ux.action.stop() } diff --git a/src/commands/git/clone.ts b/src/commands/git/clone.ts index 17d9d18b92..cc5b665483 100644 --- a/src/commands/git/clone.ts +++ b/src/commands/git/clone.ts @@ -28,5 +28,6 @@ remote: Counting objects: 42, done. const directory = args.DIRECTORY || (app.name as string) const remote = flags.remote || 'heroku' await git.spawn(['clone', '-o', remote, git.url(app.name!), directory]) + await git.configureCredentialHelper() } } diff --git a/src/commands/git/credentials.ts b/src/commands/git/credentials.ts index 50909b5ee9..f1c21076b0 100644 --- a/src/commands/git/credentials.ts +++ b/src/commands/git/credentials.ts @@ -1,5 +1,6 @@ -import {Command} from '@heroku-cli/command' +import {Command, vars} from '@heroku-cli/command' import {Args, ux} from '@oclif/core' +import * as readline from 'node:readline' export class GitCredentials extends Command { static args = { @@ -14,14 +15,24 @@ export class GitCredentials extends Command { case 'erase': // eslint-ignore-next-line no-fallthrough case 'store': { - // ignore + // ignore break } case 'get': { - if (!this.heroku.auth) throw new Error('not logged in') + const {host, protocol} = await this.readInput() + + const {httpGitHost} = vars + if (protocol !== 'https' || host !== httpGitHost) { + return + } + + if (!this.heroku.auth) { + throw new Error('not logged in') + } + ux.stdout(`protocol=https -host=git.heroku.com +host=${httpGitHost} username=heroku password=${this.heroku.auth}`) break @@ -32,4 +43,37 @@ password=${this.heroku.auth}`) } } } + + /** + * Reads git-credential input from stdin + * Format: key=value pairs, one per line, terminated by blank line + * Returns parsed object with protocol, host, username, and path + */ + private async readInput(): Promise<{host?: string; path?: string; protocol?: string; username?: string;}> { + return new Promise(resolve => { + const rl = readline.createInterface({ + input: process.stdin, + terminal: false, + }) + + const input: Record = {} + + rl.on('line', (line: string) => { + if (!line.trim()) { + rl.close() + return + } + + const [key, value] = line.split('=', 2) + if (key && value) { + input[key] = value + } + }) + + rl.on('close', () => { + process.stdin.pause() + resolve(input) + }) + }) + } } diff --git a/src/commands/git/remote.ts b/src/commands/git/remote.ts index 8a831cf587..41e0864479 100644 --- a/src/commands/git/remote.ts +++ b/src/commands/git/remote.ts @@ -40,5 +40,7 @@ ${color.command('heroku git:remote --remote heroku-staging -a example-staging')} const newRemote = await git.remoteUrl(remote) this.log(`set git remote ${color.cyan(remote)} to ${color.cyan(newRemote)}`) + + await git.configureCredentialHelper() } } diff --git a/src/lib/accounts/accounts.ts b/src/lib/accounts/accounts.ts index 35e8d596c4..d51dced93d 100644 --- a/src/lib/accounts/accounts.ts +++ b/src/lib/accounts/accounts.ts @@ -1,34 +1,53 @@ +import { + APIClient, + getStorageConfig, + writeLoginState, +} from '@heroku-cli/command' +import {removeAuth} from '@heroku-cli/command/lib/credential-manager.js' import * as Heroku from '@heroku-cli/schema' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import {parse, stringify} from 'yaml' +export interface AccountEntry { + name?: string + username: string +} + export interface IAccountsWrapper { add(name: string, username: string, password: string): void - current(): Promise - list(): [] | Heroku.Account[] + current(heroku: APIClient): Promise + currentNetrc(): Promise + getStorageConfig(): ReturnType + list(): AccountEntry[] remove(name: string): void - set(name: string): Promise + set(account: AccountEntry, dataDir: string): Promise + writeLoginState(dataDir: string, name: string): Promise } export class AccountsWrapper implements IAccountsWrapper { private netrc: any add(name: string, username: string, password: string): void { - const basedir = path.join(this.configDir(), 'accounts') - fs.mkdirSync(basedir, {recursive: true}) - - fs.writeFileSync( - path.join(basedir, name), - // eslint-disable-next-line perfectionist/sort-objects - stringify({username, password}), - 'utf8', - ) - fs.chmodSync(path.join(basedir, name), 0o600) + fs.mkdirSync(this.accountsDir(), {recursive: true}) + + // eslint-disable-next-line perfectionist/sort-objects + this.writeAccountFile(name, {username, password}) } - async current(): Promise { + async current(heroku: APIClient): Promise { + const config = this.getStorageConfig() + if (config.credentialStore) { + const authEntry = await heroku.getAuthEntry() + const current = this.list().find(a => a.username === authEntry?.account) + return current && current.name ? current.name : null + } + + return this.currentNetrc() + } + + async currentNetrc(): Promise { const netrcInstance = await this.initNetrc() if (netrcInstance.machines['api.heroku.com']) { const current = this.list().find(a => a.username === netrcInstance.machines['api.heroku.com'].login) @@ -38,44 +57,76 @@ export class AccountsWrapper implements IAccountsWrapper { return null } - list(): [] | Heroku.Account[] { - const basedir = path.join(this.configDir(), 'accounts') + getStorageConfig() { + return getStorageConfig() + } + + list(): AccountEntry[] { + const basedir = this.accountsDir() try { return fs.readdirSync(basedir) - .map(name => Object.assign(this.account(name), {name})) + .filter(name => this.account(name).username) + .map(name => ({name, username: this.account(name).username})) } catch { return [] } } - remove(name: string): void { - const basedir = path.join(this.configDir(), 'accounts') - fs.unlinkSync(path.join(basedir, name)) + async remove(name: string): Promise { + const config = this.getStorageConfig() + + if (config.credentialStore) { + // Keychain mode + const email = this.getAliasEmail(name) + await removeAuth(email, ['api.heroku.com', 'git.heroku.com']) + fs.unlinkSync(path.join(this.accountsDir(), name)) + return + } + + // Netrc mode + fs.unlinkSync(path.join(this.accountsDir(), name)) + } + + async set(account: AccountEntry, dataDir: string): Promise { + const config = this.getStorageConfig() + + if (account.name) { + if (config.credentialStore) { + const email = this.getAliasEmail(account.name) + if (email) { + await this.writeLoginState(dataDir, email) + } + } + + const netrcInstance = await this.initNetrc() + let current + try { + current = this.account(account.name) + } catch { + throw new Error(`We can't find the alias file for ${account.name}.`) + } + + netrcInstance.machines['git.heroku.com'] = {login: current.username, password: current.password || ''} + netrcInstance.machines['api.heroku.com'] = {login: current.username, password: current.password || ''} + await netrcInstance.save() + } } - async set(name: string): Promise { - const netrcInstance = await this.initNetrc() - const current = this.account(name) - netrcInstance.machines['git.heroku.com'] = {login: current.username, password: current.password} - netrcInstance.machines['api.heroku.com'] = {login: current.username, password: current.password} - await netrcInstance.save() + async writeLoginState(dataDir: string, name: string): Promise { + return writeLoginState(dataDir, name) } private account(name: string): Heroku.Account { - const basedir = path.join(this.configDir(), 'accounts') - const file = fs.readFileSync(path.join(basedir, name), 'utf8') + const file = fs.readFileSync(path.join(this.accountsDir(), name), 'utf8') const account = parse(file) - if (account[':username']) { - // convert from ruby symbols - account.username = account[':username'] - account.password = account[':password'] - delete account[':username'] - delete account[':password'] - } - + this.convertRubySymbols(account) return account } + private accountsDir(): string { + return path.join(this.configDir(), 'accounts') + } + private configDir() { const legacyDir = path.join(os.homedir(), '.heroku') if (fs.existsSync(legacyDir)) { @@ -85,6 +136,33 @@ export class AccountsWrapper implements IAccountsWrapper { return path.join(os.homedir(), '.config', 'heroku') } + private convertRubySymbols(account: any): void { + if (account[':username']) { + account.username = account[':username'] + account.password = account[':password'] + delete account[':username'] + delete account[':password'] + } + } + + private getAliasEmail(alias: string): string | undefined { + try { + const filePath = path.join(this.accountsDir(), alias) + + if (!fs.existsSync(filePath)) { + return + } + + const file = fs.readFileSync(filePath, 'utf8') + const account = parse(file) + this.convertRubySymbols(account) + + return account.username ?? undefined + } catch { + return undefined + } + } + private async initNetrc() { if (!this.netrc) { const NetrcModule = await import('netrc-parser') @@ -95,6 +173,12 @@ export class AccountsWrapper implements IAccountsWrapper { return this.netrc } + + private writeAccountFile(name: string, content: Record): void { + const filePath = path.join(this.accountsDir(), name) + fs.writeFileSync(filePath, stringify(content), 'utf8') + fs.chmodSync(filePath, 0o600) + } } // Default export for convenience diff --git a/src/lib/analytics-telemetry/backboard-herokulytics-client.ts b/src/lib/analytics-telemetry/backboard-herokulytics-client.ts index 09bb3a9fe3..3d3c6210d0 100644 --- a/src/lib/analytics-telemetry/backboard-herokulytics-client.ts +++ b/src/lib/analytics-telemetry/backboard-herokulytics-client.ts @@ -1,4 +1,4 @@ -import {vars} from '@heroku-cli/command' +import {APIClient} from '@heroku-cli/command' import {HTTP} from '@heroku/http-call' import {Command, Interfaces} from '@oclif/core' import fs from 'fs-extra' @@ -34,8 +34,8 @@ export default class BackboardHerokulyticsClient { config: Interfaces.Config http: ReturnType userConfig!: HerokulyticsConfig + private heroku!: APIClient private isInitialized = false - private netrc: any constructor(config: Interfaces.Config) { this.config = config @@ -45,31 +45,13 @@ export default class BackboardHerokulyticsClient { } get authorizationToken(): string | undefined { - return process.env.HEROKU_API_KEY || this.netrcToken - } - - get netrcLogin(): string | undefined { - return this.netrc?.machines[vars.apiHost]?.login - } - - get netrcToken(): string | undefined { - return this.netrc?.machines[vars.apiHost]?.password + return this.heroku.auth } get url(): string { return process.env.HEROKU_ANALYTICS_URL || 'https://backboard.heroku.com/hamurai' } - get user(): string | undefined { - if (this.usingHerokuAPIKey) return undefined - return this.netrcLogin - } - - get usingHerokuAPIKey(): boolean { - const k = process.env.HEROKU_API_KEY - return Boolean(k && k.length > 0) - } - async _acAnalytics(id: string): Promise { if (id === 'autocomplete:options') return 0 const root = path.join(this.config.cacheDir, 'autocomplete', 'completion_analytics') @@ -148,10 +130,8 @@ export default class BackboardHerokulyticsClient { telemetryDebug('Initializing Herokulytics client...') this.isInitialized = true - const NetrcModule = await import('netrc-parser') - const NetrcClass = (NetrcModule as any).Netrc || (NetrcModule as any).default.constructor - this.netrc = new NetrcClass() - await this.netrc.load() + this.heroku = new APIClient(this.config) + await this.heroku.getAuth() this.userConfig = new HerokulyticsConfig(this.config) await this.userConfig.init() telemetryDebug('Herokulytics client initialized (install_id: %s)', this.userConfig.install) diff --git a/src/lib/git/git.ts b/src/lib/git/git.ts index 4414e8fbd1..ab40f3c189 100644 --- a/src/lib/git/git.ts +++ b/src/lib/git/git.ts @@ -3,21 +3,45 @@ import {ux} from '@oclif/core/ux' import cp from 'node:child_process' import fs from 'node:fs' import {promisify} from 'node:util' -const execFile = promisify(cp.execFile) +const execFilePromise = promisify(cp.execFile) import debug from 'debug' const gitDebug = debug('git') export default class Git { + private readonly execFile = execFilePromise + + /** Configures `heroku git:credentials` as a Git credential helper + * that is URL-scoped to Heroku Git operations only. + */ + async configureCredentialHelper() { + const {httpGitHost} = vars + await this.exec([ + 'config', + '--global', + `credential.https://${httpGitHost}.helper`, + '!heroku git:credentials', + ]) + } + createRemote(remote: string, url: string) { return this.hasGitRemote(remote) .then(exists => exists ? null : this.exec(['remote', 'add', remote, url])) } + /** Erases stored credentials for the Heroku Git host */ + async eraseCredentials() { + const {httpGitHost} = vars + await this.spawn(['credential', 'reject'], { + input: `protocol=https\nhost=${httpGitHost}\n\n`, + stdio: ['pipe', 'ignore', 'ignore'], + }) + } + public async exec(args: string[]): Promise { gitDebug('exec: git %o', args) try { - const {stderr, stdout} = await execFile('git', args) + const {stderr, stdout} = await this.execFile('git', args) if (stderr) process.stderr.write(stderr) return stdout.trim() } catch (error: any) { @@ -57,6 +81,7 @@ export default class Git { return true } catch (error: any) { if (error.code !== 'ENOENT') throw error + return false } } @@ -84,13 +109,29 @@ export default class Git { ?.split(' ')[0] ?? '' } - public spawn(args: string[]) { + /** Removes `heroku git:credentials` from the global config */ + async removeCredentialHelper() { + const {httpGitHost} = vars + await this.exec(['config', '--global', '--unset-all', `credential.https://${httpGitHost}.helper`]) + } + + public spawn(args: string[], options: {input?: string; stdio?: cp.StdioOptions;} = {}) { return new Promise((resolve, reject) => { gitDebug('spawn: git %o', args) - const s = cp.spawn('git', args, {stdio: [0, 1, 2]}) + const s = cp.spawn('git', args, {stdio: options.stdio ?? [0, 1, 2]}) + + if (options.input && s.stdin) { + s.stdin.write(options.input) + s.stdin.end() + } + s.on('error', (err: Error & {code?: string}) => { if (err.code === 'ENOENT') { - ux.error('Git must be installed to use the Heroku CLI. See instructions here: https://git-scm.com') + try { + ux.error('Git must be installed to use the Heroku CLI. See instructions here: https://git-scm.com') + } catch (error) { + reject(error) + } } else reject(err) }) s.on('close', resolve) diff --git a/test/helpers/credential-manager-stub.ts b/test/helpers/credential-manager-stub.ts new file mode 100644 index 0000000000..812ba57962 --- /dev/null +++ b/test/helpers/credential-manager-stub.ts @@ -0,0 +1,36 @@ +import { + getAuth as originalGetAuth, + removeAuth as originalRemoveAuth, + saveAuth as originalSaveAuth, +} from '@heroku-cli/command/lib/credential-manager-core/index.js' +import {setCredentialManagerProvider} from '@heroku-cli/command/lib/credential-manager.js' + +interface Provider { + getAuth: () => Promise<{account: string | undefined, token: string | undefined}> + removeAuth: () => Promise + saveAuth: () => Promise +} + +export function stubCredentialManager(provider?: Partial) { + const originalProvider = { + getAuth: originalGetAuth, + removeAuth: originalRemoveAuth, + saveAuth: originalSaveAuth, + } + + const defaultProvider : Provider = { + async getAuth() { + return {account: undefined, token: undefined} + }, + async removeAuth() {}, + async saveAuth() {}, + } + + setCredentialManagerProvider({...defaultProvider, ...provider}) + + return { + restore() { + setCredentialManagerProvider(originalProvider) + }, + } +} diff --git a/test/helpers/hooks.ts b/test/helpers/hooks.ts index 96083d82b5..19c8c0a8d5 100644 --- a/test/helpers/hooks.ts +++ b/test/helpers/hooks.ts @@ -1,21 +1,27 @@ import nock from 'nock' +// @heroku-cli/command reads and writes credentials via the OS (keychain / .netrc) +// Unit tests should not run that code; we swap in a fake provider and restore after. +import {stubCredentialManager} from './credential-manager-stub.js' // ux.action.start and stop are not working as expected in tests // this stub is a workaround to get the tests to pass by just // directly writing to stderr what ux.action.start and stop would write import {stubUxActionStart} from './ux-stub.js' let uxStub: {restore: () => void} +let credentialManagerStub: {restore: () => void} export const mochaHooks = { afterEach(done: () => void) { nock.cleanAll() uxStub.restore() + credentialManagerStub.restore() done() }, beforeEach(done: () => void) { uxStub = stubUxActionStart() + credentialManagerStub = stubCredentialManager() done() }, } diff --git a/test/unit/analytics-telemetry/backboard-herokulytics-client.unit.test.ts b/test/unit/analytics-telemetry/backboard-herokulytics-client.unit.test.ts new file mode 100644 index 0000000000..a4bc9d22d2 --- /dev/null +++ b/test/unit/analytics-telemetry/backboard-herokulytics-client.unit.test.ts @@ -0,0 +1,231 @@ +import {Config} from '@oclif/core' +import {expect} from 'chai' +import nock from 'nock' +import * as sinon from 'sinon' + +import AnalyticsCommand, {AnalyticsInterface} from '../../../src/lib/analytics-telemetry/backboard-herokulytics-client.js' +import HerokulyticsConfig from '../../../src/lib/analytics-telemetry/herokulytics-config.js' +import {stubCredentialManager} from '../../helpers/credential-manager-stub.js' + +const mockCommand = { + id: 'login', + plugin: { + name: 'foo', + version: '123', + }, +} + +const mockInvalidCommand = { + id: 'login', +} + +function createBackboardMock(expectedGetter: (data: AnalyticsInterface) => any, actual: any) { + const backboard = nock('https://backboard.heroku.com/', { + reqheaders: { + 'user-agent': '@oclif/command/1.5.6 darwin-x64 node-v10.2.1', + }, + }) + .get('/hamurai') + .query(({data: analyticsData}) => { + const data: AnalyticsInterface = JSON.parse(Buffer.from(analyticsData as string, 'base64').toString()) + const expected = expectedGetter(data) + expect(expected).to.eq(actual) + return true + }) + .reply(200) + + return backboard +} + +async function runAnalyticsTest(expectedCbk: (data: AnalyticsInterface) => any, actual: any) { + const config = await Config.load() + config.platform = 'win32' + config.shell = 'fish' + config.version = '1' + config.userAgent = '@oclif/command/1.5.6 darwin-x64 node-v10.2.1' + config.name = 'heroku' + const analytics = new AnalyticsCommand(config) + + const backboard = createBackboardMock(expectedCbk, actual) + await analytics.send({ + argv: ['foo', 'bar'], Command: mockCommand as any, + }) + backboard.done() +} + +describe('analytics (error handling)', function () { + let sandbox: any + + before(async function () { + sandbox = sinon.createSandbox() + sandbox.stub(HerokulyticsConfig.prototype, 'install').get(() => 'abcde') + }) + + after(function () { + sandbox.restore() + }) + + it('does not show an error on console when backboard has an error (with authorizationToken)', async function () { + const credentialManagerStub = stubCredentialManager({ + getAuth: () => Promise.resolve({account: 'test@example.com', token: 'test-token'}), + }) + + const backboard = nock('https://backboard.heroku.com/', { + reqheaders: {authorization: 'Bearer test-token'}, + }) + .get('/hamurai') + .query(() => true) + .reply(500) + + const config = await Config.load() + config.platform = 'win32' + config.shell = 'fish' + config.version = '1' + config.userAgent = '@oclif/command/1.5.6 darwin-x64 node-v10.2.1' + config.name = 'heroku' + const analytics = new AnalyticsCommand(config) + + try { + await analytics.send({ + argv: ['foo', 'bar'], Command: mockCommand as any, + }) + } catch { + throw new Error('Expected analytics.send to catch error') + } finally { + backboard.done() + credentialManagerStub.restore() + } + }) + + it('does not show an error on console when backboard has an error (without authorizationToken)', async function () { + const credentialManagerStub = stubCredentialManager({ + getAuth: () => Promise.resolve({account: undefined, token: undefined}), + }) + + const backboard = nock('https://backboard.heroku.com/', { + badheaders: ['authorization'], + }) + .get('/hamurai') + .query(() => true) + .reply(500) + + const config = await Config.load() + config.platform = 'win32' + config.shell = 'fish' + config.version = '1' + config.userAgent = '@oclif/command/1.5.6 darwin-x64 node-v10.2.1' + config.name = 'heroku' + const analytics = new AnalyticsCommand(config) + + try { + await analytics.send({ + argv: ['foo', 'bar'], Command: mockCommand as any, + }) + } catch { + throw new Error('Expected analytics.send to catch error') + } finally { + backboard.done() + credentialManagerStub.restore() + } + }) + + it('does not record if plugin is not present', async function () { + const config = await Config.load() + config.platform = 'win32' + config.shell = 'fish' + config.version = '1' + config.userAgent = '@oclif/command/1.5.6 darwin-x64 node-v10.2.1' + config.name = 'heroku' + const analytics = new AnalyticsCommand(config) + + try { + await analytics.send({ + argv: ['foo', 'bar'], Command: mockInvalidCommand as any, + }) + } catch { + throw new Error('Expected analytics.send to catch error') + } + }) +}) + +describe('analytics', function () { + let sandbox: any + + before(async function () { + sandbox = sinon.createSandbox() + sandbox.stub(HerokulyticsConfig.prototype, 'install').get(() => 'abcde') + }) + + after(function () { + sandbox.restore() + }) + + it('emits source', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.source, 'cli') + }) + + it('emits event', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.event, 'login') + }) + + it('emits property cli', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.cli, 'heroku') + }) + + it('emits property command', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.command, 'login') + }) + + it('emits property completion', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.completion, 0) + }) + + it('emits property version', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.version, '1') + }) + + it('emits property plugin', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.plugin, 'foo') + }) + + it('emits property plugin_version', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.plugin_version, '123') + }) + + it('emits property os', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.os, 'win32') + }) + + it('emits property shell', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.shell, 'fish') + }) + + it('emits property valid', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.valid, true) + }) + + it('emits property language', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.language, 'node') + }) + + it('emits property install_id', async function () { + await runAnalyticsTest((d: AnalyticsInterface) => d.properties.install_id, 'abcde') + }) + + it('includes MCP version in version string when in MCP mode', async function () { + const originalMcpMode = process.env.HEROKU_MCP_MODE + const originalMcpVersion = process.env.HEROKU_MCP_SERVER_VERSION + process.env.HEROKU_MCP_MODE = 'true' + process.env.HEROKU_MCP_SERVER_VERSION = '1.2.3' + + try { + await runAnalyticsTest( + (d: AnalyticsInterface) => d.properties.version, + '1 (MCP 1.2.3)', // '1' is the version set in runAnalyticsTest + ) + } finally { + process.env.HEROKU_MCP_MODE = originalMcpMode + process.env.HEROKU_MCP_SERVER_VERSION = originalMcpVersion + } + }) +}) diff --git a/test/unit/commands/accounts/add.unit.test.ts b/test/unit/commands/accounts/add.unit.test.ts index ed2305c5be..ead9eef57f 100644 --- a/test/unit/commands/accounts/add.unit.test.ts +++ b/test/unit/commands/accounts/add.unit.test.ts @@ -1,3 +1,4 @@ +import {APIClient} from '@heroku-cli/command' import {runCommand} from '@heroku-cli/test-utils' import {expect} from 'chai' import nock from 'nock' @@ -5,19 +6,20 @@ import {restore, SinonStub, stub} from 'sinon' import Cmd from '../../../../src/commands/accounts/add.js' import AccountsModule from '../../../../src/lib/accounts/accounts.js' +import {stubCredentialManager} from '../../../helpers/credential-manager-stub.js' describe('accounts:add', function () { let api: nock.Scope let addStub: SinonStub + let listStub: SinonStub beforeEach(function () { - stub(AccountsModule, 'list').returns([]) + listStub = stub(AccountsModule, 'list').resolves([]) addStub = stub(AccountsModule, 'add') api = nock('https://api.heroku.com') }) afterEach(function () { - delete process.env.HEROKU_API_KEY restore() api.done() nock.cleanAll() @@ -25,7 +27,10 @@ describe('accounts:add', function () { describe('when the user is logged in', function () { it('should call the accounts.add function with the account name, user email, and auth token', async function () { - process.env.HEROKU_API_KEY = 'testHerokuAPIKey' + stubCredentialManager({ + getAuth: async () => ({account: 'testEmail', token: 'testHerokuAPIKey'}), + }) + api.get('/account') .reply(200, {email: 'testEmail'}) @@ -36,26 +41,54 @@ describe('accounts:add', function () { expect(addStub.args[0][2]).to.equal('testHerokuAPIKey') }) - it('should prompt the user to log in if the user does not have an auth token', async function () { - process.env.HEROKU_API_KEY = '' + it('should prompt the user to log in if the user is not logged in', async function () { + stubCredentialManager({ + getAuth: async () => ({account: undefined, token: undefined}), + }) + + const APIClientStub = stub(APIClient.prototype, 'login') + APIClientStub.resolves() + + api.get('/account') + .reply(401, {id: 'unauthorized', message: 'Unauthorized'}) + api.get('/account') .reply(200, {email: 'testEmail'}) await runCommand(Cmd, ['testAccountName']) - .catch(error => { - expect(error.message).to.equal('You must be logged in to run this command.') - }) + + expect(APIClientStub.calledOnce).to.equal(true) + }) + + it('should error if the account name already exists', async function () { + stubCredentialManager({ + getAuth: async () => ({account: 'testEmail', token: 'testHerokuAPIKey'}), + }) + + listStub.resolves([{name: 'testAccountName', username: 'testEmail'}]) + + try { + await runCommand(Cmd, ['testAccountName']) + } catch (error: unknown) { + expect((error as Error).message).to.contain('testAccountName already exists') + } }) - it('should prompt the user to log in if the user does not have an email', async function () { - process.env.HEROKU_API_KEY = 'testHerokuAPIKey' + it('should error if the email already has an alias', async function () { + stubCredentialManager({ + getAuth: async () => ({account: 'testEmail', token: 'testHerokuAPIKey'}), + }) + api.get('/account') - .reply(200, {}) + .reply(200, {email: 'testEmail'}) - await runCommand(Cmd, ['testAccountName']) - .catch(error => { - expect(error.message).to.equal('You must be logged in to run this command.') - }) + listStub.resolves([{name: 'existingAlias', username: 'testEmail'}]) + + try { + await runCommand(Cmd, ['newAlias']) + } catch (error: unknown) { + expect((error as Error).message).to.contain('testEmail already has an alias: existingAlias') + } }) }) }) diff --git a/test/unit/commands/accounts/current.unit.test.ts b/test/unit/commands/accounts/current.unit.test.ts index 4250e3aa64..aa22142c2c 100644 --- a/test/unit/commands/accounts/current.unit.test.ts +++ b/test/unit/commands/accounts/current.unit.test.ts @@ -18,13 +18,13 @@ describe('accounts:current', function () { }) it('should print the name of the current account if an account is found', async function () { - currentStub.returns('test-account') + currentStub.resolves('test-account') const {stdout} = await runCommand(Cmd, []) expect(stdout).to.contain('test-account') }) it('should print an error message if no account is found', async function () { - currentStub.returns(null) + currentStub.resolves(null) await runCommand(Cmd, []) .catch((error: Error) => { const expected = 'You haven\'t set an account. Run heroku accounts:add to add an account to your cache or heroku accounts:set to set the current account.' diff --git a/test/unit/commands/accounts/index.unit.test.ts b/test/unit/commands/accounts/index.unit.test.ts index 6ec68aeb9b..771a5984f6 100644 --- a/test/unit/commands/accounts/index.unit.test.ts +++ b/test/unit/commands/accounts/index.unit.test.ts @@ -20,13 +20,13 @@ describe('accounts', function () { it('should print a list of added accounts with the current account highlighted if accounts are found', async function () { currentStub.resolves('test-account') - listStub.returns([{name: 'test-account'}, {name: 'test-account-2'}]) + listStub.resolves([{name: 'test-account', username: 'user1'}, {name: 'test-account-2', username: 'user2'}]) const {stdout} = await runCommand(Cmd, []) expect(stdout).to.equal('* test-account\n test-account-2\n') }) it('should print an error message if no accounts are found', async function () { - listStub.returns([]) + listStub.resolves([]) await runCommand(Cmd, []) .catch((error: Error) => { expect(error.message).to.contain('You don\'t have any accounts in your cache.') diff --git a/test/unit/commands/accounts/remove.unit.test.ts b/test/unit/commands/accounts/remove.unit.test.ts index 9700957ba2..a0813bade6 100644 --- a/test/unit/commands/accounts/remove.unit.test.ts +++ b/test/unit/commands/accounts/remove.unit.test.ts @@ -1,4 +1,5 @@ import {runCommand} from '@heroku-cli/test-utils' +import ansis from 'ansis' import {expect} from 'chai' import * as sinon from 'sinon' @@ -21,15 +22,16 @@ describe('accounts:remove', function () { }) it('calls the remove function with the account name when the account exists and it is not the current account', async function () { - currentStub.returns('test-account') - listStub.returns([{name: 'test-account'}, {name: 'test-account-2'}]) + currentStub.resolves('test-account') + listStub.resolves([{name: 'test-account', username: 'user1'}, {name: 'test-account-2', username: 'user2'}]) + removeStub.resolves() await runCommand(Cmd, ['test-account-2']) - expect(removeStub.calledWith('test-account-2')) + expect(removeStub.calledWith('test-account-2')).to.be.true }) it('should return an error if the selected account name is not included in the account list', async function () { - currentStub.returns('test-account') - listStub.returns([{name: 'test-account'}, {name: 'test-account-2'}]) + currentStub.resolves('test-account') + listStub.resolves([{name: 'test-account', username: 'user1'}, {name: 'test-account-2', username: 'user2'}]) await runCommand(Cmd, ['test-account-3']) .catch((error: Error) => { expect(error.message).to.contain('test-account-3 doesn\'t exist in your accounts cache.') @@ -37,11 +39,20 @@ describe('accounts:remove', function () { }) it('should return an error if the selected account name is the current account', async function () { - currentStub.returns('test-account') - listStub.returns([{name: 'test-account'}, {name: 'test-account-2'}]) + currentStub.resolves('test-account') + listStub.resolves([{name: 'test-account', username: 'user1'}, {name: 'test-account-2', username: 'user2'}]) await runCommand(Cmd, ['test-account']) .catch((error: Error) => { - expect(error.message).to.contain('test-account is the current account.') + expect(ansis.strip(error.message)).to.equal('test-account is the current account. To log out, run heroku logout.') + }) + }) + + it('should return an error if the selected account is the current account (aliased)', async function () { + currentStub.resolves('user1@example.com') + listStub.resolves([{name: 'test-account', username: 'user1@example.com'}, {name: 'test-account-2', username: 'user2@example.com'}]) + await runCommand(Cmd, ['test-account']) + .catch((error: Error) => { + expect(ansis.strip(error.message)).to.equal('test-account is the current account. To log out, run heroku logout.') }) }) }) diff --git a/test/unit/commands/accounts/set.unit.test.ts b/test/unit/commands/accounts/set.unit.test.ts index e12eacccc1..04fb42427b 100644 --- a/test/unit/commands/accounts/set.unit.test.ts +++ b/test/unit/commands/accounts/set.unit.test.ts @@ -11,24 +11,26 @@ describe('accounts:set', function () { beforeEach(function () { listStub = stub(AccountsModule, 'list') - setStub = stub(AccountsModule, 'set') + setStub = stub(AccountsModule, 'set').resolves() }) afterEach(function () { restore() }) - it('calls the set function with the account name when the account exists', async function () { - listStub.returns([{name: 'test-account'}, {name: 'test-account-2'}]) + it('calls set with the account name and dataDir when matched by name', async function () { + listStub.resolves([{name: 'test-account', username: 'user1'}, {name: 'test-account-2', username: 'user2'}]) await runCommand(Cmd, ['test-account-2']) - expect(setStub.calledWith('test-account-2')) + expect(setStub.calledOnce).to.be.true + expect(setStub.firstCall.args[0]).to.deep.equal({name: 'test-account-2', username: 'user2'}) + expect(setStub.firstCall.args[1].toLowerCase()).to.contain('local') + expect(setStub.firstCall.args[1]).to.contain('heroku') }) - it('should return an error if the selected account name is not included in the account list', async function () { - listStub.returns([{name: 'test-account'}, {name: 'test-account-2'}]) - await runCommand(Cmd, ['test-account-3']) - .catch((error: Error) => { - expect(error.message).to.contain('test-account-3 does not exist in your accounts cache.') - }) + it('returns an error if the account is not in the list', async function () { + listStub.resolves([{name: 'test-account', username: 'user1'}, {name: 'test-account-2', username: 'user2'}]) + + const {error} = await runCommand(Cmd, ['test-account-3']) + expect(error?.message).to.contain('test-account-3 doesn\'t exist in your accounts cache.') }) }) diff --git a/test/unit/commands/apps/create.unit.test.ts b/test/unit/commands/apps/create.unit.test.ts index 62ebc2ba2d..639397a0ef 100644 --- a/test/unit/commands/apps/create.unit.test.ts +++ b/test/unit/commands/apps/create.unit.test.ts @@ -5,26 +5,26 @@ import {execSync} from 'node:child_process' import {SinonStub, stub} from 'sinon' import CreateCommand from '../../../../src/commands/apps/create.js' +import Git from '../../../../src/lib/git/git.js' describe('apps:create', function () { let api: nock.Scope + let configureCredentialHelperStub: SinonStub + let gitCreateRemoteStub: SinonStub beforeEach(function () { api = nock('https://api.heroku.com') + + configureCredentialHelperStub = stub(Git.prototype, 'configureCredentialHelper').resolves() + gitCreateRemoteStub = stub(Git.prototype, 'createRemote').resolves() }) afterEach(function () { api.done() nock.cleanAll() - // Clean up any heroku git remotes created by the tests - try { - const remotes = execSync('git remote', {encoding: 'utf8', stdio: ['pipe', 'pipe', 'ignore']}) - if (remotes.includes('heroku')) { - execSync('git remote remove heroku', {stdio: 'ignore'}) - } - } catch { - // Ignore errors - } + + configureCredentialHelperStub.restore() + gitCreateRemoteStub.restore() }) it('creates an app', async function () { @@ -254,4 +254,81 @@ describe('apps:create', function () { expect(stderr).to.contain('Creating app... done, ⬢ foobar, stack is test') expect(stdout).to.equal('https://foobar.com | https://git.heroku.com/foobar.git\n') }) + + describe('git operations', function () { + it('creates a remote when in a git repository and --no-remote is not used', async function () { + api + .post('/apps', {}) + .reply(200, { + name: 'foobar', + stack: {name: 'cedar-14'}, + web_url: 'https://foobar.com', + }) + + await runCommand(CreateCommand, []) + + expect(gitCreateRemoteStub.calledOnce).to.be.true + }) + + it('does not create a remote when not in a git repository', async function () { + const inGitRepoStub = stub(Git.prototype, 'inGitRepo').returns(false) + + api + .post('/apps', {}) + .reply(200, { + name: 'foobar', + stack: {name: 'cedar-14'}, + web_url: 'https://foobar.com', + }) + + try { + await runCommand(CreateCommand, []) + expect(gitCreateRemoteStub.called).to.be.false + } finally { + inGitRepoStub.restore() + } + }) + + it('does not create a remote when --no-remote is used', async function () { + api + .post('/apps', {}) + .reply(200, { + name: 'foobar', + stack: {name: 'cedar-14'}, + web_url: 'https://foobar.com', + }) + + await runCommand(CreateCommand, ['--no-remote']) + + expect(gitCreateRemoteStub.called).to.be.false + }) + + it('configures git credential helper when creating a remote', async function () { + api + .post('/apps', {}) + .reply(200, { + name: 'foobar', + stack: {name: 'cedar-14'}, + web_url: 'https://foobar.com', + }) + + await runCommand(CreateCommand, []) + + expect(configureCredentialHelperStub.calledOnce).to.be.true + }) + + it('does not configure git credential helper when --no-remote is used', async function () { + api + .post('/apps', {}) + .reply(200, { + name: 'foobar', + stack: {name: 'cedar-14'}, + web_url: 'https://foobar.com', + }) + + await runCommand(CreateCommand, ['--no-remote']) + + expect(configureCredentialHelperStub.called).to.be.false + }) + }) }) diff --git a/test/unit/commands/auth/login.unit.test.ts b/test/unit/commands/auth/login.unit.test.ts new file mode 100644 index 0000000000..d37a754d2c --- /dev/null +++ b/test/unit/commands/auth/login.unit.test.ts @@ -0,0 +1,113 @@ +import {APIClient} from '@heroku-cli/command' +import {runCommand} from '@heroku-cli/test-utils' +import {expect} from 'chai' +import nock from 'nock' +import {SinonStub, stub} from 'sinon' + +import Login from '../../../../src/commands/auth/login.js' +import Git from '../../../../src/lib/git/git.js' + +describe('auth:login', function () { + let api: nock.Scope + let configureCredentialHelperStub: SinonStub + let eraseCredentialsStub: SinonStub + let loginStub: SinonStub + let savedApiKey: string | undefined + + beforeEach(function () { + api = nock('https://api.heroku.com') + + savedApiKey = process.env.HEROKU_API_KEY + delete process.env.HEROKU_API_KEY + + configureCredentialHelperStub = stub(Git.prototype, 'configureCredentialHelper').resolves() + eraseCredentialsStub = stub(Git.prototype, 'eraseCredentials').resolves() + loginStub = stub(APIClient.prototype, 'login').resolves() + }) + + afterEach(function () { + api.done() + nock.cleanAll() + + if (savedApiKey !== undefined) { + process.env.HEROKU_API_KEY = savedApiKey + } + + configureCredentialHelperStub.restore() + eraseCredentialsStub.restore() + loginStub.restore() + }) + + it('displays the logged in user', async function () { + api + .get('/account') + .reply(200, {email: 'user@example.com'}) + + const {stdout} = await runCommand(Login, []) + + expect(stdout).to.contain('Logged in as user@example.com') + }) + + it('configures git credential helper after successful login', async function () { + api + .get('/account') + .reply(200, { + email: 'user@example.com', + }) + + await runCommand(Login, []) + + expect(configureCredentialHelperStub.calledOnce).to.be.true + }) + + it('rejects stale git credentials after successful login', async function () { + api + .get('/account') + .reply(200, { + email: 'user@example.com', + }) + + await runCommand(Login, []) + + expect(eraseCredentialsStub.calledOnce).to.be.true + }) + + it('does not perform git operations if not logged in', async function () { + loginStub.rejects(new Error('Not logged in')) + + await runCommand(Login, []) + + expect(configureCredentialHelperStub.notCalled).to.be.true + expect(eraseCredentialsStub.notCalled).to.be.true + }) + + it('does not fail login if git credential helper configuration fails', async function () { + configureCredentialHelperStub.rejects(new Error('Git not found')) + + api + .get('/account') + .reply(200, { + email: 'user@example.com', + }) + + const {error} = await runCommand(Login, []) + + expect(error).to.be.undefined + expect(configureCredentialHelperStub.calledOnce).to.be.true + }) + + it('does not fail login if git credential deletion fails', async function () { + eraseCredentialsStub.rejects(new Error('Git not found')) + + api + .get('/account') + .reply(200, { + email: 'user@example.com', + }) + + const {error} = await runCommand(Login, []) + + expect(error).to.be.undefined + expect(eraseCredentialsStub.calledOnce).to.be.true + }) +}) diff --git a/test/unit/commands/auth/logout.unit.test.ts b/test/unit/commands/auth/logout.unit.test.ts index a05e2462a8..509e0f0e13 100644 --- a/test/unit/commands/auth/logout.unit.test.ts +++ b/test/unit/commands/auth/logout.unit.test.ts @@ -1,11 +1,73 @@ import {runCommand} from '@heroku-cli/test-utils' import {expect} from 'chai' +import sinon from 'sinon' import Logout from '../../../../src/commands/auth/logout.js' +import AccountsModule from '../../../../src/lib/accounts/accounts.js' +import Git from '../../../../src/lib/git/git.js' describe('auth:logout', function () { + let eraseCredentialsStub: sinon.SinonStub + let removeCredentialHelperStub: sinon.SinonStub + let currentNetrcStub: sinon.SinonStub + let removeStub: sinon.SinonStub + + beforeEach(function () { + eraseCredentialsStub = sinon.stub(Git.prototype, 'eraseCredentials').resolves() + removeCredentialHelperStub = sinon.stub(Git.prototype, 'removeCredentialHelper').resolves() + currentNetrcStub = sinon.stub(AccountsModule, 'currentNetrc').resolves(null) + removeStub = sinon.stub(AccountsModule, 'remove').resolves() + }) + + afterEach(function () { + sinon.restore() + }) + it('shows cli logging user out', async function () { const {stderr} = await runCommand(Logout, []) expect(stderr).to.equal('Logging out... done\n') }) + + it('removes git credential helper on logout', async function () { + await runCommand(Logout, []) + + expect(removeCredentialHelperStub.calledOnce).to.be.true + }) + + it('erases stale git credentials on logout', async function () { + await runCommand(Logout, []) + + expect(eraseCredentialsStub.calledOnce).to.be.true + }) + + it('does not fail logout if git operations fail', async function () { + eraseCredentialsStub.rejects(new Error('Git not found')) + + const {error} = await runCommand(Logout, []) + + expect(error).to.be.undefined + }) + + it('checks for cached netrc account', async function () { + await runCommand(Logout, []) + + expect(currentNetrcStub.calledOnce).to.be.true + }) + + it('removes cached netrc account when present', async function () { + currentNetrcStub.resolves('my-account') + + await runCommand(Logout, []) + + expect(removeStub.calledOnce).to.be.true + expect(removeStub.firstCall.args[0]).to.equal('my-account') + }) + + it('does not remove account when no cached netrc account', async function () { + currentNetrcStub.resolves(null) + + await runCommand(Logout, []) + + expect(removeStub.called).to.be.false + }) }) diff --git a/test/unit/commands/auth/token.unit.test.ts b/test/unit/commands/auth/token.unit.test.ts index 5164c22ffc..0e2a8d4540 100644 --- a/test/unit/commands/auth/token.unit.test.ts +++ b/test/unit/commands/auth/token.unit.test.ts @@ -1,6 +1,8 @@ +import {APIClient} from '@heroku-cli/command' import {runCommand} from '@heroku-cli/test-utils' import {expect} from 'chai' import nock from 'nock' +import {restore, stub} from 'sinon' import Token from '../../../../src/commands/auth/token.js' @@ -9,13 +11,13 @@ describe('auth:token', function () { beforeEach(function () { api = nock('https://api.heroku.com') - process.env.HEROKU_API_KEY = 'foobar' + stub(APIClient.prototype, 'auth').get(() => 'foobar') }) afterEach(function () { - delete process.env.HEROKU_API_KEY api.done() nock.cleanAll() + restore() }) it('shows auth token', async function () { diff --git a/test/unit/commands/auth/whoami.unit.test.ts b/test/unit/commands/auth/whoami.unit.test.ts index fd719115c9..0d5c22255b 100644 --- a/test/unit/commands/auth/whoami.unit.test.ts +++ b/test/unit/commands/auth/whoami.unit.test.ts @@ -1,6 +1,8 @@ +import {APIClient} from '@heroku-cli/command' import {runCommand} from '@heroku-cli/test-utils' import {expect} from 'chai' import nock from 'nock' +import {restore, stub} from 'sinon' import Whoami from '../../../../src/commands/auth/whoami.js' @@ -9,13 +11,14 @@ describe('auth:whoami', function () { beforeEach(function () { api = nock('https://api.heroku.com') - process.env.HEROKU_API_KEY = 'foobar' + stub(APIClient.prototype, 'auth').get(() => 'foobar') }) afterEach(function () { delete process.env.HEROKU_API_KEY api.done() nock.cleanAll() + restore() }) it('shows user email when logged in', async function () { @@ -23,10 +26,9 @@ describe('auth:whoami', function () { .get('/account') .reply(200, {email: 'gandalf@example.com'}) - const {stderr, stdout} = await runCommand(Whoami, []) + const {stdout} = await runCommand(Whoami, []) expect(stdout).to.equal('gandalf@example.com\n') - expect(stderr).to.contain('Warning: HEROKU_API_KEY is set') }) it('exits with status 100 when not logged in', async function () { @@ -34,9 +36,20 @@ describe('auth:whoami', function () { .get('/account') .reply(401) - const {error, stderr} = await runCommand(Whoami, []) + const {error} = await runCommand(Whoami, []) expect(error?.oclif?.exit).to.equal(100) + }) + + it('shows a warning when the HEROKU_API_KEY env var is set', async function () { + process.env.HEROKU_API_KEY = 'foobar' + api + .get('/account') + .reply(200, {email: 'gandalf@example.com'}) + + const {stderr, stdout} = await runCommand(Whoami, []) + expect(stderr).to.contain('Warning: HEROKU_API_KEY is set') + expect(stdout).to.equal('gandalf@example.com\n') }) }) diff --git a/test/unit/commands/buildpacks/versions.unit.test.ts b/test/unit/commands/buildpacks/versions.unit.test.ts index 2bffb64a78..4af9e8fb70 100644 --- a/test/unit/commands/buildpacks/versions.unit.test.ts +++ b/test/unit/commands/buildpacks/versions.unit.test.ts @@ -1,28 +1,24 @@ +import {APIClient} from '@heroku-cli/command' import {runCommand} from '@heroku-cli/test-utils' import {Fixture} from '@heroku/buildpack-registry' import {expect} from 'chai' import nock from 'nock' +import {restore, stub} from 'sinon' import BuildpacksVersions from '../../../../src/commands/buildpacks/versions.js' describe('buildpacks:versions', function () { - let originalApiKey: string | undefined let registryApi: nock.Scope beforeEach(function () { registryApi = nock('https://buildpack-registry.heroku.com') - originalApiKey = process.env.HEROKU_API_KEY - process.env.HEROKU_API_KEY = 'authtoken' + stub(APIClient.prototype, 'auth').get(() => 'authtoken') }) afterEach(function () { registryApi.done() nock.cleanAll() - if (originalApiKey) { - process.env.HEROKU_API_KEY = originalApiKey - } else { - delete process.env.HEROKU_API_KEY - } + restore() }) it('shows info about the buildpack', async function () { diff --git a/test/unit/commands/container/login.unit.test.ts b/test/unit/commands/container/login.unit.test.ts index 73f0d901eb..89d4c8c98b 100644 --- a/test/unit/commands/container/login.unit.test.ts +++ b/test/unit/commands/container/login.unit.test.ts @@ -1,3 +1,4 @@ +import {APIClient} from '@heroku-cli/command' import {runCommand} from '@heroku-cli/test-utils' import {expect} from 'chai' import {createSandbox, SinonSandbox} from 'sinon' @@ -10,7 +11,7 @@ describe('container:login', function () { beforeEach(function () { sandbox = createSandbox() - process.env.HEROKU_API_KEY = 'heroku_token' + sandbox.stub(APIClient.prototype, 'auth').get(() => 'heroku_token') }) afterEach(function () { diff --git a/test/unit/commands/git/clone.unit.test.ts b/test/unit/commands/git/clone.unit.test.ts index 36edd28cc5..7a87f6dd4a 100644 --- a/test/unit/commands/git/clone.unit.test.ts +++ b/test/unit/commands/git/clone.unit.test.ts @@ -1,12 +1,46 @@ import {runCommand} from '@heroku-cli/test-utils' import {expect} from 'chai' +import nock from 'nock' +import sinon from 'sinon' import {GitClone as Clone} from '../../../../src/commands/git/clone.js' +import Git from '../../../../src/lib/git/git.js' describe('git:clone', function () { + let api: nock.Scope + let configureCredentialHelperStub: sinon.SinonStub + let spawnStub: sinon.SinonStub + + beforeEach(function () { + api = nock('https://api.heroku.com') + + configureCredentialHelperStub = sinon.stub(Git.prototype, 'configureCredentialHelper').resolves() + spawnStub = sinon.stub(Git.prototype, 'spawn').resolves() + }) + + afterEach(function () { + api.done() + nock.cleanAll() + + configureCredentialHelperStub.restore() + spawnStub.restore() + }) + it('errors if no app given', async function () { const {error} = await runCommand(Clone, []) expect(error?.message).to.contain('Missing required flag app') }) + + it('configures git credential helper after cloning', async function () { + api + .get('/apps/test-app') + .reply(200, { + name: 'test-app', + }) + + await runCommand(Clone, ['-a', 'test-app']) + + expect(configureCredentialHelperStub.calledOnce).to.be.true + }) }) diff --git a/test/unit/commands/git/credentials.unit.test.ts b/test/unit/commands/git/credentials.unit.test.ts index de047fd920..75d263a22f 100644 --- a/test/unit/commands/git/credentials.unit.test.ts +++ b/test/unit/commands/git/credentials.unit.test.ts @@ -1,12 +1,111 @@ +import {APIClient} from '@heroku-cli/command' import {runCommand} from '@heroku-cli/test-utils' import {expect} from 'chai' +import mockStdin from 'mock-stdin' +import {restore, stub} from 'sinon' import {GitCredentials as Credentials} from '../../../../src/commands/git/credentials.js' +import {stubCredentialManager} from '../../../helpers/credential-manager-stub.js' describe('git:credentials', function () { + let stdin: mockStdin.MockSTDIN + let savedApiKey: string | undefined + + beforeEach(function () { + savedApiKey = process.env.HEROKU_API_KEY + delete process.env.HEROKU_API_KEY + + stdin = mockStdin.stdin() + }) + + afterEach(function () { + if (savedApiKey !== undefined) { + process.env.HEROKU_API_KEY = savedApiKey + } + + stdin.restore() + }) + it('errors if no app given', async function () { const {error} = await runCommand(Credentials, []) expect(error?.message).to.contain('Missing 1 required arg') }) + + describe('get operation', function () { + it('outputs credentials', async function () { + stubCredentialManager({ + getAuth: async () => ({account: 'test@example.com', token: 'test-token'}), + }) + stub(APIClient.prototype, 'auth').get(() => 'test-token') + + process.stdin.push('protocol=https\nhost=git.heroku.com\n\n') + + const {error, stdout} = await runCommand(Credentials, ['get']) + + expect(error).to.be.undefined + expect(stdout).to.equal('protocol=https\nhost=git.heroku.com\nusername=heroku\npassword=test-token\n') + restore() + }) + + it('does not output credentials for non-Heroku hosts', async function () { + stubCredentialManager({ + getAuth: async () => ({account: 'test@example.com', token: 'test-token'}), + }) + + process.stdin.push('protocol=https\nhost=github.com\n\n') + + const {error, stdout} = await runCommand(Credentials, ['get']) + + expect(error).to.be.undefined + expect(stdout).to.equal('') + }) + + it('does not output credentials when protocol is not https', async function () { + stubCredentialManager({ + getAuth: async () => ({account: 'test@example.com', token: 'test-token'}), + }) + + process.stdin.push('protocol=http\nhost=git.heroku.com\n\n') + + const {error, stdout} = await runCommand(Credentials, ['get']) + + expect(error).to.be.undefined + expect(stdout).to.equal('') + }) + + it('errors when not logged in', async function () { + stubCredentialManager({ + getAuth: async () => ({account: undefined, token: undefined}), + }) + + process.stdin.push('protocol=https\nhost=git.heroku.com\n\n') + + const {error} = await runCommand(Credentials, ['get']) + + expect(error?.message).to.contain('not logged in') + }) + }) + + describe('store operation', function () { + it('accepts input without error', async function () { + process.stdin.push('protocol=https\nhost=git.heroku.com\nusername=heroku\npassword=test-token\n\n') + + const {error, stdout} = await runCommand(Credentials, ['store']) + + expect(error).to.be.undefined + expect(stdout).to.equal('') + }) + }) + + describe('erase operation', function () { + it('accepts input without error', async function () { + process.stdin.push('protocol=https\nhost=git.heroku.com\n\n') + + const {error, stdout} = await runCommand(Credentials, ['erase']) + + expect(error).to.be.undefined + expect(stdout).to.equal('') + }) + }) }) diff --git a/test/unit/commands/git/remote.unit.test.ts b/test/unit/commands/git/remote.unit.test.ts index 8f5bb68ba0..51e13b46fb 100644 --- a/test/unit/commands/git/remote.unit.test.ts +++ b/test/unit/commands/git/remote.unit.test.ts @@ -1,12 +1,46 @@ import {runCommand} from '@heroku-cli/test-utils' import {expect} from 'chai' +import nock from 'nock' +import sinon from 'sinon' import {GitRemote as Remote} from '../../../../src/commands/git/remote.js' +import Git from '../../../../src/lib/git/git.js' describe('git:remote', function () { + let api: nock.Scope + let configureCredentialHelperStub: sinon.SinonStub + let execStub: sinon.SinonStub + + beforeEach(function () { + api = nock('https://api.heroku.com') + + configureCredentialHelperStub = sinon.stub(Git.prototype, 'configureCredentialHelper').resolves() + execStub = sinon.stub(Git.prototype, 'exec').resolves('') + }) + + afterEach(function () { + api.done() + nock.cleanAll() + + configureCredentialHelperStub.restore() + execStub.restore() + }) + it('errors if no app given', async function () { const {error} = await runCommand(Remote, []) expect(error?.message).to.contain('Specify an app with --app') }) + + it('configures git credential helper after adding remote', async function () { + api + .get('/apps/test-app') + .reply(200, { + name: 'test-app', + }) + + await runCommand(Remote, ['-a', 'test-app']) + + expect(configureCredentialHelperStub.calledOnce).to.be.true + }) }) diff --git a/test/unit/lib/accounts/accounts.unit.test.ts b/test/unit/lib/accounts/accounts.unit.test.ts index 7ecc2f9d43..f9f2a4388f 100644 --- a/test/unit/lib/accounts/accounts.unit.test.ts +++ b/test/unit/lib/accounts/accounts.unit.test.ts @@ -1,55 +1,57 @@ import {expect} from 'chai' import fs from 'node:fs' import os from 'node:os' -import path from 'node:path' import { match, restore, SinonStub, stub, } from 'sinon' import AccountsModule from '../../../../src/lib/accounts/accounts.js' +import {stubCredentialManager} from '../../../helpers/credential-manager-stub.js' describe('accounts', function () { let fsReaddirStub: SinonStub let fsReadFileStub: SinonStub beforeEach(function () { + process.env.HEROKU_NETRC_WRITE = 'true' fsReaddirStub = stub(fs, 'readdirSync') fsReadFileStub = stub(fs, 'readFileSync') }) afterEach(function () { + delete process.env.HEROKU_NETRC_WRITE + delete process.env.HEROKU_NATIVE_STORE_WRITE restore() - // fs.unlinkSync(tmpNetrc) }) describe('list()', function () { + let existsSyncStub: SinonStub + + beforeEach(function () { + delete process.env.HEROKU_NETRC_WRITE + stub(AccountsModule, 'getStorageConfig').returns({credentialStore: 'keychain' as any, useNetrc: false}) + existsSyncStub = stub(fs, 'existsSync') + }) + it('should return an empty array when directory is not accessible', function () { fsReaddirStub.throws(new Error('Directory not found')) const result = AccountsModule.list() expect(result).to.be.an('array').that.is.empty }) - it('should return array of account objects when files exist', function () { + it('should return array of AccountEntry objects when files exist', function () { fsReaddirStub.returns(['account1', 'account2']) fsReadFileStub.withArgs(match(/account1$/), 'utf8') - .returns('{"username": "user1", "password": "pass1"}') + .returns('username: user1\npassword: pass1\n') fsReadFileStub.withArgs(match(/account2$/), 'utf8') - .returns('{"username": "user2", "password": "pass2"}') + .returns('username: user2\npassword: pass2\n') const result = AccountsModule.list() expect(result).to.be.an('array') expect(result).to.have.lengthOf(2) - expect(result[0]).to.deep.include({ - name: 'account1', - password: 'pass1', - username: 'user1', - }) - expect(result[1]).to.deep.include({ - name: 'account2', - password: 'pass2', - username: 'user2', - }) + expect(result[0]).to.deep.equal({name: 'account1', username: 'user1'}) + expect(result[1]).to.deep.equal({name: 'account2', username: 'user2'}) }) it('should handle ruby-style symbol keys', function () { @@ -61,13 +63,21 @@ describe('accounts', function () { expect(result).to.be.an('array') expect(result).to.have.lengthOf(1) - expect(result[0]).to.deep.include({ - name: 'account1', - password: 'pass1', - username: 'user1', - }) - expect(result[0]).to.not.have.property(':username') - expect(result[0]).to.not.have.property(':password') + expect(result[0]).to.deep.equal({name: 'account1', username: 'user1'}) + }) + + it('should filter out files without username field', function () { + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + fsReaddirStub.returns(['valid', 'invalid']) + fsReadFileStub.withArgs(match(/valid$/), 'utf8') + .returns('username: valid@example.com\n') + fsReadFileStub.withArgs(match(/invalid$/), 'utf8') + .returns('other_field: value\n') + + const result = AccountsModule.list() + + expect(result).to.have.lengthOf(1) + expect(result[0]).to.deep.equal({name: 'valid', username: 'valid@example.com'}) }) }) @@ -77,7 +87,6 @@ describe('accounts', function () { let chmodSyncStub: SinonStub beforeEach(function () { - // Setup stubs before each test mkdirSyncStub = stub(fs, 'mkdirSync') writeFileSyncStub = stub(fs, 'writeFileSync') chmodSyncStub = stub(fs, 'chmodSync') @@ -91,11 +100,7 @@ describe('accounts', function () { }) it('should write credentials to file with correct format', function () { - const testName = 'test-user' - const testUsername = 'username123' - const testPassword = 'password123' - - AccountsModule.add(testName, testUsername, testPassword) + AccountsModule.add('test-user', 'username123', 'password123') expect(writeFileSyncStub.calledOnce).to.be.true expect(writeFileSyncStub.firstCall.args[1]).to.equal('username: username123\npassword: password123\n') @@ -103,9 +108,7 @@ describe('accounts', function () { }) it('should set correct file permissions', function () { - const testName = 'test-user' - - AccountsModule.add(testName, 'username123', 'password123') + AccountsModule.add('test-user', 'username123', 'password123') expect(chmodSyncStub.calledOnce).to.be.true expect(chmodSyncStub.firstCall.args[1]).to.equal(0o600) @@ -118,37 +121,331 @@ describe('accounts', function () { }) }) + describe('set()', function () { + describe('with credentialStore', function () { + let writeLoginStateStub: SinonStub + let existsSyncStub: SinonStub + let osHomeStub: SinonStub + let fakeNetrc: {machines: Record, save: SinonStub} + + function setNetrc(value: typeof fakeNetrc | undefined) { + (AccountsModule as unknown as {netrc: typeof fakeNetrc | undefined}).netrc = value + } + + beforeEach(function () { + stub(AccountsModule, 'getStorageConfig').returns({credentialStore: 'keychain' as any, useNetrc: false}) + writeLoginStateStub = stub(AccountsModule, 'writeLoginState').resolves() + existsSyncStub = stub(fs, 'existsSync') + osHomeStub = stub(os, 'homedir').returns('/user/home') + fakeNetrc = {machines: {}, save: stub().resolves()} + setNetrc(fakeNetrc) + }) + + afterEach(function () { + setNetrc(null as unknown as typeof fakeNetrc) + }) + + it('calls writeLoginState with email from alias file and writes to netrc', async function () { + const account = {name: 'production', username: 'prod@example.com'} + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/production$/)).returns(true) + fsReadFileStub.withArgs(match(/production$/), 'utf8') + .returns('username: prod@example.com\npassword: secret\n') + + await AccountsModule.set(account, '/data/heroku') + + expect(writeLoginStateStub.calledOnce).to.be.true + expect(writeLoginStateStub.firstCall.args[0]).to.equal('/data/heroku') + expect(writeLoginStateStub.firstCall.args[1]).to.equal('prod@example.com') + expect(fakeNetrc.machines['api.heroku.com']).to.deep.equal({login: 'prod@example.com', password: 'secret'}) + expect(fakeNetrc.machines['git.heroku.com']).to.deep.equal({login: 'prod@example.com', password: 'secret'}) + expect(fakeNetrc.save.calledOnce).to.be.true + }) + + it('throws error when alias file does not exist', async function () { + const account = {name: 'nonexistent', username: 'user@example.com'} + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/nonexistent$/)).returns(false) + fsReadFileStub.withArgs(match(/nonexistent$/), 'utf8') + .throws(new Error('ENOENT: no such file or directory')) + + await expect(AccountsModule.set(account, '/data/heroku')) + .to.be.rejectedWith('We can\'t find the alias file for nonexistent.') + }) + + it('writes email to login.json not alias name', async function () { + const account = {name: 'production', username: 'prod@example.com'} + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/production$/)).returns(true) + fsReadFileStub.withArgs(match(/production$/), 'utf8') + .returns('username: prod@example.com\n') + + await AccountsModule.set(account, '/data/heroku') + + // Verify it writes EMAIL not alias + expect(writeLoginStateStub.firstCall.args[1]).to.equal('prod@example.com') + expect(writeLoginStateStub.firstCall.args[1]).to.not.equal('production') + }) + }) + + describe('with useNetrc and account name', function () { + let fakeNetrc: {machines: Record, save: SinonStub} + + function setNetrc(value: typeof fakeNetrc | undefined) { + (AccountsModule as unknown as {netrc: typeof fakeNetrc | undefined}).netrc = value + } + + beforeEach(function () { + stub(AccountsModule, 'getStorageConfig').returns({credentialStore: null, useNetrc: true}) + fakeNetrc = {machines: {}, save: stub().resolves()} + setNetrc(fakeNetrc) + fsReadFileStub.withArgs(match(/my-account$/), 'utf8') + .returns('username: user@example.com\npassword: secret\n') + }) + + afterEach(function () { + setNetrc(null as unknown as typeof fakeNetrc) + }) + + it('writes credentials to api.heroku.com and git.heroku.com machines', async function () { + const account = {name: 'my-account', username: 'user@example.com'} + await AccountsModule.set(account, '/data/heroku') + + expect(fakeNetrc.machines['api.heroku.com']).to.deep.equal({login: 'user@example.com', password: 'secret'}) + expect(fakeNetrc.machines['git.heroku.com']).to.deep.equal({login: 'user@example.com', password: 'secret'}) + }) + + it('saves the netrc file', async function () { + const account = {name: 'my-account', username: 'user@example.com'} + await AccountsModule.set(account, '/data/heroku') + + expect(fakeNetrc.save.calledOnce).to.be.true + }) + + it('does not update netrc when account has no name', async function () { + const account = {username: 'user@example.com'} + await AccountsModule.set(account, '/data/heroku') + + expect(fakeNetrc.save.called).to.be.false + }) + + it('handles alias file without password (created in keychain mode)', async function () { + fsReadFileStub.withArgs(match(/keychain-alias$/), 'utf8') + .returns('username: user@example.com\n') + const account = {name: 'keychain-alias', username: 'user@example.com'} + await AccountsModule.set(account, '/data/heroku') + + expect(fakeNetrc.machines['api.heroku.com']).to.deep.equal({login: 'user@example.com', password: ''}) + expect(fakeNetrc.machines['git.heroku.com']).to.deep.equal({login: 'user@example.com', password: ''}) + expect(fakeNetrc.save.calledOnce).to.be.true + }) + }) + }) + + describe('currentNetrc()', function () { + let fakeNetrc: {machines: Record, save: SinonStub} + + function setNetrc(value: typeof fakeNetrc | undefined) { + (AccountsModule as unknown as {netrc: typeof fakeNetrc | undefined}).netrc = value + } + + beforeEach(function () { + fakeNetrc = {machines: {}, save: stub().resolves()} + setNetrc(fakeNetrc) + fsReadFileStub.withArgs(match(/my-account$/), 'utf8') + .returns('username: user@example.com\npassword: secret\n') + fsReaddirStub.returns(['my-account', 'other-account']) + fsReadFileStub.withArgs(match(/other-account$/), 'utf8') + .returns('username: other@example.com\npassword: secret\n') + }) + + afterEach(function () { + setNetrc(null as unknown as typeof fakeNetrc) + }) + + it('returns account name when api.heroku.com machine exists and matches', async function () { + fakeNetrc.machines['api.heroku.com'] = {login: 'user@example.com', password: 'secret'} + + const result = await AccountsModule.currentNetrc() + + expect(result).to.equal('my-account') + }) + + it('returns null when api.heroku.com machine does not exist', async function () { + const result = await AccountsModule.currentNetrc() + + expect(result).to.equal(null) + }) + + it('returns null when no account matches the login', async function () { + fakeNetrc.machines['api.heroku.com'] = {login: 'nomatch@example.com', password: 'secret'} + + const result = await AccountsModule.currentNetrc() + + expect(result).to.equal(null) + }) + }) + + describe('getAliasEmail()', function () { + let existsSyncStub: SinonStub + let osHomeStub: SinonStub + + beforeEach(function () { + existsSyncStub = stub(fs, 'existsSync') + osHomeStub = stub(os, 'homedir').returns('/user/home') + }) + + it('should return email when valid alias file exists', function () { + existsSyncStub.returns(false) + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/production$/)).returns(true) + fsReadFileStub.withArgs(match(/production$/), 'utf8') + .returns('username: user@example.com\n') + + const email = (AccountsModule as any).getAliasEmail('production') + + expect(email).to.equal('user@example.com') + }) + + it('should return undefined when alias file does not exist', function () { + existsSyncStub.returns(false) + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/nonexistent$/)).returns(false) + + const email = (AccountsModule as any).getAliasEmail('nonexistent') + + expect(email).to.be.undefined + }) + + it('should handle ruby-style symbol keys', function () { + existsSyncStub.returns(false) + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/legacy$/)).returns(true) + fsReadFileStub.withArgs(match(/legacy$/), 'utf8') + .returns('{":username": "legacy@example.com"}') + + const email = (AccountsModule as any).getAliasEmail('legacy') + + expect(email).to.equal('legacy@example.com') + }) + + it('should return undefined when file exists but username is missing', function () { + existsSyncStub.returns(false) + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/invalid$/)).returns(true) + fsReadFileStub.withArgs(match(/invalid$/), 'utf8') + .returns('other_field: value\n') + + const email = (AccountsModule as any).getAliasEmail('invalid') + + expect(email).to.be.undefined + }) + + it('should return undefined when file is malformed', function () { + existsSyncStub.returns(false) + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/malformed$/)).returns(true) + fsReadFileStub.withArgs(match(/malformed$/), 'utf8') + .throws(new Error('Parse error')) + + const email = (AccountsModule as any).getAliasEmail('malformed') + + expect(email).to.be.undefined + }) + + it('should use legacy .heroku directory when it exists', function () { + existsSyncStub.returns(false) + existsSyncStub.withArgs('/user/home/.heroku').returns(true) + existsSyncStub.withArgs(match(/production$/)).returns(true) + fsReadFileStub.withArgs(match(/production$/), 'utf8') + .returns('username: user@example.com\n') + + const email = (AccountsModule as any).getAliasEmail('production') + + expect(email).to.equal('user@example.com') + }) + }) + describe('remove', function () { let unlinkStub: SinonStub let osHomeStub: SinonStub let existsSyncStub: SinonStub beforeEach(function () { - // Create a stub for fs.unlinkSync before each test unlinkStub = stub(fs, 'unlinkSync') - osHomeStub = stub(os, 'homedir') + osHomeStub = stub(os, 'homedir').returns('/user/home') existsSyncStub = stub(fs, 'existsSync') }) - it('should remove the account file with the given name', function () { + it('should remove the alias file when no credential store', async function () { const accountName = 'test-account' - const basedir = '/user/home' - - osHomeStub.returns(basedir) existsSyncStub.returns(false) - AccountsModule.remove(accountName) + await AccountsModule.remove(accountName) expect(unlinkStub.calledOnce).to.be.true - expect(unlinkStub.firstCall.args[0]).to.equal(path.join(`${basedir}/.config/heroku/accounts`, accountName)) + expect(unlinkStub.firstCall.args[0]).to.match(/test-account$/) }) - it('should throw an error if the file cannot be removed', function () { - const accountName = 'non-existent-account' - const error = new Error('File not found') - unlinkStub.throws(error) + describe('with credentialStore', function () { + let credStub: ReturnType + let removeAuthStub: SinonStub - expect(() => AccountsModule.remove(accountName)).to.throw(Error) + beforeEach(function () { + removeAuthStub = stub().resolves() + credStub = stubCredentialManager({ + removeAuth: removeAuthStub, + }) + stub(AccountsModule, 'getStorageConfig').returns({credentialStore: 'keychain' as any, useNetrc: false}) + }) + + afterEach(function () { + credStub.restore() + }) + + it('should remove account from keychain and delete alias file', async function () { + const alias = 'production' + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/production$/)).returns(true) + fsReadFileStub.withArgs(match(/production$/), 'utf8') + .returns('username: prod@example.com\n') + + await AccountsModule.remove(alias) + + expect(removeAuthStub.calledOnce).to.be.true + expect(removeAuthStub.firstCall.args[0]).to.equal('prod@example.com') + expect(removeAuthStub.firstCall.args[1]).to.deep.equal(['api.heroku.com', 'git.heroku.com']) + expect(unlinkStub.calledOnce).to.be.true + expect(unlinkStub.firstCall.args[0]).to.match(/production$/) + }) + + it('should throw an error if removeAuth fails', async function () { + const alias = 'production' + existsSyncStub.withArgs('/user/home/.config/heroku').returns(false) + existsSyncStub.withArgs(match(/production$/)).returns(true) + fsReadFileStub.withArgs(match(/production$/), 'utf8') + .returns('username: prod@example.com\n') + const error = new Error('Keychain removal failed') + removeAuthStub.rejects(error) + + await expect(AccountsModule.remove(alias)).to.be.rejectedWith('Keychain removal failed') + }) + }) + + describe('with netrc mode', function () { + beforeEach(function () { + stub(AccountsModule, 'getStorageConfig').returns({credentialStore: null, useNetrc: true}) + }) + + it('should remove alias file', async function () { + const alias = 'netrc-account' + existsSyncStub.returns(false) + + await AccountsModule.remove(alias) + + expect(unlinkStub.calledOnce).to.be.true + expect(unlinkStub.firstCall.args[0]).to.match(/netrc-account$/) + }) }) }) }) diff --git a/test/unit/lib/addons/resolve.unit.test.ts b/test/unit/lib/addons/resolve.unit.test.ts index 09b8ce10ec..1f420f9799 100644 --- a/test/unit/lib/addons/resolve.unit.test.ts +++ b/test/unit/lib/addons/resolve.unit.test.ts @@ -66,13 +66,13 @@ describe('resolve', function () { it('fails if errored', function () { nock('https://api.heroku.com:443') - .post('/actions/addons/resolve', {addon: 'myaddon-5', app: 'myapp'}).reply(401) + .post('/actions/addons/resolve', {addon: 'myaddon-5', app: 'myapp'}).reply(503) return resolveAddon(herokuAPI, 'myapp', 'myaddon-5') .then(() => { throw new Error('unreachable') }) - .catch((error: any) => expect(error).to.have.nested.include({statusCode: 401})) + .catch((error: any) => expect(error).to.have.nested.include({statusCode: 503})) }) it('fails if ambiguous', function () { diff --git a/test/unit/lib/git/git.unit.test.ts b/test/unit/lib/git/git.unit.test.ts index c114868f9e..bc34119a70 100644 --- a/test/unit/lib/git/git.unit.test.ts +++ b/test/unit/lib/git/git.unit.test.ts @@ -1,90 +1,159 @@ import {expect} from 'chai' import cp from 'node:child_process' import {EventEmitter} from 'node:events' -import sinon from 'sinon' +import {SinonStub, stub} from 'sinon' import Git from '../../../../src/lib/git/git.js' describe('git', function () { - let mock: sinon.SinonMock + let execFileStub: SinonStub + let spawnStub: SinonStub let git: Git beforeEach(function () { - mock = sinon.mock(cp) git = new Git() + execFileStub = stub(git as any, 'execFile') + spawnStub = stub(cp, 'spawn') }) afterEach(function () { - return mock.restore() + execFileStub.restore() + spawnStub.restore() }) - it.skip('runs exec', function () { - mock.expects('execFile').withArgs('git', ['remote']).yieldsAsync(null, 'foo') - return git.exec(['remote']) - .then((data: string) => { - expect(data).to.equal('foo') - mock.verify() - }) + it('runs exec', async function () { + execFileStub.resolves({stderr: '', stdout: 'foo'}) + + const data = await git.exec(['remote']) + + expect(data).to.equal('foo') + expect(execFileStub.calledOnceWith('git', ['remote'])).to.be.true }) - it.skip('translates exec Errno::ENOENT to a friendlier error message', function () { + it('translates exec Errno::ENOENT to a friendlier error message', async function () { const err: any = new Error('err') err.code = 'ENOENT' - mock.expects('execFile').withArgs('git', ['remote']).yieldsAsync(err, null) + execFileStub.rejects(err) - return expect(git.exec(['remote'])).to.throw(Error, 'Git must be installed to use the Heroku CLI. See instructions here: https://git-scm.com') + try { + await git.exec(['remote']) + expect.fail('Should have thrown an error') + } catch (error: any) { + expect(error.message).to.contain('Git must be installed to use the Heroku CLI. See instructions here: https://git-scm.com') + } }) - it.skip('exec passes through all other errors', function () { + it('exec passes through all other errors', async function () { const err = new Error('Some other error message') - mock.expects('execFile').withArgs('git', ['remote']).yieldsAsync(err, null) + execFileStub.rejects(err) - return expect(git.exec(['remote'])).to.throw(err.message) + try { + await git.exec(['remote']) + expect.fail('Should have thrown an error') + } catch (error: any) { + expect(error.message).to.equal('Some other error message') + } }) - it.skip('runs spawn', function () { + it('runs spawn', async function () { const emitter = new EventEmitter() - mock.expects('spawn').withExactArgs('git', ['remote'], {stdio: [0, 1, 2]}).returns(emitter) - process.nextTick(() => emitter.emit('close')) - return git.spawn(['remote']) - .then(() => mock.verify()) + spawnStub.returns(emitter) + + const spawnPromise = git.spawn(['remote']) + + process.nextTick(() => emitter.emit('close', 0)) + + await spawnPromise + expect(spawnStub.calledOnceWith('git', ['remote'], {stdio: [0, 1, 2]})).to.be.true }) - it.skip('translates spawn Errno::ENOENT to a friendlier error message', function () { - const err = new Error('err') - err.name = 'ENOENT' + it('translates spawn Errno::ENOENT to a friendlier error message', async function () { + const err: any = new Error('err') + err.code = 'ENOENT' const emitter = new EventEmitter() - mock.expects('spawn').withExactArgs('git', ['remote'], {stdio: [0, 1, 2]}).returns(emitter) + spawnStub.returns(emitter) + + const spawnPromise = git.spawn(['remote']) + process.nextTick(() => emitter.emit('error', err)) - return expect(git.spawn(['remote'])).to.throw('Git must be installed to use the Heroku CLI. See instructions here: https://git-scm.com') + try { + await spawnPromise + expect.fail('Should have thrown an error') + } catch (error: any) { + expect(error.message).to.contain('Git must be installed to use the Heroku CLI. See instructions here: https://git-scm.com') + } }) - it.skip('spawn passes through all other errors', function () { + it('spawn passes through all other errors', async function () { const err = new Error('Some other error message') const emitter = new EventEmitter() - mock.expects('spawn').withExactArgs('git', ['remote'], {stdio: [0, 1, 2]}).returns(emitter) + spawnStub.returns(emitter) + + const spawnPromise = git.spawn(['remote']) + process.nextTick(() => emitter.emit('error', err)) - return expect(git.spawn(['remote'])).to.throw(err.message) + try { + await spawnPromise + expect.fail('Should have thrown an error') + } catch (error: any) { + expect(error.message).to.equal('Some other error message') + } }) - it.skip('gets heroku git remote config', function () { - mock.expects('execFile').withArgs('git', ['config', 'heroku.remote']).yieldsAsync(null, 'staging') - return git.remoteFromGitConfig() - .then((remote: string | void) => expect(remote).to.equal('staging')) - .then(() => mock.verify()) + it('gets heroku git remote config', async function () { + execFileStub.resolves({stderr: '', stdout: 'staging'}) + + const remote = await git.remoteFromGitConfig() + + expect(remote).to.equal('staging') + expect(execFileStub.calledOnceWith('git', ['config', 'heroku.remote'])).to.be.true }) - it.skip('returns an http git url', function () { + it('returns an https git url', function () { expect(git.url('foo')).to.equal('https://git.heroku.com/foo.git') }) - it.skip('returns an ssh git url', function () { - expect(git.url('foo')).to.equal('git@heroku.com:foo.git') + it('configures git credential helper globally for the Heroku Git host', async function () { + execFileStub.resolves({stderr: '', stdout: ''}) + + await git.configureCredentialHelper() + + expect(execFileStub.calledOnce).to.be.true + const [cmd, args] = execFileStub.firstCall.args + expect(cmd).to.equal('git') + expect(args).to.deep.equal(['config', '--global', 'credential.https://git.heroku.com.helper', '!heroku git:credentials']) + }) + + it('removes git credential helper from global config', async function () { + execFileStub.resolves({stderr: '', stdout: ''}) + + await git.removeCredentialHelper() + + expect(execFileStub.calledOnce).to.be.true + const [cmd, args] = execFileStub.firstCall.args + expect(cmd).to.equal('git') + expect(args).to.deep.equal(['config', '--global', '--unset-all', 'credential.https://git.heroku.com.helper']) + }) + + it('erases stored credentials for the Heroku Git host', async function () { + const emitter = new EventEmitter() as any + emitter.stdin = {end: stub(), write: stub()} + spawnStub.returns(emitter) + + const erasePromise = git.eraseCredentials() + + process.nextTick(() => emitter.emit('close', 0)) + + await erasePromise + + expect(spawnStub.calledOnceWith('git', ['credential', 'reject'], {stdio: ['pipe', 'ignore', 'ignore']})).to.be.true + expect(emitter.stdin.write.calledOnceWith('protocol=https\nhost=git.heroku.com\n\n')).to.be.true + expect(emitter.stdin.end.calledOnce).to.be.true }) }) diff --git a/test/unit/lib/ps-exec/exec.unit.test.ts b/test/unit/lib/ps-exec/exec.unit.test.ts index 43a806b5b4..abd5e4d047 100644 --- a/test/unit/lib/ps-exec/exec.unit.test.ts +++ b/test/unit/lib/ps-exec/exec.unit.test.ts @@ -5,7 +5,12 @@ import {Errors, ux} from '@oclif/core' import {expect} from 'chai' import nock from 'nock' import child from 'node:child_process' -import {restore, SinonStub, stub} from 'sinon' +import { + match, + restore, + SinonStub, + stub, +} from 'sinon' import {HerokuExec} from '../../../../src/lib/ps-exec/exec.js' import {BuildpackInstallation} from '../../../../src/lib/types/fir.js' @@ -426,7 +431,7 @@ describe('HerokuExec', function () { const {oclif} = error as Errors.ExitError expect(uxActionStartStub.calledWith('Initializing feature')).to.be.true expect(uxStdoutStub.called).to.be.true - expect(childExecSyncStub.calledOnce).to.be.true + expect(childExecSyncStub.calledWith(match(/heroku buildpacks:add/))).to.be.true expect(oclif.exit).to.equal(0) } })