From eba6518a68292b0188dc179d721e749b59ae6650 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 8 Jul 2025 23:49:34 -0500 Subject: [PATCH 01/45] ci(release): enable triggering for the alpha branch --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ef0f5a41..752e588f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,7 @@ name: Release - master - next - beta + - alpha - "*.x" permissions: contents: read From 733fe89c342e1dee53e376cb17e7088606ddb8d7 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 9 Jul 2025 00:13:54 -0500 Subject: [PATCH 02/45] fix(deps): depend on the oidc branch for npm --- package-lock.json | 5 ++--- package.json | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 18a6f8e6..bf8e6da0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "lodash-es": "^4.17.21", "nerf-dart": "^1.0.0", "normalize-url": "^8.0.0", - "npm": "^11.4.2", + "npm": "npm/cli#oidc", "rc": "^1.2.8", "read-pkg": "^9.0.0", "registry-auth-token": "^5.0.0", @@ -10212,8 +10212,7 @@ }, "node_modules/npm": { "version": "11.4.2", - "resolved": "https://registry.npmjs.org/npm/-/npm-11.4.2.tgz", - "integrity": "sha512-+QweyLIHtiXW7bZpOu8j2ss5w45CF/6MRqlz8RnKs5KsDeI/4/B+WDGI2un9kQizhFrW9SW1mHQr0GDrrWC/8w==", + "resolved": "git+ssh://git@github.com/npm/cli.git#83ef001d2cd5f2a80171d98779dd2b1870674e63", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", diff --git a/package.json b/package.json index 19bb0f06..230c5910 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "lodash-es": "^4.17.21", "nerf-dart": "^1.0.0", "normalize-url": "^8.0.0", - "npm": "^11.4.2", + "npm": "npm/cli#oidc", "rc": "^1.2.8", "read-pkg": "^9.0.0", "registry-auth-token": "^5.0.0", From 73185a38e2aa8147b3491fcadc861596cb8a3acf Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 9 Jul 2025 00:21:41 -0500 Subject: [PATCH 03/45] fix(token): temporarily disable configuring token into .npmrc since oidc removes the need for the token for #958 --- lib/verify-auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 99e138e9..806ddc13 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -14,7 +14,7 @@ export default async function (npmrc, pkg, context) { } = context; const registry = getRegistry(pkg, context); - await setNpmrcAuth(npmrc, registry, context); + // await setNpmrcAuth(npmrc, registry, context); if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) { try { From 0fc050db79cf81cf265928dd126e8e19ae70ad29 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 9 Jul 2025 00:24:11 -0500 Subject: [PATCH 04/45] fix(whoami): temporarily disable verifying token validity since oidc does not currently work for whoami for #958 --- lib/verify-auth.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 806ddc13..67fb6456 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -16,18 +16,18 @@ export default async function (npmrc, pkg, context) { // await setNpmrcAuth(npmrc, registry, context); - if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) { - try { - const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { - cwd, - env, - preferLocal: true, - }); - whoamiResult.stdout.pipe(stdout, { end: false }); - whoamiResult.stderr.pipe(stderr, { end: false }); - await whoamiResult; - } catch { - throw new AggregateError([getError("EINVALIDNPMTOKEN", { registry })]); - } - } + // if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) { + // try { + // const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { + // cwd, + // env, + // preferLocal: true, + // }); + // whoamiResult.stdout.pipe(stdout, { end: false }); + // whoamiResult.stderr.pipe(stderr, { end: false }); + // await whoamiResult; + // } catch { + // throw new AggregateError([getError("EINVALIDNPMTOKEN", { registry })]); + // } + // } } From 156b6c840c651533337770072589697e3e7fd026 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 9 Jul 2025 00:50:32 -0500 Subject: [PATCH 05/45] fix: raise the minimum semantic-release requirement to v25 BREAKING CHANGE: v25 of semantic-release is now expected for #958 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf8e6da0..ff315cf9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "node": "^22.14.0 || >= 24.2.0" }, "peerDependencies": { - "semantic-release": ">=20.1.0" + "semantic-release": ">=25.0.0-alpha.1" } }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index 230c5910..e228d264 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ ] }, "peerDependencies": { - "semantic-release": ">=20.1.0" + "semantic-release": ">=25.0.0-alpha.1" }, "prettier": { "printWidth": 120, From 65dffcabec61296c17e9ce7202f13894badb3115 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Thu, 10 Jul 2025 07:48:01 -0500 Subject: [PATCH 06/45] Revert "fix: raise the minimum semantic-release requirement to v25" This reverts commit 156b6c840c651533337770072589697e3e7fd026. --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index ff315cf9..bf8e6da0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,7 +43,7 @@ "node": "^22.14.0 || >= 24.2.0" }, "peerDependencies": { - "semantic-release": ">=25.0.0-alpha.1" + "semantic-release": ">=20.1.0" } }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index e228d264..230c5910 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ ] }, "peerDependencies": { - "semantic-release": ">=25.0.0-alpha.1" + "semantic-release": ">=20.1.0" }, "prettier": { "printWidth": 120, From fc30c212bd8f6dc022303bd64145d2c67bf2f9e2 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 25 Jul 2025 13:52:33 -0500 Subject: [PATCH 07/45] fix(deps): depend on the released version of the cli branch now that the oidc features are merged --- package-lock.json | 345 +++++----------------------------------------- package.json | 2 +- 2 files changed, 38 insertions(+), 309 deletions(-) diff --git a/package-lock.json b/package-lock.json index bf8e6da0..8b9ba779 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "lodash-es": "^4.17.21", "nerf-dart": "^1.0.0", "normalize-url": "^8.0.0", - "npm": "npm/cli#oidc", + "npm": "^11.5.1", "rc": "^1.2.8", "read-pkg": "^9.0.0", "registry-auth-token": "^5.0.0", @@ -101,7 +101,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true, "license": "MIT" }, "node_modules/@grpc/grpc-js": { @@ -141,7 +140,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -159,14 +157,12 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -184,7 +180,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.4" @@ -197,7 +192,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", - "dev": true, "license": "ISC" }, "node_modules/@istanbuljs/schema": { @@ -313,7 +307,6 @@ "version": "6.5.1", "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-6.5.1.tgz", "integrity": "sha512-cdV8pGurLK0CifZRilMJbm2CZ3H4Snk8PAqOngj5qmgFLjEllMLvScSZ3XKfd+CK8fo/hrPHO9zazy9OYdvmUg==", - "dev": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", @@ -361,7 +354,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -371,7 +363,6 @@ "version": "7.2.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "dev": true, "license": "ISC", "dependencies": { "abbrev": "^2.0.0" @@ -387,7 +378,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", - "dev": true, "license": "ISC", "dependencies": { "semver": "^7.3.5" @@ -400,7 +390,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", - "dev": true, "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^6.0.0", @@ -420,7 +409,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -436,7 +424,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", - "dev": true, "license": "ISC", "dependencies": { "npm-bundled": "^3.0.0", @@ -453,7 +440,6 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-3.0.6.tgz", "integrity": "sha512-tkYs0OYnzQm6iIRdfy+LcLBjcKuQCeE5YLb8KnrIlutJfheNaPvPpgoFEyEFgbjzl5PLZ3IA/BWAwRU0eHuQDA==", - "dev": true, "license": "ISC", "dependencies": { "@npmcli/name-from-folder": "^2.0.0", @@ -469,7 +455,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-5.0.1.tgz", "integrity": "sha512-qb8Q9wIIlEPj3WeA1Lba91R4ZboPL0uspzV0F9uwP+9AYMVB2zOoa7Pbk12g6D2NHAinSbHh6QYmGuRyHZ874Q==", - "dev": true, "license": "ISC", "dependencies": { "cacache": "^17.0.0", @@ -486,7 +471,6 @@ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", "deprecated": "This functionality has been moved to @npmcli/fs", - "dev": true, "license": "MIT", "dependencies": { "mkdirp": "^1.0.4", @@ -500,7 +484,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -513,7 +496,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz", "integrity": "sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -523,7 +505,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -533,7 +514,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-4.0.1.tgz", "integrity": "sha512-lRCEGdHZomFsURroh522YvA/2cVb9oPIJrjHanCJZkiasz1BzcnLr3tBJhlV7S86MBJBuAQ33is2D60YitZL2Q==", - "dev": true, "license": "ISC", "dependencies": { "@npmcli/git": "^4.1.0", @@ -552,7 +532,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", - "dev": true, "license": "ISC", "dependencies": { "which": "^3.0.0" @@ -565,7 +544,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -581,7 +559,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-3.1.0.tgz", "integrity": "sha512-C/iR0tk7KSKGldibYIB9x8GtO/0Bd0I2mhOaDb8ucQL/bQVTmGoeREaFj64Z5+iCBRf3dQfed0CjJL7I8iTkiQ==", - "dev": true, "license": "ISC", "dependencies": { "postcss-selector-parser": "^6.0.10" @@ -594,7 +571,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", - "dev": true, "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^3.0.0", @@ -611,7 +587,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -783,7 +758,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -3855,7 +3829,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.1.0.tgz", "integrity": "sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.2.0" @@ -3868,7 +3841,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz", "integrity": "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -3878,7 +3850,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-1.0.0.tgz", "integrity": "sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^1.1.0", @@ -3893,7 +3864,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -3906,7 +3876,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -3920,7 +3889,6 @@ "version": "11.1.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, "license": "ISC", "dependencies": { "agentkeepalive": "^4.2.1", @@ -3947,7 +3915,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -3957,7 +3924,6 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", @@ -3975,7 +3941,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -3985,7 +3950,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz", "integrity": "sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.2.0", @@ -4079,7 +4043,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 10" @@ -4089,7 +4052,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", - "dev": true, "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -4099,7 +4061,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", - "dev": true, "license": "MIT", "dependencies": { "@tufjs/canonical-json": "1.0.0", @@ -4285,7 +4246,6 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "dev": true, "license": "MIT", "dependencies": { "humanize-ms": "^1.2.1" @@ -4347,7 +4307,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4360,7 +4319,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -4380,7 +4338,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true, "license": "ISC" }, "node_modules/are-we-there-yet": { @@ -4388,7 +4345,6 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-4.0.2.tgz", "integrity": "sha512-ncSWAawFhKMJDTdoAeOV+jyW1VCMj5QIAwULIBV0SSR7B/RLPPEQiknKcg/RIIZlUQrxELpsxMiTUoAQ4sIUyg==", "deprecated": "This package is no longer supported.", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -4692,7 +4648,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -4737,7 +4692,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-4.0.4.tgz", "integrity": "sha512-cMtq4W5ZsEwcutJrVId+a/tjt8GSbS+h0oNkdl6+6rBuEv8Ot33Bevj5KPm40t309zuhVic8NjpuL42QCiJWWA==", - "dev": true, "license": "ISC", "dependencies": { "cmd-shim": "^6.0.0", @@ -4753,7 +4707,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", @@ -4803,7 +4756,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -4895,7 +4847,6 @@ "version": "17.1.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.4.tgz", "integrity": "sha512-/aJwG2l3ZMJ1xNAnqbMpA40of9dj/pIH3QfiuQSqjfPJF747VR0J/bHn+/KdNnHKc6XQcWt/AfRSBft82W1d2A==", - "dev": true, "license": "ISC", "dependencies": { "@npmcli/fs": "^3.1.0", @@ -4919,7 +4870,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", @@ -4933,7 +4883,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -4943,7 +4892,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4953,7 +4901,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4963,7 +4910,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -4976,7 +4922,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" @@ -4992,7 +4937,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -5010,7 +4954,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -5023,7 +4966,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -5036,7 +4978,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -5174,7 +5115,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -5191,7 +5131,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", - "dev": true, "funding": [ { "type": "github", @@ -5590,7 +5529,6 @@ "version": "6.0.3", "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.3.tgz", "integrity": "sha512-FMabTRlc5t5zjdenF6mS0MBeFZm0XqHqeOkcskKFb/LYCcRQ5fVgLOHVc4Lq9CqABd9zhjwPjMBCJvMCziSVtA==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -5613,7 +5551,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -5626,14 +5563,12 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, "license": "MIT" }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, "license": "ISC", "bin": { "color-support": "bin.js" @@ -5653,7 +5588,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "dev": true, "license": "ISC" }, "node_modules/common-path-prefix": { @@ -5678,7 +5612,6 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, "node_modules/concordance": { @@ -5725,7 +5658,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true, "license": "ISC" }, "node_modules/conventional-changelog-angular": { @@ -5910,7 +5842,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -6003,7 +5934,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -6105,7 +6035,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -6268,7 +6197,6 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, "license": "MIT" }, "node_modules/emittery": { @@ -6302,7 +6230,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -6439,7 +6366,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -6462,7 +6388,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true, "license": "MIT" }, "node_modules/error-ex": { @@ -6735,7 +6660,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", - "dev": true, "license": "Apache-2.0" }, "node_modules/fast_array_intersect": { @@ -6922,7 +6846,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.6", @@ -7021,7 +6944,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" @@ -7034,14 +6956,12 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -7096,7 +7016,6 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-5.0.2.tgz", "integrity": "sha512-pMaFftXPtiGIHCJHdcUUx9Rby/rFT/Kkt3fIIGCs+9PMDIljSyRiqraTlxNtBReJRDfUefpa263RQ3vnp5G/LQ==", "deprecated": "This package is no longer supported.", - "dev": true, "license": "ISC", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -7116,7 +7035,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7126,14 +7044,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/gauge/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -7143,7 +7059,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -7158,7 +7073,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -7317,7 +7231,6 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -7550,14 +7463,12 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true, "license": "ISC" }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7593,7 +7504,6 @@ "version": "6.1.3", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.3.tgz", "integrity": "sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw==", - "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^7.5.1" @@ -7613,14 +7523,12 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "dev": true, "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, "license": "MIT", "dependencies": { "@tootallnate/once": "2", @@ -7635,7 +7543,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -7685,7 +7592,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.0.0" @@ -7695,7 +7601,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -7750,7 +7655,6 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", - "dev": true, "license": "ISC", "dependencies": { "minimatch": "^9.0.0" @@ -7815,7 +7719,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" @@ -7849,7 +7752,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true, "license": "ISC" }, "node_modules/inflight": { @@ -7857,7 +7759,6 @@ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -7868,7 +7769,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -7913,7 +7813,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dev": true, "license": "MIT", "dependencies": { "jsbn": "1.1.0", @@ -8055,7 +7954,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -8177,7 +8075,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true, "license": "MIT" }, "node_modules/is-map": { @@ -8551,7 +8448,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -8606,7 +8502,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true, "license": "MIT" }, "node_modules/json-buffer": { @@ -8647,7 +8542,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", - "dev": true, "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8664,7 +8558,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", - "dev": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" @@ -8712,7 +8605,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true, "engines": [ "node >= 0.2.0" ], @@ -8722,14 +8614,12 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", "integrity": "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==", - "dev": true, "license": "MIT" }, "node_modules/just-diff-apply": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz", "integrity": "sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==", - "dev": true, "license": "MIT" }, "node_modules/keyv": { @@ -8924,7 +8814,6 @@ "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -8983,7 +8872,6 @@ "version": "10.2.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "dev": true, "license": "ISC", "dependencies": { "agentkeepalive": "^4.2.1", @@ -9011,7 +8899,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", - "dev": true, "license": "ISC", "dependencies": { "@gar/promisify": "^1.1.3", @@ -9025,7 +8912,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -9038,7 +8924,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", @@ -9052,7 +8937,6 @@ "version": "16.1.3", "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", - "dev": true, "license": "ISC", "dependencies": { "@npmcli/fs": "^2.1.0", @@ -9082,7 +8966,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -9092,7 +8975,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -9102,7 +8984,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -9116,7 +8997,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -9136,7 +9016,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -9150,7 +9029,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9160,7 +9038,6 @@ "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -9173,7 +9050,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9186,7 +9062,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -9199,7 +9074,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" @@ -9215,7 +9089,6 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.1.1" @@ -9228,7 +9101,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -9246,7 +9118,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -9256,7 +9127,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", - "dev": true, "license": "ISC", "dependencies": { "unique-slug": "^3.0.0" @@ -9269,7 +9139,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", - "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" @@ -9493,7 +9362,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -9518,7 +9386,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -9528,7 +9395,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -9541,7 +9407,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9554,7 +9419,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^3.1.6", @@ -9572,7 +9436,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9585,7 +9448,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -9598,7 +9460,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9611,7 +9472,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.2.tgz", "integrity": "sha512-myxeeTm57lYs8pH2nxPzmEEg8DGIgW+9mv6D4JZD2pa81I/OBjeU7PtICXV6c9eRGTA5JMDsuIPUZRCyBMYNhg==", - "dev": true, "license": "MIT", "dependencies": { "jsonparse": "^1.3.1", @@ -9622,7 +9482,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9635,7 +9494,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -9648,7 +9506,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9661,7 +9518,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -9674,7 +9530,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9687,7 +9542,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -9701,7 +9555,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9714,7 +9567,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "dist/cjs/src/bin.js" @@ -9747,7 +9599,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -9774,7 +9625,6 @@ "version": "0.6.4", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6" @@ -9847,7 +9697,6 @@ "version": "9.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", - "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", @@ -9885,14 +9734,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, "license": "ISC" }, "node_modules/node-gyp/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9903,7 +9750,6 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "deprecated": "This package is no longer supported.", - "dev": true, "license": "ISC", "dependencies": { "delegates": "^1.0.0", @@ -9917,7 +9763,6 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -9928,7 +9773,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -9938,14 +9782,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/node-gyp/node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -9958,7 +9800,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -9972,7 +9813,6 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "deprecated": "This package is no longer supported.", - "dev": true, "license": "ISC", "dependencies": { "aproba": "^1.0.3 || ^2.0.0", @@ -9993,7 +9833,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -10014,7 +9853,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10024,7 +9862,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -10037,7 +9874,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -10047,7 +9883,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -10060,7 +9895,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", - "dev": true, "license": "ISC", "dependencies": { "abbrev": "^1.0.0" @@ -10077,7 +9911,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "deprecated": "This package is no longer supported.", - "dev": true, "license": "ISC", "dependencies": { "are-we-there-yet": "^3.0.0", @@ -10093,14 +9926,12 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, "license": "ISC" }, "node_modules/node-gyp/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -10115,7 +9946,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -10128,7 +9958,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -10186,7 +10015,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^6.0.0", @@ -10211,8 +10039,9 @@ } }, "node_modules/npm": { - "version": "11.4.2", - "resolved": "git+ssh://git@github.com/npm/cli.git#83ef001d2cd5f2a80171d98779dd2b1870674e63", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-11.5.1.tgz", + "integrity": "sha512-Iy5vXZ55m8tIaSCz6bqQf9+W5XbPfoyURsgWLjOkFglqHTep6RDZqRj2sfYGeRyZvGu2HuJWm0lux0rxPQ29lQ==", "bundleDependencies": [ "@isaacs/string-locale-compare", "@npmcli/arborist", @@ -10291,8 +10120,8 @@ ], "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/arborist": "^9.1.2", - "@npmcli/config": "^10.3.0", + "@npmcli/arborist": "^9.1.3", + "@npmcli/config": "^10.3.1", "@npmcli/fs": "^4.0.0", "@npmcli/map-workspaces": "^4.0.2", "@npmcli/package-json": "^6.2.0", @@ -10304,7 +10133,7 @@ "archy": "~1.0.0", "cacache": "^19.0.1", "chalk": "^5.4.1", - "ci-info": "^4.2.0", + "ci-info": "^4.3.0", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", @@ -10316,12 +10145,12 @@ "is-cidr": "^5.1.1", "json-parse-even-better-errors": "^4.0.0", "libnpmaccess": "^10.0.1", - "libnpmdiff": "^8.0.5", - "libnpmexec": "^10.1.4", - "libnpmfund": "^7.0.5", + "libnpmdiff": "^8.0.6", + "libnpmexec": "^10.1.5", + "libnpmfund": "^7.0.6", "libnpmorg": "^8.0.0", - "libnpmpack": "^9.0.5", - "libnpmpublish": "^11.0.1", + "libnpmpack": "^9.0.6", + "libnpmpublish": "^11.1.0", "libnpmsearch": "^9.0.0", "libnpmteam": "^8.0.1", "libnpmversion": "^8.0.1", @@ -10332,7 +10161,7 @@ "ms": "^2.1.2", "node-gyp": "^11.2.0", "nopt": "^8.1.0", - "normalize-package-data": "^7.0.0", + "normalize-package-data": "^7.0.1", "npm-audit-report": "^6.0.0", "npm-install-checks": "^7.1.1", "npm-package-arg": "^12.0.2", @@ -10354,7 +10183,7 @@ "text-table": "~0.2.0", "tiny-relative-date": "^1.3.0", "treeverse": "^3.0.0", - "validate-npm-package-name": "^6.0.1", + "validate-npm-package-name": "^6.0.2", "which": "^5.0.0" }, "bin": { @@ -10369,7 +10198,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", - "dev": true, "license": "ISC", "dependencies": { "npm-normalize-package-bin": "^3.0.0" @@ -10382,7 +10210,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "semver": "^7.1.1" @@ -10395,7 +10222,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -10405,7 +10231,6 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", - "dev": true, "license": "ISC", "dependencies": { "hosted-git-info": "^6.0.0", @@ -10421,7 +10246,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", - "dev": true, "license": "ISC", "dependencies": { "ignore-walk": "^6.0.0" @@ -10434,7 +10258,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.2.tgz", "integrity": "sha512-1dKY+86/AIiq1tkKVD3l0WI+Gd3vkknVGAggsFeBkTvbhMQ1OND/LKkYv4JtXPKUJ8bOTCyLiqEg2P6QNdK+Gg==", - "dev": true, "license": "ISC", "dependencies": { "npm-install-checks": "^6.0.0", @@ -10450,7 +10273,6 @@ "version": "14.0.5", "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", - "dev": true, "license": "ISC", "dependencies": { "make-fetch-happen": "^11.0.0", @@ -10469,7 +10291,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -10482,7 +10303,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -10496,7 +10316,6 @@ "version": "11.1.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, "license": "ISC", "dependencies": { "agentkeepalive": "^4.2.1", @@ -10523,7 +10342,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -10533,7 +10351,6 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", @@ -10551,7 +10368,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -10766,7 +10582,7 @@ } }, "node_modules/npm/node_modules/@npmcli/arborist": { - "version": "9.1.2", + "version": "9.1.3", "inBundle": true, "license": "ISC", "dependencies": { @@ -10813,7 +10629,7 @@ } }, "node_modules/npm/node_modules/@npmcli/config": { - "version": "10.3.0", + "version": "10.3.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -10889,7 +10705,7 @@ } }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { - "version": "9.0.0", + "version": "9.0.1", "inBundle": true, "license": "ISC", "dependencies": { @@ -11088,7 +10904,7 @@ } }, "node_modules/npm/node_modules/agent-base": { - "version": "7.1.3", + "version": "7.1.4", "inBundle": true, "license": "MIT", "engines": { @@ -11115,7 +10931,7 @@ } }, "node_modules/npm/node_modules/aproba": { - "version": "2.0.0", + "version": "2.1.0", "inBundle": true, "license": "ISC" }, @@ -11262,7 +11078,7 @@ } }, "node_modules/npm/node_modules/ci-info": { - "version": "4.2.0", + "version": "4.3.0", "funding": [ { "type": "github", @@ -11693,11 +11509,11 @@ } }, "node_modules/npm/node_modules/libnpmdiff": { - "version": "8.0.5", + "version": "8.0.6", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.2", + "@npmcli/arborist": "^9.1.3", "@npmcli/installed-package-contents": "^3.0.0", "binary-extensions": "^3.0.0", "diff": "^7.0.0", @@ -11711,11 +11527,11 @@ } }, "node_modules/npm/node_modules/libnpmexec": { - "version": "10.1.4", + "version": "10.1.5", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.2", + "@npmcli/arborist": "^9.1.3", "@npmcli/package-json": "^6.1.1", "@npmcli/run-script": "^9.0.1", "ci-info": "^4.0.0", @@ -11732,11 +11548,11 @@ } }, "node_modules/npm/node_modules/libnpmfund": { - "version": "7.0.5", + "version": "7.0.6", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.2" + "@npmcli/arborist": "^9.1.3" }, "engines": { "node": "^20.17.0 || >=22.9.0" @@ -11755,11 +11571,11 @@ } }, "node_modules/npm/node_modules/libnpmpack": { - "version": "9.0.5", + "version": "9.0.6", "inBundle": true, "license": "ISC", "dependencies": { - "@npmcli/arborist": "^9.1.2", + "@npmcli/arborist": "^9.1.3", "@npmcli/run-script": "^9.0.1", "npm-package-arg": "^12.0.0", "pacote": "^21.0.0" @@ -11769,7 +11585,7 @@ } }, "node_modules/npm/node_modules/libnpmpublish": { - "version": "11.0.1", + "version": "11.1.0", "inBundle": true, "license": "ISC", "dependencies": { @@ -12126,7 +11942,7 @@ } }, "node_modules/npm/node_modules/normalize-package-data": { - "version": "7.0.0", + "version": "7.0.1", "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -12532,7 +12348,7 @@ } }, "node_modules/npm/node_modules/socks": { - "version": "2.8.5", + "version": "2.8.6", "inBundle": true, "license": "MIT", "dependencies": { @@ -12756,7 +12572,7 @@ } }, "node_modules/npm/node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", + "version": "4.0.3", "inBundle": true, "license": "MIT", "engines": { @@ -12775,13 +12591,13 @@ } }, "node_modules/npm/node_modules/tuf-js": { - "version": "3.0.1", + "version": "3.1.0", "inBundle": true, "license": "MIT", "dependencies": { "@tufjs/models": "3.0.1", - "debug": "^4.3.6", - "make-fetch-happen": "^14.0.1" + "debug": "^4.4.1", + "make-fetch-happen": "^14.0.3" }, "engines": { "node": "^18.17.0 || >=20.5.0" @@ -12833,7 +12649,7 @@ } }, "node_modules/npm/node_modules/validate-npm-package-name": { - "version": "6.0.1", + "version": "6.0.2", "inBundle": true, "license": "ISC", "engines": { @@ -12985,7 +12801,6 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-7.0.1.tgz", "integrity": "sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg==", "deprecated": "This package is no longer supported.", - "dev": true, "license": "ISC", "dependencies": { "are-we-there-yet": "^4.0.0", @@ -13118,7 +12933,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -13314,7 +13128,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/package-manager-detector": { @@ -13328,7 +13141,6 @@ "version": "15.2.0", "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", - "dev": true, "license": "ISC", "dependencies": { "@npmcli/git": "^4.0.0", @@ -13361,7 +13173,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -13371,7 +13182,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -13381,7 +13191,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -13394,7 +13203,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -13412,7 +13220,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -13425,7 +13232,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -13461,7 +13267,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz", "integrity": "sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==", - "dev": true, "license": "ISC", "dependencies": { "json-parse-even-better-errors": "^3.0.0", @@ -13548,7 +13353,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13567,7 +13371,6 @@ "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -13584,7 +13387,6 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, "license": "ISC" }, "node_modules/path-type": { @@ -13779,7 +13581,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -13824,7 +13625,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -13851,7 +13651,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", - "dev": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" @@ -13861,7 +13660,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.2.tgz", "integrity": "sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA==", - "dev": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" @@ -13887,14 +13685,12 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true, "license": "ISC" }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, "license": "MIT", "dependencies": { "err-code": "^2.0.2", @@ -13908,7 +13704,6 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -14069,7 +13864,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -14080,7 +13874,6 @@ "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", "deprecated": "This package is no longer supported. Please use @npmcli/package-json instead.", - "dev": true, "license": "ISC", "dependencies": { "glob": "^10.2.2", @@ -14096,7 +13889,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", - "dev": true, "license": "ISC", "dependencies": { "json-parse-even-better-errors": "^3.0.0", @@ -14208,7 +14000,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -14367,7 +14158,6 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -14383,7 +14173,6 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -14395,7 +14184,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -14416,7 +14204,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -14486,7 +14273,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -14542,7 +14328,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/semantic-release": { @@ -14696,7 +14482,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, "license": "ISC" }, "node_modules/set-function-length": { @@ -14980,7 +14765,6 @@ "version": "1.9.0", "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.9.0.tgz", "integrity": "sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^1.1.0", @@ -15000,7 +14784,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -15013,7 +14796,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -15027,7 +14809,6 @@ "version": "11.1.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, "license": "ISC", "dependencies": { "agentkeepalive": "^4.2.1", @@ -15054,7 +14835,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -15064,7 +14844,6 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", @@ -15082,7 +14861,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -15153,7 +14931,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6.0.0", @@ -15164,7 +14941,6 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", - "dev": true, "license": "MIT", "dependencies": { "ip-address": "^9.0.5", @@ -15179,7 +14955,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "^6.0.2", @@ -15194,7 +14969,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -15273,7 +15047,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, "license": "BSD-3-Clause" }, "node_modules/ssh2": { @@ -15298,7 +15071,6 @@ "version": "10.0.6", "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" @@ -15409,7 +15181,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -15438,7 +15209,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -15453,7 +15223,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -15463,14 +15232,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -15480,7 +15247,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -15552,7 +15318,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -15569,7 +15334,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -15582,7 +15346,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -15823,7 +15586,6 @@ "version": "7.4.3", "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", - "dev": true, "license": "ISC", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", @@ -15878,7 +15640,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.1.2" @@ -15891,7 +15652,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=18" @@ -16100,7 +15860,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -16117,7 +15876,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", - "dev": true, "license": "MIT", "dependencies": { "@tufjs/models": "1.0.4", @@ -16132,7 +15890,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "license": "MIT", "dependencies": { "debug": "4" @@ -16145,7 +15902,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, "license": "MIT", "dependencies": { "agent-base": "6", @@ -16159,7 +15915,6 @@ "version": "11.1.1", "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, "license": "ISC", "dependencies": { "agentkeepalive": "^4.2.1", @@ -16186,7 +15941,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -16196,7 +15950,6 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", @@ -16214,7 +15967,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -16393,7 +16145,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, "license": "ISC", "dependencies": { "unique-slug": "^4.0.0" @@ -16406,7 +16157,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" @@ -16460,7 +16210,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, "node_modules/uuid": { @@ -16506,7 +16255,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -16516,7 +16264,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", - "dev": true, "license": "ISC" }, "node_modules/webidl-conversions": { @@ -16655,7 +16402,6 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" @@ -16665,7 +16411,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16675,14 +16420,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/wide-align/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16692,7 +16435,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -16707,7 +16449,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -16727,7 +16468,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -16746,7 +16486,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -16764,7 +16503,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16774,7 +16512,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -16790,14 +16527,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16807,7 +16542,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -16822,7 +16556,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -16835,14 +16568,12 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/wrap-ansi/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -16860,7 +16591,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -16901,7 +16631,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, "license": "ISC" }, "node_modules/yargs": { diff --git a/package.json b/package.json index 230c5910..9a41cfca 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "lodash-es": "^4.17.21", "nerf-dart": "^1.0.0", "normalize-url": "^8.0.0", - "npm": "npm/cli#oidc", + "npm": "^11.5.1", "rc": "^1.2.8", "read-pkg": "^9.0.0", "registry-auth-token": "^5.0.0", From 841dc67dd094d51f3e576f02c2bcd7303f36794f Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sun, 27 Jul 2025 20:49:36 -0500 Subject: [PATCH 08/45] feat(auth): attempt a dry-run publish to determine auth status --- lib/verify-auth.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 67fb6456..ed4fc400 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -16,8 +16,9 @@ export default async function (npmrc, pkg, context) { // await setNpmrcAuth(npmrc, registry, context); - // if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) { + if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) { // try { + const publishDryRunResult = execa("npm", ["publish", "--dry-run", "--tag=semantic-release-auth-check"], {cwd, env, preferLocal: true}); // const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { // cwd, // env, @@ -26,8 +27,11 @@ export default async function (npmrc, pkg, context) { // whoamiResult.stdout.pipe(stdout, { end: false }); // whoamiResult.stderr.pipe(stderr, { end: false }); // await whoamiResult; + publishDryRunResult.stdout.pipe(stdout, { end: false }); + publishDryRunResult.stderr.pipe(stderr, { end: false }); + await publishDryRunResult; // } catch { // throw new AggregateError([getError("EINVALIDNPMTOKEN", { registry })]); // } - // } + } } From 8f88e9dafb6c85a61e020044838507a4d1efeda5 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sun, 27 Jul 2025 21:23:28 -0500 Subject: [PATCH 09/45] fix(auth): throw error if dry-run publish determines lack of auth --- lib/verify-auth.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index ed4fc400..0b42eacd 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -28,8 +28,13 @@ export default async function (npmrc, pkg, context) { // whoamiResult.stderr.pipe(stderr, { end: false }); // await whoamiResult; publishDryRunResult.stdout.pipe(stdout, { end: false }); - publishDryRunResult.stderr.pipe(stderr, { end: false }); - await publishDryRunResult; + publishDryRunResult.stderr.pipe(stderr, { end: false, lines: true }); + const {stdout} = await publishDryRunResult; + stdout.forEach((line) => { + if (line.includes("warn This command requires you to be logged in to https://registry.npmjs.org/")) { + throw new Error('no auth context'); + } + }) // } catch { // throw new AggregateError([getError("EINVALIDNPMTOKEN", { registry })]); // } From 1967d72e61b1178c9f380ed9a2f35d1434878f52 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sun, 27 Jul 2025 21:29:49 -0500 Subject: [PATCH 10/45] fix(error): throw an aggregate error rather than a simple error --- lib/verify-auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 0b42eacd..297f34d7 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -32,7 +32,7 @@ export default async function (npmrc, pkg, context) { const {stdout} = await publishDryRunResult; stdout.forEach((line) => { if (line.includes("warn This command requires you to be logged in to https://registry.npmjs.org/")) { - throw new Error('no auth context'); + throw new AggregateError([new Error('no auth context')]); } }) // } catch { From 4ab3e74eba4cb44e6a9a6afd80dded45d63fd5e0 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sun, 27 Jul 2025 22:08:13 -0500 Subject: [PATCH 11/45] fix(stdout): fix the reference of stdout from execa --- lib/verify-auth.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 297f34d7..813fdcc9 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -1,9 +1,7 @@ import { execa } from "execa"; import normalizeUrl from "normalize-url"; import AggregateError from "aggregate-error"; -import getError from "./get-error.js"; import getRegistry from "./get-registry.js"; -import setNpmrcAuth from "./set-npmrc-auth.js"; export default async function (npmrc, pkg, context) { const { @@ -18,7 +16,7 @@ export default async function (npmrc, pkg, context) { if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) { // try { - const publishDryRunResult = execa("npm", ["publish", "--dry-run", "--tag=semantic-release-auth-check"], {cwd, env, preferLocal: true}); + const publishDryRunResult = execa("npm", ["publish", "--dry-run", "--tag=semantic-release-auth-check"], {cwd, env, preferLocal: true, lines: true}); // const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { // cwd, // env, @@ -28,9 +26,9 @@ export default async function (npmrc, pkg, context) { // whoamiResult.stderr.pipe(stderr, { end: false }); // await whoamiResult; publishDryRunResult.stdout.pipe(stdout, { end: false }); - publishDryRunResult.stderr.pipe(stderr, { end: false, lines: true }); - const {stdout} = await publishDryRunResult; - stdout.forEach((line) => { + publishDryRunResult.stderr.pipe(stderr, { end: false }); + const {stdout: execaStdout} = await publishDryRunResult; + execaStdout.forEach((line) => { if (line.includes("warn This command requires you to be logged in to https://registry.npmjs.org/")) { throw new AggregateError([new Error('no auth context')]); } From 86f65a6de07b88420ffd6204c1ca917ba77cfbec Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sun, 27 Jul 2025 22:25:37 -0500 Subject: [PATCH 12/45] fix(dry-run): look for the warning in stderr output rather than stdout --- lib/verify-auth.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 813fdcc9..8a211317 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -27,8 +27,8 @@ export default async function (npmrc, pkg, context) { // await whoamiResult; publishDryRunResult.stdout.pipe(stdout, { end: false }); publishDryRunResult.stderr.pipe(stderr, { end: false }); - const {stdout: execaStdout} = await publishDryRunResult; - execaStdout.forEach((line) => { + const {stderr: execaStderr} = await publishDryRunResult; + execaStderr.forEach((line) => { if (line.includes("warn This command requires you to be logged in to https://registry.npmjs.org/")) { throw new AggregateError([new Error('no auth context')]); } From bee5db6550e44163624a0f3ecd8abb6fb79695cc Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sun, 27 Jul 2025 22:34:52 -0500 Subject: [PATCH 13/45] fix(dry-run): stop searching for "warn" to avoid ANSI color complications --- lib/verify-auth.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 8a211317..c1951f3e 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -29,7 +29,7 @@ export default async function (npmrc, pkg, context) { publishDryRunResult.stderr.pipe(stderr, { end: false }); const {stderr: execaStderr} = await publishDryRunResult; execaStderr.forEach((line) => { - if (line.includes("warn This command requires you to be logged in to https://registry.npmjs.org/")) { + if (line.includes("This command requires you to be logged in to https://registry.npmjs.org/")) { throw new AggregateError([new Error('no auth context')]); } }) From 9bdfd068d1888a5536d603e8fc27d16390fe67fd Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 1 Aug 2025 16:50:50 -0500 Subject: [PATCH 14/45] docs(oidc): update the authentication details to include recommending oidc --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 21f6662a..27637057 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,17 @@ The plugin can be configured in the [**semantic-release** configuration file](ht ### npm registry authentication -The npm [token](https://docs.npmjs.com/about-access-tokens) authentication configuration is **required** and can be set via [environment variables](#environment-variables). +### Official Registry -Automation tokens are recommended since they can be used for an automated workflow, even when your account is configured to use the [`auth-and-writes` level of 2FA](https://docs.npmjs.com/about-two-factor-authentication#authorization-and-writes). +When publishing to the [official registry](https://registry.npmjs.org/), it is recommended to publish with authentication that is intended for automation: + +* [OIDC publishing](https://docs.npmjs.com/trusted-publishers) is recommended when publishing from a CI provider +* [Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended when publishing from a CI provider that is not supported by npm for OIDC publishing, and can be set via [environment variables](#environment-variables) + +### Alternative Registries + +The npm token authentication configuration is **required** and can be set via [environment variables](#environment-variables). +See the documentation for your registry for details on how to create a token for automation. ### npm provenance @@ -49,6 +57,9 @@ Since semantic-release wraps the npm publish command, configuring provenance is Instead, provenance can be configured through the [other configuration options exposed by npm](https://docs.npmjs.com/generating-provenance-statements#using-third-party-package-publishing-tools). Provenance applies specifically to publishing, so our recommendation is to configure under `publishConfig` within the `package.json`. +> [!NOTE] +> When publishing a public package to the official registry with OIDC, [npm provenance is automatically enabled](https://docs.npmjs.com/trusted-publishers#automatic-provenance-generation) and does not require any additional configuration. + #### npm provenance on GitHub Actions For package provenance to be signed on the GitHub Actions CI the following permission is required From e24967d7c15f29eef164fd2825f0c29850e629b2 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 1 Aug 2025 16:55:00 -0500 Subject: [PATCH 15/45] feat(auth-error): updated messaging for auth failure to be less token specific --- lib/definitions/errors.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index aaa5056c..47e8decd 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -45,17 +45,14 @@ Please make sure to create an [npm token](https://docs.npmjs.com/getting-started }; } -export function EINVALIDNPMTOKEN({ registry }) { +export function EINVALIDNPMAUTH({ registry }) { return { - message: "Invalid npm token.", - details: `The [npm token](${linkify( + message: "Invalid npm authentication.", + details: `The [authentication required to publish](${linkify( "README.md#npm-registry-authentication" - )}) configured in the \`NPM_TOKEN\` environment variable must be a valid [token](https://docs.npmjs.com/getting-started/working_with_tokens) allowing to publish to the registry \`${registry}\`. + )}) is not configured as needed. -If you are using Two Factor Authentication for your account, set its level to ["Authorization only"](https://docs.npmjs.com/getting-started/using-two-factor-authentication#levels-of-authentication) in your account settings. **semantic-release** cannot publish with the default " -Authorization and writes" level. - -Please make sure to set the \`NPM_TOKEN\` environment variable in your CI with the exact value of the npm token.`, +Please verify your authentication configuration.`, }; } From f5c8d853bb1b1a5dfece1024fd07de382ae4e543 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 1 Aug 2025 17:04:11 -0500 Subject: [PATCH 16/45] fix(auth): throw appropriate error when auth context fails to enable publishing --- lib/definitions/errors.js | 2 +- lib/verify-auth.js | 18 ++++++++++++------ test/integration.test.js | 4 ++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index 47e8decd..180b1bf6 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -45,7 +45,7 @@ Please make sure to create an [npm token](https://docs.npmjs.com/getting-started }; } -export function EINVALIDNPMAUTH({ registry }) { +export function EINVALIDNPMAUTH() { return { message: "Invalid npm authentication.", details: `The [authentication required to publish](${linkify( diff --git a/lib/verify-auth.js b/lib/verify-auth.js index c1951f3e..ef1bafc2 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -2,6 +2,12 @@ import { execa } from "execa"; import normalizeUrl from "normalize-url"; import AggregateError from "aggregate-error"; import getRegistry from "./get-registry.js"; +import setNpmrcAuth from "./set-npmrc-auth.js"; +import getError from "./get-error.js"; + +function registryIsDefault(registry, DEFAULT_NPM_REGISTRY) { + return normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY); +} export default async function (npmrc, pkg, context) { const { @@ -14,7 +20,7 @@ export default async function (npmrc, pkg, context) { // await setNpmrcAuth(npmrc, registry, context); - if (normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY)) { + if (registryIsDefault(registry, DEFAULT_NPM_REGISTRY)) { // try { const publishDryRunResult = execa("npm", ["publish", "--dry-run", "--tag=semantic-release-auth-check"], {cwd, env, preferLocal: true, lines: true}); // const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { @@ -27,14 +33,14 @@ export default async function (npmrc, pkg, context) { // await whoamiResult; publishDryRunResult.stdout.pipe(stdout, { end: false }); publishDryRunResult.stderr.pipe(stderr, { end: false }); - const {stderr: execaStderr} = await publishDryRunResult; - execaStderr.forEach((line) => { - if (line.includes("This command requires you to be logged in to https://registry.npmjs.org/")) { - throw new AggregateError([new Error('no auth context')]); + + (await publishDryRunResult).stderr.forEach((line) => { + if (line.includes("This command requires you to be logged in to ")) { + throw new AggregateError([getError("EINVALIDNPMAUTH" )]); } }) // } catch { - // throw new AggregateError([getError("EINVALIDNPMTOKEN", { registry })]); + // throw new AggregateError([getError("EINVALIDNPMAUTH", { registry })]); // } } } diff --git a/test/integration.test.js b/test/integration.test.js index 8c12ae7a..a632e40a 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -107,8 +107,8 @@ test("Throws error if NPM token is invalid", async (t) => { ); t.is(error.name, "SemanticReleaseError"); - t.is(error.code, "EINVALIDNPMTOKEN"); - t.is(error.message, "Invalid npm token."); + t.is(error.code, "EINVALIDNPMAUTH"); + t.is(error.message, "Invalid npm authentication."); }); test("Skip Token validation if the registry configured is not the default one", async (t) => { From bd7acfbca13ccdae2874ec53f6a820d579840e10 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Fri, 15 Aug 2025 16:52:00 -0500 Subject: [PATCH 17/45] feat(verify-auth): configure npmrc details again --- lib/verify-auth.js | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index ef1bafc2..9b9f921d 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -9,38 +9,47 @@ function registryIsDefault(registry, DEFAULT_NPM_REGISTRY) { return normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY); } +function targetingDefaultRegistryButNoTokenProvided(registry, DEFAULT_NPM_REGISTRY, errors) { + return registryIsDefault(registry, DEFAULT_NPM_REGISTRY) && errors.length === 1 && errors[0].code === "ENONPMTOKEN"; +} + export default async function (npmrc, pkg, context) { const { cwd, env: { DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/", ...env }, stdout, stderr, + logger, } = context; const registry = getRegistry(pkg, context); - // await setNpmrcAuth(npmrc, registry, context); + try { + logger.log("setting npmrc auth for registry", registry); + await setNpmrcAuth(npmrc, registry, context); + } catch (aggregateError) { + const { errors } = aggregateError; + + if (targetingDefaultRegistryButNoTokenProvided(registry, DEFAULT_NPM_REGISTRY, errors)) { + logger.log("NPM_TOKEN was not provided for the default npm registry."); + } else { + throw aggregateError; + } + } if (registryIsDefault(registry, DEFAULT_NPM_REGISTRY)) { - // try { - const publishDryRunResult = execa("npm", ["publish", "--dry-run", "--tag=semantic-release-auth-check"], {cwd, env, preferLocal: true, lines: true}); - // const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { - // cwd, - // env, - // preferLocal: true, - // }); - // whoamiResult.stdout.pipe(stdout, { end: false }); - // whoamiResult.stderr.pipe(stderr, { end: false }); - // await whoamiResult; + const publishDryRunResult = execa( + "npm", + ["publish", "--dry-run", "--tag=semantic-release-auth-check", "--userconfig", npmrc, "--registry", registry], + { cwd, env, preferLocal: true, lines: true } + ); + publishDryRunResult.stdout.pipe(stdout, { end: false }); publishDryRunResult.stderr.pipe(stderr, { end: false }); (await publishDryRunResult).stderr.forEach((line) => { if (line.includes("This command requires you to be logged in to ")) { - throw new AggregateError([getError("EINVALIDNPMAUTH" )]); + throw new AggregateError([getError("EINVALIDNPMAUTH")]); } - }) - // } catch { - // throw new AggregateError([getError("EINVALIDNPMAUTH", { registry })]); - // } + }); } } From dd9ea8606f8b5bedfd383ad51053edd268668082 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 09:33:01 -0500 Subject: [PATCH 18/45] refactor(registry): extract a constant rather than repeating the url string --- lib/definitions/constants.js | 1 + lib/get-registry.js | 7 ++----- lib/get-release-info.js | 3 ++- lib/set-npmrc-auth.js | 3 ++- lib/verify-auth.js | 3 ++- test/get-registry.test.js | 5 +++-- test/get-release-info.test.js | 3 ++- test/verify-auth.test.js | 3 ++- 8 files changed, 16 insertions(+), 12 deletions(-) create mode 100644 lib/definitions/constants.js diff --git a/lib/definitions/constants.js b/lib/definitions/constants.js new file mode 100644 index 00000000..9f59ec7e --- /dev/null +++ b/lib/definitions/constants.js @@ -0,0 +1 @@ +export const OFFICIAL_REGISTRY = "https://registry.npmjs.org/"; diff --git a/lib/get-registry.js b/lib/get-registry.js index 4cb42dea..360621eb 100644 --- a/lib/get-registry.js +++ b/lib/get-registry.js @@ -1,6 +1,7 @@ import path from "path"; import rc from "rc"; import getRegistryUrl from "registry-auth-token/registry-url.js"; +import { OFFICIAL_REGISTRY } from "./definitions/constants.js"; export default function ({ publishConfig: { registry } = {}, name }, { cwd, env }) { return ( @@ -8,11 +9,7 @@ export default function ({ publishConfig: { registry } = {}, name }, { cwd, env env.NPM_CONFIG_REGISTRY || getRegistryUrl( name.split("/")[0], - rc( - "npm", - { registry: "https://registry.npmjs.org/" }, - { config: env.NPM_CONFIG_USERCONFIG || path.resolve(cwd, ".npmrc") } - ) + rc("npm", { registry: OFFICIAL_REGISTRY }, { config: env.NPM_CONFIG_USERCONFIG || path.resolve(cwd, ".npmrc") }) ) ); } diff --git a/lib/get-release-info.js b/lib/get-release-info.js index 84546ac3..80a4e08c 100644 --- a/lib/get-release-info.js +++ b/lib/get-release-info.js @@ -1,8 +1,9 @@ import normalizeUrl from "normalize-url"; +import { OFFICIAL_REGISTRY } from "./definitions/constants.js"; export default function ( { name }, - { env: { DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/" }, nextRelease: { version } }, + { env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY }, nextRelease: { version } }, distTag, registry ) { diff --git a/lib/set-npmrc-auth.js b/lib/set-npmrc-auth.js index 6e92d080..0925bc5b 100644 --- a/lib/set-npmrc-auth.js +++ b/lib/set-npmrc-auth.js @@ -5,12 +5,13 @@ import getAuthToken from "registry-auth-token"; import nerfDart from "nerf-dart"; import AggregateError from "aggregate-error"; import getError from "./get-error.js"; +import { OFFICIAL_REGISTRY } from "./definitions/constants.js"; export default async function (npmrc, registry, { cwd, env: { NPM_TOKEN, NPM_CONFIG_USERCONFIG }, logger }) { logger.log("Verify authentication for registry %s", registry); const { configs, ...rcConfig } = rc( "npm", - { registry: "https://registry.npmjs.org/" }, + { registry: OFFICIAL_REGISTRY }, { config: NPM_CONFIG_USERCONFIG || path.resolve(cwd, ".npmrc") } ); diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 9b9f921d..908c7d9d 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -4,6 +4,7 @@ import AggregateError from "aggregate-error"; import getRegistry from "./get-registry.js"; import setNpmrcAuth from "./set-npmrc-auth.js"; import getError from "./get-error.js"; +import { OFFICIAL_REGISTRY } from "./definitions/constants.js"; function registryIsDefault(registry, DEFAULT_NPM_REGISTRY) { return normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY); @@ -16,7 +17,7 @@ function targetingDefaultRegistryButNoTokenProvided(registry, DEFAULT_NPM_REGIST export default async function (npmrc, pkg, context) { const { cwd, - env: { DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/", ...env }, + env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY, ...env }, stdout, stderr, logger, diff --git a/test/get-registry.test.js b/test/get-registry.test.js index 2cad03f7..e6d444e2 100644 --- a/test/get-registry.test.js +++ b/test/get-registry.test.js @@ -3,11 +3,12 @@ import test from "ava"; import fs from "fs-extra"; import { temporaryDirectory } from "tempy"; import getRegistry from "../lib/get-registry.js"; +import { OFFICIAL_REGISTRY } from "../lib/definitions/constants.js"; test("Get default registry", (t) => { const cwd = temporaryDirectory(); - t.is(getRegistry({ name: "package-name" }, { cwd, env: {} }), "https://registry.npmjs.org/"); - t.is(getRegistry({ name: "package-name", publishConfig: {} }, { cwd, env: {} }), "https://registry.npmjs.org/"); + t.is(getRegistry({ name: "package-name" }, { cwd, env: {} }), OFFICIAL_REGISTRY); + t.is(getRegistry({ name: "package-name", publishConfig: {} }, { cwd, env: {} }), OFFICIAL_REGISTRY); }); test('Get the registry configured in ".npmrc" and normalize trailing slash', async (t) => { diff --git a/test/get-release-info.test.js b/test/get-release-info.test.js index 951e2d24..9a7c5dc4 100644 --- a/test/get-release-info.test.js +++ b/test/get-release-info.test.js @@ -1,5 +1,6 @@ import test from "ava"; import getReleaseInfo from "../lib/get-release-info.js"; +import { OFFICIAL_REGISTRY } from "../lib/definitions/constants.js"; test("Default registry and scoped module", async (t) => { t.deepEqual( @@ -7,7 +8,7 @@ test("Default registry and scoped module", async (t) => { { name: "@scope/module" }, { env: {}, nextRelease: { version: "1.0.0" } }, "latest", - "https://registry.npmjs.org/" + OFFICIAL_REGISTRY ), { name: "npm package (@latest dist-tag)", diff --git a/test/verify-auth.test.js b/test/verify-auth.test.js index 9ec940d1..9ae00eea 100644 --- a/test/verify-auth.test.js +++ b/test/verify-auth.test.js @@ -1,8 +1,9 @@ import test from "ava"; import * as td from "testdouble"; +import { OFFICIAL_REGISTRY } from "../lib/definitions/constants.js"; let execa, verifyAuth, getRegistry, setNpmrcAuth; -const DEFAULT_NPM_REGISTRY = "https://registry.npmjs.org/"; +const DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY; const npmrc = "npmrc contents"; const pkg = {}; const otherEnvVars = { foo: "bar" }; From 18b5911a9b5b7a0b38524fc75f01a7adac6811ad Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 10:25:03 -0500 Subject: [PATCH 19/45] wip(trusted-publishing): capture rule about targeting the official registry for #958 --- lib/trusted-publishing/oidc-context.js | 5 +++++ test/trusted-publishing/oidc-context.test.js | 12 ++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 lib/trusted-publishing/oidc-context.js create mode 100644 test/trusted-publishing/oidc-context.test.js diff --git a/lib/trusted-publishing/oidc-context.js b/lib/trusted-publishing/oidc-context.js new file mode 100644 index 00000000..6e8e7595 --- /dev/null +++ b/lib/trusted-publishing/oidc-context.js @@ -0,0 +1,5 @@ +import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; + +export default function oidcContextEstablished(registry) { + return OFFICIAL_REGISTRY === registry; +} diff --git a/test/trusted-publishing/oidc-context.test.js b/test/trusted-publishing/oidc-context.test.js new file mode 100644 index 00000000..915b704b --- /dev/null +++ b/test/trusted-publishing/oidc-context.test.js @@ -0,0 +1,12 @@ +import test from "ava"; + +import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; +import oidcContextEstablished from "../../lib/trusted-publishing/oidc-context.js"; + +test("that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => { + t.true(await oidcContextEstablished(OFFICIAL_REGISTRY)); +}); + +test("that `false` is returned when a custom registry is targeted", async (t) => { + t.false(await oidcContextEstablished("https://custom.registry.org/")); +}); From 1e279d8bddc53ac698a8e1b17c9e1c4f308cf5e7 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 10:55:00 -0500 Subject: [PATCH 20/45] wip(trusted-publishing): detect whether running from a trusted provider for #958 --- .../supported-ci-provider.js | 7 ++++ package-lock.json | 13 +------ package.json | 1 + .../supported-ci-provider.test.js | 34 +++++++++++++++++++ 4 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 lib/trusted-publishing/supported-ci-provider.js create mode 100644 test/trusted-publishing/supported-ci-provider.test.js diff --git a/lib/trusted-publishing/supported-ci-provider.js b/lib/trusted-publishing/supported-ci-provider.js new file mode 100644 index 00000000..2008588f --- /dev/null +++ b/lib/trusted-publishing/supported-ci-provider.js @@ -0,0 +1,7 @@ +import envCi from "env-ci"; + +export default function trustedCiProvider() { + const { name } = envCi(); + + return "GitHub Actions" === name || "GitLab CI/CD" === name; +} diff --git a/package-lock.json b/package-lock.json index 5223992a..623c0464 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", + "env-ci": "^11.2.0", "execa": "^9.0.0", "fs-extra": "^11.0.0", "lodash-es": "^4.17.21", @@ -6247,7 +6248,6 @@ "version": "11.2.0", "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-11.2.0.tgz", "integrity": "sha512-D5kWfzkmaOQDioPmiviWAVtKmpPT4/iJmMVQxWxMPJTFyTkdc5JQUfc5iXEeWxcOdsYTKSAiA/Age4NUOqKsRA==", - "dev": true, "license": "MIT", "dependencies": { "execa": "^8.0.0", @@ -6261,7 +6261,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -6285,7 +6284,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, "license": "MIT", "engines": { "node": ">=16" @@ -6298,7 +6296,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=16.17.0" @@ -6308,7 +6305,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -6321,7 +6317,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^4.0.0" @@ -6337,7 +6332,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6350,7 +6344,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -8527,7 +8520,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", "integrity": "sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -9319,7 +9311,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, "license": "MIT" }, "node_modules/merge2": { @@ -9379,7 +9370,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -12982,7 +12972,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" diff --git a/package.json b/package.json index 0858f899..a38df6b9 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dependencies": { "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", + "env-ci": "^11.2.0", "execa": "^9.0.0", "fs-extra": "^11.0.0", "lodash-es": "^4.17.21", diff --git a/test/trusted-publishing/supported-ci-provider.test.js b/test/trusted-publishing/supported-ci-provider.test.js new file mode 100644 index 00000000..ea39be8a --- /dev/null +++ b/test/trusted-publishing/supported-ci-provider.test.js @@ -0,0 +1,34 @@ +import test from "ava"; +import * as td from "testdouble"; + +//https://docs.npmjs.com/trusted-publishers#supported-cicd-providers + +let envCi, trustedCiProvider; + +test.beforeEach(async (t) => { + ({ default: envCi } = await td.replaceEsm("env-ci")); + + ({ default: trustedCiProvider } = await import("../../lib/trusted-publishing/supported-ci-provider.js")); +}); + +test.afterEach.always((t) => { + td.reset(); +}); + +test("that `true` is returned when GitHub Actions is detected", async (t) => { + td.when(envCi()).thenReturn({ name: "GitHub Actions" }); + + t.true(await trustedCiProvider()); +}); + +test("that `true` is returned when GitLab Pipelines is detected", async (t) => { + td.when(envCi()).thenReturn({ name: "GitLab CI/CD" }); + + t.true(await trustedCiProvider()); +}); + +test("that `false` is returned no supported CI provider is detected", async (t) => { + td.when(envCi()).thenReturn({ name: "Other Service" }); + + t.false(await trustedCiProvider()); +}); From 00ab5fcb9c2bca8fdb9c66189747a427dc4fdcff Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 11:42:30 -0500 Subject: [PATCH 21/45] wip(trusted-publishing): limit successful oidc context detection to supported ci providers for #958 --- lib/trusted-publishing/oidc-context.js | 3 ++- test/trusted-publishing/oidc-context.test.js | 22 +++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lib/trusted-publishing/oidc-context.js b/lib/trusted-publishing/oidc-context.js index 6e8e7595..7f850c5d 100644 --- a/lib/trusted-publishing/oidc-context.js +++ b/lib/trusted-publishing/oidc-context.js @@ -1,5 +1,6 @@ import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; +import trustedCiProvider from "./supported-ci-provider.js"; export default function oidcContextEstablished(registry) { - return OFFICIAL_REGISTRY === registry; + return OFFICIAL_REGISTRY === registry && trustedCiProvider(); } diff --git a/test/trusted-publishing/oidc-context.test.js b/test/trusted-publishing/oidc-context.test.js index 915b704b..471c7203 100644 --- a/test/trusted-publishing/oidc-context.test.js +++ b/test/trusted-publishing/oidc-context.test.js @@ -1,12 +1,32 @@ import test from "ava"; +import * as td from "testdouble"; import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; -import oidcContextEstablished from "../../lib/trusted-publishing/oidc-context.js"; + +let oidcContextEstablished, trustedCiProvider; + +test.beforeEach(async (t) => { + ({ default: trustedCiProvider } = await td.replaceEsm("../../lib/trusted-publishing/supported-ci-provider.js")); + + ({ default: oidcContextEstablished } = await import("../../lib/trusted-publishing/oidc-context.js")); +}); + +test.afterEach.always((t) => { + td.reset(); +}); test("that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => { + td.when(trustedCiProvider()).thenResolve(true); + t.true(await oidcContextEstablished(OFFICIAL_REGISTRY)); }); +test("that `false` is returned when the official registry is targeted, but outside the context of a supported CI provider", async (t) => { + td.when(trustedCiProvider()).thenResolve(false); + + t.false(await oidcContextEstablished(OFFICIAL_REGISTRY)); +}); + test("that `false` is returned when a custom registry is targeted", async (t) => { t.false(await oidcContextEstablished("https://custom.registry.org/")); }); From a2aa38ff15d316d16e1262b065fb5ad9832eb92d Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 11:55:09 -0500 Subject: [PATCH 22/45] feat(trusted-publishing): highlight in the error that a token is only required when not using OIDC for #958 --- lib/definitions/errors.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index 180b1bf6..e7fae4e1 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -37,7 +37,7 @@ Your configuration for the \`pkgRoot\` option is \`${pkgRoot}\`.`, export function ENONPMTOKEN({ registry }) { return { message: "No npm token specified.", - details: `An [npm token](${linkify( + details: `When not publishing through [trusted publishing](https://docs.npmjs.com/trusted-publishers) an [npm token](${linkify( "README.md#npm-registry-authentication" )}) must be created and set in the \`NPM_TOKEN\` environment variable on your CI environment. From c9f0da57c741f8a3b0d0a577455d3f6504e65ed5 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 11:57:20 -0500 Subject: [PATCH 23/45] fix(errors): bring back the invalid token error for #958 --- lib/definitions/errors.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index e7fae4e1..eecb2d01 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -52,6 +52,13 @@ export function EINVALIDNPMAUTH() { "README.md#npm-registry-authentication" )}) is not configured as needed. +export function EINVALIDNPMTOKEN({ registry }) { + return { + message: "Invalid npm token.", + details: `The [npm token](${linkify( + "README.md#npm-registry-authentication" + )}) configured in the \`NPM_TOKEN\` environment variable must be a valid [token](https://docs.npmjs.com/getting-started/working_with_tokens) allowing to publish to the registry \`${registry}\`. + Please verify your authentication configuration.`, }; } From e3319f1b2cb07eef8f61f9fa613552fa33bc92ae Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 12:14:28 -0500 Subject: [PATCH 24/45] feat(trusted-publishing): verify auth, considering OIDC vs tokens from various registriess the trusted publishing verification is incomplete, but this change wires the various options together, at least for #958 --- lib/verify-auth.js | 40 +++++++-------- test/integration.test.js | 4 +- test/verify-auth.test.js | 102 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 121 insertions(+), 25 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 908c7d9d..5f3a2166 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -4,52 +4,54 @@ import AggregateError from "aggregate-error"; import getRegistry from "./get-registry.js"; import setNpmrcAuth from "./set-npmrc-auth.js"; import getError from "./get-error.js"; +import oidcContextEstablished from "./trusted-publishing/oidc-context.js"; import { OFFICIAL_REGISTRY } from "./definitions/constants.js"; function registryIsDefault(registry, DEFAULT_NPM_REGISTRY) { return normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY); } -function targetingDefaultRegistryButNoTokenProvided(registry, DEFAULT_NPM_REGISTRY, errors) { - return registryIsDefault(registry, DEFAULT_NPM_REGISTRY) && errors.length === 1 && errors[0].code === "ENONPMTOKEN"; -} - export default async function (npmrc, pkg, context) { const { cwd, env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY, ...env }, stdout, stderr, - logger, } = context; const registry = getRegistry(pkg, context); - try { - logger.log("setting npmrc auth for registry", registry); - await setNpmrcAuth(npmrc, registry, context); - } catch (aggregateError) { - const { errors } = aggregateError; - - if (targetingDefaultRegistryButNoTokenProvided(registry, DEFAULT_NPM_REGISTRY, errors)) { - logger.log("NPM_TOKEN was not provided for the default npm registry."); - } else { - throw aggregateError; - } + if (oidcContextEstablished(registry)) { + return; } + await setNpmrcAuth(npmrc, registry, context); + if (registryIsDefault(registry, DEFAULT_NPM_REGISTRY)) { + try { + const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { + cwd, + env, + preferLocal: true, + }); + whoamiResult.stdout.pipe(stdout, { end: false }); + whoamiResult.stderr.pipe(stderr, { end: false }); + await whoamiResult; + } catch { + throw new AggregateError([getError("EINVALIDNPMTOKEN", { registry })]); + } + } else { const publishDryRunResult = execa( "npm", ["publish", "--dry-run", "--tag=semantic-release-auth-check", "--userconfig", npmrc, "--registry", registry], { cwd, env, preferLocal: true, lines: true } ); - publishDryRunResult.stdout.pipe(stdout, { end: false }); - publishDryRunResult.stderr.pipe(stderr, { end: false }); + // publishDryRunResult.stdout.pipe(stdout, { end: false }); + // publishDryRunResult.stderr.pipe(stderr, { end: false }); (await publishDryRunResult).stderr.forEach((line) => { if (line.includes("This command requires you to be logged in to ")) { - throw new AggregateError([getError("EINVALIDNPMAUTH")]); + throw new AggregateError([getError("EINVALIDNPMAUTH", { registry })]); } }); } diff --git a/test/integration.test.js b/test/integration.test.js index e77f7c71..c16378de 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -91,7 +91,7 @@ test('Skip npm token verification if "package.private" is true', async (t) => { ); }); -test("Throws error if NPM token is invalid", async (t) => { +test("Throws error if NPM token is invalid when targeting the default registry", async (t) => { const cwd = temporaryDirectory(); const env = { NPM_TOKEN: "wrong_token", DEFAULT_NPM_REGISTRY: npmRegistry.url }; const pkg = { name: "published", version: "1.0.0", publishConfig: { registry: npmRegistry.url } }; @@ -111,7 +111,7 @@ test("Throws error if NPM token is invalid", async (t) => { t.is(error.message, "Invalid npm authentication."); }); -test("Throws error if NPM token is not provided", async (t) => { +test("Throws error if NPM token is not provided when targeting the default registry", async (t) => { const cwd = temporaryDirectory(); const env = { DEFAULT_NPM_REGISTRY: npmRegistry.url }; const pkg = { name: "published", version: "1.0.0", publishConfig: { registry: npmRegistry.url } }; diff --git a/test/verify-auth.test.js b/test/verify-auth.test.js index 9ae00eea..f24c8e22 100644 --- a/test/verify-auth.test.js +++ b/test/verify-auth.test.js @@ -1,8 +1,9 @@ import test from "ava"; import * as td from "testdouble"; +import AggregateError from "aggregate-error"; import { OFFICIAL_REGISTRY } from "../lib/definitions/constants.js"; -let execa, verifyAuth, getRegistry, setNpmrcAuth; +let execa, verifyAuth, getRegistry, setNpmrcAuth, oidcContextEstablished; const DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY; const npmrc = "npmrc contents"; const pkg = {}; @@ -15,6 +16,8 @@ test.beforeEach(async (t) => { ({ execa } = await td.replaceEsm("execa")); ({ default: getRegistry } = await td.replaceEsm("../lib/get-registry.js")); ({ default: setNpmrcAuth } = await td.replaceEsm("../lib/set-npmrc-auth.js")); + ({ default: oidcContextEstablished } = await td.replaceEsm("../lib/trusted-publishing/oidc-context.js")); + td.when(oidcContextEstablished()).thenReturn(false); ({ default: verifyAuth } = await import("../lib/verify-auth.js")); }); @@ -23,10 +26,21 @@ test.afterEach.always((t) => { td.reset(); }); +test.serial( + "that the auth context for the official registry is considered valid when trusted publishing is established", + async (t) => { + td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY)).thenReturn(true); + + await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); + } +); + test.serial( "that the provided token is verified with `npm whoami` when trusted publishing is not established for the official registry", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY)).thenReturn(false); td.when( execa("npm", ["whoami", "--userconfig", npmrc, "--registry", DEFAULT_NPM_REGISTRY], { cwd, @@ -46,6 +60,7 @@ test.serial( "that the auth context for the official registry is considered invalid when no token is provided and trusted publishing is not established", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY)).thenReturn(false); td.when( execa("npm", ["whoami", "--userconfig", npmrc, "--registry", DEFAULT_NPM_REGISTRY], { cwd, @@ -64,10 +79,89 @@ test.serial( } ); +test.serial( + "that a publish dry run is performed to validate token presence when publishing to a custom registry", + async (t) => { + const otherRegistry = "https://other.registry.org"; + td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); + td.when(oidcContextEstablished(otherRegistry)).thenReturn(false); + td.when( + execa( + "npm", + [ + "publish", + "--dry-run", + "--tag=semantic-release-auth-check", + "--userconfig", + npmrc, + "--registry", + otherRegistry, + ], + { + cwd, + env: otherEnvVars, + preferLocal: true, + lines: true, + } + ) + ).thenResolve({ + stderr: ["foo", "bar", "baz"], + }); + + await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); + } +); + // since alternative registries are not consistent in implementing `npm whoami`, // we do not attempt to verify the provided token when publishing to them -test.serial("that `npm whoami` is not invoked when publishing to a custom registry", async (t) => { - td.when(getRegistry(pkg, context)).thenReturn("https://other.registry.org"); +test.serial( + "that the token is considered invalid when the publish dry run fails when publishing to a custom registry", + async (t) => { + const otherRegistry = "https://other.registry.org"; + td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); + td.when(oidcContextEstablished(otherRegistry)).thenReturn(false); + td.when( + execa( + "npm", + [ + "publish", + "--dry-run", + "--tag=semantic-release-auth-check", + "--userconfig", + npmrc, + "--registry", + otherRegistry, + ], + { + cwd, + env: otherEnvVars, + preferLocal: true, + lines: true, + } + ) + ).thenResolve({ + stderr: ["foo", "bar", "baz", `This command requires you to be logged in to ${otherRegistry}`, "qux"], + }); + + const { + errors: [error], + } = await t.throwsAsync(verifyAuth(npmrc, pkg, context)); + + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDNPMAUTH"); + t.is(error.message, "Invalid npm authentication."); + } +); + +test.serial("that errors from setting up auth bubble through this function", async (t) => { + const registry = DEFAULT_NPM_REGISTRY; + const thrownError = new Error(); + td.when(getRegistry(pkg, context)).thenReturn(registry); + td.when(setNpmrcAuth(npmrc, registry, context)).thenThrow(new AggregateError([thrownError])); + + const { + errors: [error], + } = await t.throwsAsync(verifyAuth(npmrc, pkg, context)); - await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); + t.is(error, thrownError); }); From d825403a1c153071d0973ec3b7c7b4fad6fc481e Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 12:17:13 -0500 Subject: [PATCH 25/45] fix(errors): resolve syntax problem --- lib/definitions/errors.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index eecb2d01..6237c79c 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -50,7 +50,9 @@ export function EINVALIDNPMAUTH() { message: "Invalid npm authentication.", details: `The [authentication required to publish](${linkify( "README.md#npm-registry-authentication" - )}) is not configured as needed. + )}) is not configured as needed.`, + }; +} export function EINVALIDNPMTOKEN({ registry }) { return { From 739f65563211551f069034140bc4b7da3e0f0956 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 13:50:08 -0500 Subject: [PATCH 26/45] test(integration): adjust to align to updated auth verification logic --- test/integration.test.js | 15 +++++++++++---- .../supported-ci-provider.test.js | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/test/integration.test.js b/test/integration.test.js index c16378de..6a912755 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -107,8 +107,8 @@ test("Throws error if NPM token is invalid when targeting the default registry", ); t.is(error.name, "SemanticReleaseError"); - t.is(error.code, "EINVALIDNPMAUTH"); - t.is(error.message, "Invalid npm authentication."); + t.is(error.code, "EINVALIDNPMTOKEN"); + t.is(error.message, "Invalid npm token."); }); test("Throws error if NPM token is not provided when targeting the default registry", async (t) => { @@ -131,17 +131,24 @@ test("Throws error if NPM token is not provided when targeting the default regis t.is(error.message, "No npm token specified."); }); -test("Skip Token validation if the registry configured is not the default one", async (t) => { +test("Verify the token with a publish dry-run if the registry configured is not the default one", async (t) => { const cwd = temporaryDirectory(); const env = { NPM_TOKEN: "wrong_token" }; const pkg = { name: "published", version: "1.0.0", publishConfig: { registry: "http://custom-registry.com/" } }; await fs.outputJson(path.resolve(cwd, "package.json"), pkg); - await t.notThrowsAsync( + + const { + errors: [error], + } = await t.throwsAsync( t.context.m.verifyConditions( {}, { cwd, env, options: {}, stdout: t.context.stdout, stderr: t.context.stderr, logger: t.context.logger } ) ); + + t.is(error.name, "SemanticReleaseError"); + t.is(error.code, "EINVALIDNPMAUTH"); + t.is(error.message, "Invalid npm authentication."); }); test("Verify npm auth and package", async (t) => { diff --git a/test/trusted-publishing/supported-ci-provider.test.js b/test/trusted-publishing/supported-ci-provider.test.js index ea39be8a..c00fe43c 100644 --- a/test/trusted-publishing/supported-ci-provider.test.js +++ b/test/trusted-publishing/supported-ci-provider.test.js @@ -27,7 +27,7 @@ test("that `true` is returned when GitLab Pipelines is detected", async (t) => { t.true(await trustedCiProvider()); }); -test("that `false` is returned no supported CI provider is detected", async (t) => { +test("that `false` is returned when no supported CI provider is detected", async (t) => { td.when(envCi()).thenReturn({ name: "Other Service" }); t.false(await trustedCiProvider()); From e75c6200f1558f9cc4d6af9c7c919ab449263828 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Tue, 14 Oct 2025 14:00:05 -0500 Subject: [PATCH 27/45] refactor(verify-auth): extract token verification logic to intent-revealing functions to improve readability for #958 --- lib/verify-auth.js | 72 +++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 5f3a2166..ba2f2f40 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -11,6 +11,48 @@ function registryIsDefault(registry, DEFAULT_NPM_REGISTRY) { return normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY); } +async function verifyAuthContextAgainstRegistry(npmrc, registry, cwd, env, stdout, stderr) { + try { + const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { + cwd, + env, + preferLocal: true, + }); + + whoamiResult.stdout.pipe(stdout, { end: false }); + whoamiResult.stderr.pipe(stderr, { end: false }); + + await whoamiResult; + } catch { + throw new AggregateError([getError("EINVALIDNPMTOKEN", { registry })]); + } +} + +async function attemptPublishDryRun(npmrc, registry, cwd, env) { + const publishDryRunResult = execa( + "npm", + ["publish", "--dry-run", "--tag=semantic-release-auth-check", "--userconfig", npmrc, "--registry", registry], + { cwd, env, preferLocal: true, lines: true } + ); + + // publishDryRunResult.stdout.pipe(stdout, { end: false }); + // publishDryRunResult.stderr.pipe(stderr, { end: false }); + + (await publishDryRunResult).stderr.forEach((line) => { + if (line.includes("This command requires you to be logged in to ")) { + throw new AggregateError([getError("EINVALIDNPMAUTH", { registry })]); + } + }); +} + +async function verifyTokenAuth(registry, DEFAULT_NPM_REGISTRY, npmrc, cwd, env, stdout, stderr) { + if (registryIsDefault(registry, DEFAULT_NPM_REGISTRY)) { + await verifyAuthContextAgainstRegistry(npmrc, registry, cwd, env, stdout, stderr); + } else { + await attemptPublishDryRun(npmrc, registry, cwd, env); + } +} + export default async function (npmrc, pkg, context) { const { cwd, @@ -26,33 +68,5 @@ export default async function (npmrc, pkg, context) { await setNpmrcAuth(npmrc, registry, context); - if (registryIsDefault(registry, DEFAULT_NPM_REGISTRY)) { - try { - const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { - cwd, - env, - preferLocal: true, - }); - whoamiResult.stdout.pipe(stdout, { end: false }); - whoamiResult.stderr.pipe(stderr, { end: false }); - await whoamiResult; - } catch { - throw new AggregateError([getError("EINVALIDNPMTOKEN", { registry })]); - } - } else { - const publishDryRunResult = execa( - "npm", - ["publish", "--dry-run", "--tag=semantic-release-auth-check", "--userconfig", npmrc, "--registry", registry], - { cwd, env, preferLocal: true, lines: true } - ); - - // publishDryRunResult.stdout.pipe(stdout, { end: false }); - // publishDryRunResult.stderr.pipe(stderr, { end: false }); - - (await publishDryRunResult).stderr.forEach((line) => { - if (line.includes("This command requires you to be logged in to ")) { - throw new AggregateError([getError("EINVALIDNPMAUTH", { registry })]); - } - }); - } + await verifyTokenAuth(registry, DEFAULT_NPM_REGISTRY, npmrc, cwd, env, stdout, stderr); } From c80ecb0404f44fa60c5d9edb1d3424adf8a336f0 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 00:19:34 -0500 Subject: [PATCH 28/45] feat(trusted-publishing): make request to verify if OIDC token exchange can succeed this is the correct call, but details are still incomplete since the bearer token for the request needs to be the OIDC token from the CI IdP for #958 --- lib/set-npmrc-auth.js | 2 +- lib/trusted-publishing/oidc-context.js | 5 +-- lib/trusted-publishing/token-exchange.js | 11 +++++++ lib/verify-auth.js | 2 +- test/trusted-publishing/oidc-context.test.js | 28 ++++++++++++---- .../trusted-publishing/token-exchange.test.js | 33 +++++++++++++++++++ test/verify-auth.test.js | 10 +++--- 7 files changed, 75 insertions(+), 16 deletions(-) create mode 100644 lib/trusted-publishing/token-exchange.js create mode 100644 test/trusted-publishing/token-exchange.test.js diff --git a/lib/set-npmrc-auth.js b/lib/set-npmrc-auth.js index 0925bc5b..a8534ca7 100644 --- a/lib/set-npmrc-auth.js +++ b/lib/set-npmrc-auth.js @@ -9,7 +9,7 @@ import { OFFICIAL_REGISTRY } from "./definitions/constants.js"; export default async function (npmrc, registry, { cwd, env: { NPM_TOKEN, NPM_CONFIG_USERCONFIG }, logger }) { logger.log("Verify authentication for registry %s", registry); - const { configs, ...rcConfig } = rc( + const { configs, config, ...rcConfig } = rc( "npm", { registry: OFFICIAL_REGISTRY }, { config: NPM_CONFIG_USERCONFIG || path.resolve(cwd, ".npmrc") } diff --git a/lib/trusted-publishing/oidc-context.js b/lib/trusted-publishing/oidc-context.js index 7f850c5d..293ec68c 100644 --- a/lib/trusted-publishing/oidc-context.js +++ b/lib/trusted-publishing/oidc-context.js @@ -1,6 +1,7 @@ import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; import trustedCiProvider from "./supported-ci-provider.js"; +import tokenExchange from "./token-exchange.js"; -export default function oidcContextEstablished(registry) { - return OFFICIAL_REGISTRY === registry && trustedCiProvider(); +export default async function oidcContextEstablished(registry, pkg) { + return OFFICIAL_REGISTRY === registry && trustedCiProvider() && !!(await tokenExchange(pkg)); } diff --git a/lib/trusted-publishing/token-exchange.js b/lib/trusted-publishing/token-exchange.js new file mode 100644 index 00000000..4b864b15 --- /dev/null +++ b/lib/trusted-publishing/token-exchange.js @@ -0,0 +1,11 @@ +import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; + +export default async function tokenExchange(pkg) { + const response = await fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${pkg.name}`, { method: 'POST' }); + + if (response.ok) { + return (await response.json()).token; + } + + return undefined; +} diff --git a/lib/verify-auth.js b/lib/verify-auth.js index ba2f2f40..da7f6779 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -62,7 +62,7 @@ export default async function (npmrc, pkg, context) { } = context; const registry = getRegistry(pkg, context); - if (oidcContextEstablished(registry)) { + if (oidcContextEstablished(registry, pkg)) { return; } diff --git a/test/trusted-publishing/oidc-context.test.js b/test/trusted-publishing/oidc-context.test.js index 471c7203..bc7c3dd0 100644 --- a/test/trusted-publishing/oidc-context.test.js +++ b/test/trusted-publishing/oidc-context.test.js @@ -3,10 +3,14 @@ import * as td from "testdouble"; import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; -let oidcContextEstablished, trustedCiProvider; +let oidcContextEstablished, trustedCiProvider, tokenExchange; +const pkg = {}; test.beforeEach(async (t) => { + await td.replace(globalThis, "fetch"); ({ default: trustedCiProvider } = await td.replaceEsm("../../lib/trusted-publishing/supported-ci-provider.js")); + ({ default: tokenExchange } = await td.replaceEsm("../../lib/trusted-publishing/token-exchange.js")); + td.when(tokenExchange(pkg)).thenResolve(undefined); ({ default: oidcContextEstablished } = await import("../../lib/trusted-publishing/oidc-context.js")); }); @@ -15,18 +19,28 @@ test.afterEach.always((t) => { td.reset(); }); -test("that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => { +test.serial("that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => { td.when(trustedCiProvider()).thenResolve(true); + td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); + td.when(tokenExchange(pkg)).thenResolve('token-value'); - t.true(await oidcContextEstablished(OFFICIAL_REGISTRY)); + t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); }); -test("that `false` is returned when the official registry is targeted, but outside the context of a supported CI provider", async (t) => { +test.serial("that `false` is returned when the official registry is targeted, but outside the context of a supported CI provider", async (t) => { td.when(trustedCiProvider()).thenResolve(false); - t.false(await oidcContextEstablished(OFFICIAL_REGISTRY)); + t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); }); -test("that `false` is returned when a custom registry is targeted", async (t) => { - t.false(await oidcContextEstablished("https://custom.registry.org/")); +test.serial("that `false` is returned when OIDC token exchange fails in a supported CI provider", async (t) => { + td.when(trustedCiProvider()).thenResolve(true); + td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); + td.when(tokenExchange(pkg)).thenResolve(undefined); + + t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); +}); + +test.serial("that `false` is returned when a custom registry is targeted", async (t) => { + t.false(await oidcContextEstablished("https://custom.registry.org/", pkg)); }); diff --git a/test/trusted-publishing/token-exchange.test.js b/test/trusted-publishing/token-exchange.test.js new file mode 100644 index 00000000..e82de984 --- /dev/null +++ b/test/trusted-publishing/token-exchange.test.js @@ -0,0 +1,33 @@ +import test from "ava"; +import * as td from "testdouble"; + +import tokenExchange from '../../lib/trusted-publishing/token-exchange.js'; +import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; + +// https://api-docs.npmjs.com/#tag/registry.npmjs.org/operation/exchangeOidcToken + +const packageName = "some-package"; +const pkg = { name: packageName }; + +test.beforeEach(async (t) => { + await td.replace(globalThis, "fetch"); +}); + +test.afterEach.always((t) => { + td.reset(); +}); + +test.serial("that an access token is returned when token exchange succeeds", async (t) => { + const token = "token-value"; + td.when(fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${packageName}`, {method: 'POST'})) + .thenResolve(new Response(JSON.stringify({token}), {status: 201, headers: {'Content-Type': 'application/json'}})); + + t.is(await tokenExchange(pkg), token); +}); + +test.serial("that `undefined` is returned when token exchange fails", async (t) => { + td.when(fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${packageName}`, {method: 'POST'})) + .thenResolve(new Response(JSON.stringify({message: 'foo'}), {status: 401, headers: {'Content-Type': 'application/json'}})); + + t.is(await tokenExchange(pkg), undefined); +}); diff --git a/test/verify-auth.test.js b/test/verify-auth.test.js index f24c8e22..a64217fa 100644 --- a/test/verify-auth.test.js +++ b/test/verify-auth.test.js @@ -30,7 +30,7 @@ test.serial( "that the auth context for the official registry is considered valid when trusted publishing is established", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY)).thenReturn(true); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg)).thenReturn(true); await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); } @@ -40,7 +40,7 @@ test.serial( "that the provided token is verified with `npm whoami` when trusted publishing is not established for the official registry", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY)).thenReturn(false); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg)).thenReturn(false); td.when( execa("npm", ["whoami", "--userconfig", npmrc, "--registry", DEFAULT_NPM_REGISTRY], { cwd, @@ -60,7 +60,7 @@ test.serial( "that the auth context for the official registry is considered invalid when no token is provided and trusted publishing is not established", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY)).thenReturn(false); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg)).thenReturn(false); td.when( execa("npm", ["whoami", "--userconfig", npmrc, "--registry", DEFAULT_NPM_REGISTRY], { cwd, @@ -84,7 +84,7 @@ test.serial( async (t) => { const otherRegistry = "https://other.registry.org"; td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); - td.when(oidcContextEstablished(otherRegistry)).thenReturn(false); + td.when(oidcContextEstablished(otherRegistry, pkg)).thenReturn(false); td.when( execa( "npm", @@ -119,7 +119,7 @@ test.serial( async (t) => { const otherRegistry = "https://other.registry.org"; td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); - td.when(oidcContextEstablished(otherRegistry)).thenReturn(false); + td.when(oidcContextEstablished(otherRegistry, pkg)).thenReturn(false); td.when( execa( "npm", From 3dd95d0b5161a3af609e2fdcd416ee67f4f1c353 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 00:38:28 -0500 Subject: [PATCH 29/45] fix(trusted-publishing): uri encode the package name for the token exchange request for #958 --- lib/trusted-publishing/token-exchange.js | 5 ++- test/trusted-publishing/oidc-context.test.js | 32 +++++++++++-------- .../trusted-publishing/token-exchange.test.js | 22 +++++++++---- 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/lib/trusted-publishing/token-exchange.js b/lib/trusted-publishing/token-exchange.js index 4b864b15..70616b05 100644 --- a/lib/trusted-publishing/token-exchange.js +++ b/lib/trusted-publishing/token-exchange.js @@ -1,7 +1,10 @@ import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; export default async function tokenExchange(pkg) { - const response = await fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${pkg.name}`, { method: 'POST' }); + const response = await fetch( + `${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(pkg.name)}`, + { method: "POST" } + ); if (response.ok) { return (await response.json()).token; diff --git a/test/trusted-publishing/oidc-context.test.js b/test/trusted-publishing/oidc-context.test.js index bc7c3dd0..d7f41547 100644 --- a/test/trusted-publishing/oidc-context.test.js +++ b/test/trusted-publishing/oidc-context.test.js @@ -19,19 +19,25 @@ test.afterEach.always((t) => { td.reset(); }); -test.serial("that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => { - td.when(trustedCiProvider()).thenResolve(true); - td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); - td.when(tokenExchange(pkg)).thenResolve('token-value'); - - t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); -}); - -test.serial("that `false` is returned when the official registry is targeted, but outside the context of a supported CI provider", async (t) => { - td.when(trustedCiProvider()).thenResolve(false); - - t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); -}); +test.serial( + "that `true` is returned when a trusted-publishing context has been established with the official registry", + async (t) => { + td.when(trustedCiProvider()).thenResolve(true); + td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); + td.when(tokenExchange(pkg)).thenResolve("token-value"); + + t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); + } +); + +test.serial( + "that `false` is returned when the official registry is targeted, but outside the context of a supported CI provider", + async (t) => { + td.when(trustedCiProvider()).thenResolve(false); + + t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); + } +); test.serial("that `false` is returned when OIDC token exchange fails in a supported CI provider", async (t) => { td.when(trustedCiProvider()).thenResolve(true); diff --git a/test/trusted-publishing/token-exchange.test.js b/test/trusted-publishing/token-exchange.test.js index e82de984..4bfeba27 100644 --- a/test/trusted-publishing/token-exchange.test.js +++ b/test/trusted-publishing/token-exchange.test.js @@ -1,12 +1,12 @@ import test from "ava"; import * as td from "testdouble"; -import tokenExchange from '../../lib/trusted-publishing/token-exchange.js'; +import tokenExchange from "../../lib/trusted-publishing/token-exchange.js"; import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; // https://api-docs.npmjs.com/#tag/registry.npmjs.org/operation/exchangeOidcToken -const packageName = "some-package"; +const packageName = "@scope/some-package"; const pkg = { name: packageName }; test.beforeEach(async (t) => { @@ -19,15 +19,25 @@ test.afterEach.always((t) => { test.serial("that an access token is returned when token exchange succeeds", async (t) => { const token = "token-value"; - td.when(fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${packageName}`, {method: 'POST'})) - .thenResolve(new Response(JSON.stringify({token}), {status: 201, headers: {'Content-Type': 'application/json'}})); + td.when( + fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { + method: "POST", + }) + ).thenResolve( + new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } }) + ); t.is(await tokenExchange(pkg), token); }); test.serial("that `undefined` is returned when token exchange fails", async (t) => { - td.when(fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${packageName}`, {method: 'POST'})) - .thenResolve(new Response(JSON.stringify({message: 'foo'}), {status: 401, headers: {'Content-Type': 'application/json'}})); + td.when( + fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { + method: "POST", + }) + ).thenResolve( + new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } }) + ); t.is(await tokenExchange(pkg), undefined); }); From 4a8cd7aefc11f22126b01eed16643afd9db09510 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 08:55:58 -0500 Subject: [PATCH 30/45] docs(trusted-publishing): further clarify scenarios for trusted publishing vs access token use since trusted publishing is now more required than it previously was: https://github.blog/changelog/2025-09-29-strengthening-npm-security-important-changes-to-authentication-and-token-management/ --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c7ad4744..35bfcc4c 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,12 @@ The plugin can be configured in the [**semantic-release** configuration file](ht ### Official Registry -When publishing to the [official registry](https://registry.npmjs.org/), it is recommended to publish with authentication that is intended for automation: +When publishing to the [official registry](https://registry.npmjs.org/), it is recommended to publish with authentication intended for automation: -- [OIDC publishing](https://docs.npmjs.com/trusted-publishers) is recommended when publishing from a CI provider -- [Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended when publishing from a CI provider that is not supported by npm for OIDC publishing, and can be set via [environment variables](#environment-variables) +- Since access tokens have recently had their [maximum lifetimes restricted](https://github.blog/changelog/2025-09-29-strengthening-npm-security-important-changes-to-authentication-and-token-management/), + [trusted publishing](https://docs.npmjs.com/trusted-publishers) is recommended when publishing from a [supported CI provider](https://docs.npmjs.com/trusted-publishers#supported-cicd-providers) +- [Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended when publishing from a CI provider that is not supported by npm for trusted publishing, and can be set via [environment variables](#environment-variables). + Because these access tokens expire, rotation will need to be accounted for in this scenario. ### Alternative Registries From 66e2a443990960e6b2db82d9466f13f25af71112 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 09:11:04 -0500 Subject: [PATCH 31/45] docs(trusted-publsihing): combine information about configuring github actions for both trusted publishing and provenance attestations for #958 --- README.md | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 35bfcc4c..69e94ede 100644 --- a/README.md +++ b/README.md @@ -47,33 +47,19 @@ When publishing to the [official registry](https://registry.npmjs.org/), it is r - [Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended when publishing from a CI provider that is not supported by npm for trusted publishing, and can be set via [environment variables](#environment-variables). Because these access tokens expire, rotation will need to be accounted for in this scenario. -### Alternative Registries - -The npm token authentication configuration is **required** and can be set via [environment variables](#environment-variables). -See the documentation for your registry for details on how to create a token for automation. - -### npm provenance - -If you are publishing to the official registry and your pipeline is on a [provider that is supported by npm for provenance](https://docs.npmjs.com/generating-provenance-statements#provenance-limitations), npm can be configured to [publish with provenance](https://docs.npmjs.com/generating-provenance-statements). - -Since semantic-release wraps the npm publish command, configuring provenance is not exposed directly. -Instead, provenance can be configured through the [other configuration options exposed by npm](https://docs.npmjs.com/generating-provenance-statements#using-third-party-package-publishing-tools). -Provenance applies specifically to publishing, so our recommendation is to configure under `publishConfig` within the `package.json`. - -> [!NOTE] -> When publishing a public package to the official registry with OIDC, [npm provenance is automatically enabled](https://docs.npmjs.com/trusted-publishers#automatic-provenance-generation) and does not require any additional configuration. +#### Trusted publishing from GitHub Actions -#### npm provenance on GitHub Actions - -For package provenance to be signed on the GitHub Actions CI the following permission is required -to be enabled on the job: +To publish with OIDC from GitHub Actions, the `id-token: write` permission is required to be enabled on the job: ```yaml permissions: - id-token: write # to enable use of OIDC for npm provenance + id-token: write # to enable use of OIDC for trusted publishing and npm provenance ``` -It's worth noting that if you are using semantic-release to its fullest with a GitHub release, GitHub comments, +> [!NOTE] +> When using trusted publishing, provenance attestations are automatically generated for your packages without requiring provenance to be explicitly enabled. + +It's also worth noting that if you are using semantic-release to its fullest with a GitHub release, GitHub comments, and other features, then [more permissions are required](https://github.com/semantic-release/github#github-authentication) to be enabled on this job: ```yaml @@ -81,11 +67,16 @@ permissions: contents: write # to be able to publish a GitHub release issues: write # to be able to comment on released issues pull-requests: write # to be able to comment on released pull requests - id-token: write # to enable use of OIDC for npm provenance + id-token: write # to enable use of OIDC for trusted publishing and npm provenance ``` Refer to the [GitHub Actions recipe for npm package provenance](https://semantic-release.gitbook.io/semantic-release/recipes/ci-configurations/github-actions#.github-workflows-release.yml-configuration-for-node-projects) for the full CI job's YAML code example. +### Alternative Registries + +The npm token authentication configuration is **required** and can be set via [environment variables](#environment-variables). +See the documentation for your registry for details on how to create a token for automation. + ### Environment variables | Variable | Description | @@ -110,13 +101,14 @@ The plugin uses the [`npm` CLI](https://github.com/npm/cli) which will read the The [`registry`](https://docs.npmjs.com/misc/registry) can be configured via the npm environment variable `NPM_CONFIG_REGISTRY` and will take precedence over the configuration in `.npmrc`. -The [`registry`](https://docs.npmjs.com/misc/registry) and [`dist-tag`](https://docs.npmjs.com/cli/dist-tag) can be configured under `publishConfig` in the `package.json`: +The [`registry`](https://docs.npmjs.com/misc/registry), [`dist-tag`](https://docs.npmjs.com/cli/dist-tag), and [`provenance`](https://docs.npmjs.com/generating-provenance-statements#using-third-party-package-publishing-tools) can be configured under `publishConfig` in the `package.json`: ```json { "publishConfig": { "registry": "https://registry.npmjs.org/", - "tag": "latest" + "tag": "latest", + "provenance": true } } ``` From d83b727440121f4e029d5c2885e9cb55f8aab839 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 11:29:01 -0500 Subject: [PATCH 32/45] feat(trusted-publishing): pass id-token as bearer header for github actions for #958 --- lib/trusted-publishing/token-exchange.js | 19 +++++- package-lock.json | 66 +++++++++++++++++++ package.json | 1 + .../trusted-publishing/token-exchange.test.js | 12 +++- 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/lib/trusted-publishing/token-exchange.js b/lib/trusted-publishing/token-exchange.js index 70616b05..4d35a426 100644 --- a/lib/trusted-publishing/token-exchange.js +++ b/lib/trusted-publishing/token-exchange.js @@ -1,9 +1,18 @@ +import { getIDToken } from "@actions/core"; + import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; -export default async function tokenExchange(pkg) { +const GITHUB_ACTIONS_PROVIDER_NAME = "GitHub Actions"; +const GITLAB_PIPELINES_PROVIDER_NAME = "GitLab CI/CD"; + +async function exchangeGithubActionsToken(packageName) { + const idToken = await getIDToken("npm:registry.npmjs.org"); const response = await fetch( - `${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(pkg.name)}`, - { method: "POST" } + `${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, + { + method: "POST", + headers: { Authorization: `Bearer ${idToken}` }, + } ); if (response.ok) { @@ -12,3 +21,7 @@ export default async function tokenExchange(pkg) { return undefined; } + +export default async function tokenExchange(pkg) { + return await exchangeGithubActionsToken(pkg.name); +} diff --git a/package-lock.json b/package-lock.json index 623c0464..1f262f9e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0-development", "license": "MIT", "dependencies": { + "@actions/core": "^1.11.1", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "env-ci": "^11.2.0", @@ -48,6 +49,41 @@ "semantic-release": ">=20.1.0" } }, + "node_modules/@actions/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz", + "integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==", + "license": "MIT", + "dependencies": { + "@actions/exec": "^1.1.1", + "@actions/http-client": "^2.0.1" + } + }, + "node_modules/@actions/exec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz", + "integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==", + "license": "MIT", + "dependencies": { + "@actions/io": "^1.0.1" + } + }, + "node_modules/@actions/http-client": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz", + "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", + "license": "MIT", + "dependencies": { + "tunnel": "^0.0.6", + "undici": "^5.25.4" + } + }, + "node_modules/@actions/io": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", + "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", + "license": "MIT" + }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -99,6 +135,15 @@ "node": ">=0.1.90" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -16150,6 +16195,15 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -16290,6 +16344,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/undici-types": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", diff --git a/package.json b/package.json index a38df6b9..5cbc5cb3 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "Gregor Martynus (https://twitter.com/gr2m)" ], "dependencies": { + "@actions/core": "^1.11.1", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "env-ci": "^11.2.0", diff --git a/test/trusted-publishing/token-exchange.test.js b/test/trusted-publishing/token-exchange.test.js index 4bfeba27..80588377 100644 --- a/test/trusted-publishing/token-exchange.test.js +++ b/test/trusted-publishing/token-exchange.test.js @@ -1,16 +1,21 @@ import test from "ava"; import * as td from "testdouble"; -import tokenExchange from "../../lib/trusted-publishing/token-exchange.js"; import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; // https://api-docs.npmjs.com/#tag/registry.npmjs.org/operation/exchangeOidcToken +let tokenExchange, getIDToken; const packageName = "@scope/some-package"; const pkg = { name: packageName }; +const idToken = "id-token-value"; +const token = "token-value"; test.beforeEach(async (t) => { await td.replace(globalThis, "fetch"); + ({ getIDToken } = await td.replaceEsm("@actions/core")); + + ({ default: tokenExchange } = await import("../../lib/trusted-publishing/token-exchange.js")); }); test.afterEach.always((t) => { @@ -18,10 +23,11 @@ test.afterEach.always((t) => { }); test.serial("that an access token is returned when token exchange succeeds", async (t) => { - const token = "token-value"; + td.when(getIDToken("npm:registry.npmjs.org")).thenResolve(idToken); td.when( fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { method: "POST", + headers: { Authorization: `Bearer ${idToken}` }, }) ).thenResolve( new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } }) @@ -31,9 +37,11 @@ test.serial("that an access token is returned when token exchange succeeds", asy }); test.serial("that `undefined` is returned when token exchange fails", async (t) => { + td.when(getIDToken("npm:registry.npmjs.org")).thenResolve(idToken); td.when( fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { method: "POST", + headers: { Authorization: `Bearer ${idToken}` }, }) ).thenResolve( new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } }) From 701510ee28e5b4d6ff4d9ddf527b6a362ebbf646 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 11:36:02 -0500 Subject: [PATCH 33/45] refactor(token-exchange): rename the function to be verb focused --- lib/trusted-publishing/oidc-context.js | 4 ++-- lib/trusted-publishing/token-exchange.js | 2 +- test/trusted-publishing/oidc-context.test.js | 10 +++++----- test/trusted-publishing/token-exchange.test.js | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/trusted-publishing/oidc-context.js b/lib/trusted-publishing/oidc-context.js index 293ec68c..ac73a27a 100644 --- a/lib/trusted-publishing/oidc-context.js +++ b/lib/trusted-publishing/oidc-context.js @@ -1,7 +1,7 @@ import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; import trustedCiProvider from "./supported-ci-provider.js"; -import tokenExchange from "./token-exchange.js"; +import exchangeToken from "./token-exchange.js"; export default async function oidcContextEstablished(registry, pkg) { - return OFFICIAL_REGISTRY === registry && trustedCiProvider() && !!(await tokenExchange(pkg)); + return OFFICIAL_REGISTRY === registry && trustedCiProvider() && !!(await exchangeToken(pkg)); } diff --git a/lib/trusted-publishing/token-exchange.js b/lib/trusted-publishing/token-exchange.js index 4d35a426..6d629ef2 100644 --- a/lib/trusted-publishing/token-exchange.js +++ b/lib/trusted-publishing/token-exchange.js @@ -22,6 +22,6 @@ async function exchangeGithubActionsToken(packageName) { return undefined; } -export default async function tokenExchange(pkg) { +export default async function exchangeToken(pkg) { return await exchangeGithubActionsToken(pkg.name); } diff --git a/test/trusted-publishing/oidc-context.test.js b/test/trusted-publishing/oidc-context.test.js index d7f41547..2636c9ee 100644 --- a/test/trusted-publishing/oidc-context.test.js +++ b/test/trusted-publishing/oidc-context.test.js @@ -3,14 +3,14 @@ import * as td from "testdouble"; import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; -let oidcContextEstablished, trustedCiProvider, tokenExchange; +let oidcContextEstablished, trustedCiProvider, exchangeToken; const pkg = {}; test.beforeEach(async (t) => { await td.replace(globalThis, "fetch"); ({ default: trustedCiProvider } = await td.replaceEsm("../../lib/trusted-publishing/supported-ci-provider.js")); - ({ default: tokenExchange } = await td.replaceEsm("../../lib/trusted-publishing/token-exchange.js")); - td.when(tokenExchange(pkg)).thenResolve(undefined); + ({ default: exchangeToken } = await td.replaceEsm("../../lib/trusted-publishing/token-exchange.js")); + td.when(exchangeToken(pkg)).thenResolve(undefined); ({ default: oidcContextEstablished } = await import("../../lib/trusted-publishing/oidc-context.js")); }); @@ -24,7 +24,7 @@ test.serial( async (t) => { td.when(trustedCiProvider()).thenResolve(true); td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); - td.when(tokenExchange(pkg)).thenResolve("token-value"); + td.when(exchangeToken(pkg)).thenResolve("token-value"); t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); } @@ -42,7 +42,7 @@ test.serial( test.serial("that `false` is returned when OIDC token exchange fails in a supported CI provider", async (t) => { td.when(trustedCiProvider()).thenResolve(true); td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); - td.when(tokenExchange(pkg)).thenResolve(undefined); + td.when(exchangeToken(pkg)).thenResolve(undefined); t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); }); diff --git a/test/trusted-publishing/token-exchange.test.js b/test/trusted-publishing/token-exchange.test.js index 80588377..c4b4f5e1 100644 --- a/test/trusted-publishing/token-exchange.test.js +++ b/test/trusted-publishing/token-exchange.test.js @@ -5,7 +5,7 @@ import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; // https://api-docs.npmjs.com/#tag/registry.npmjs.org/operation/exchangeOidcToken -let tokenExchange, getIDToken; +let exchangeToken, getIDToken; const packageName = "@scope/some-package"; const pkg = { name: packageName }; const idToken = "id-token-value"; @@ -15,7 +15,7 @@ test.beforeEach(async (t) => { await td.replace(globalThis, "fetch"); ({ getIDToken } = await td.replaceEsm("@actions/core")); - ({ default: tokenExchange } = await import("../../lib/trusted-publishing/token-exchange.js")); + ({ default: exchangeToken } = await import("../../lib/trusted-publishing/token-exchange.js")); }); test.afterEach.always((t) => { @@ -33,7 +33,7 @@ test.serial("that an access token is returned when token exchange succeeds", asy new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } }) ); - t.is(await tokenExchange(pkg), token); + t.is(await exchangeToken(pkg), token); }); test.serial("that `undefined` is returned when token exchange fails", async (t) => { @@ -47,5 +47,5 @@ test.serial("that `undefined` is returned when token exchange fails", async (t) new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } }) ); - t.is(await tokenExchange(pkg), undefined); + t.is(await exchangeToken(pkg), undefined); }); From b67325747f7ce80bfc0e2c647f5b8c151109ea57 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 12:07:14 -0500 Subject: [PATCH 34/45] feat(trusted-publishing): handle failure to retrieve id-token in the context of github actions for #958 --- lib/trusted-publishing/token-exchange.js | 9 ++++++++- test/trusted-publishing/token-exchange.test.js | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/trusted-publishing/token-exchange.js b/lib/trusted-publishing/token-exchange.js index 6d629ef2..90595dda 100644 --- a/lib/trusted-publishing/token-exchange.js +++ b/lib/trusted-publishing/token-exchange.js @@ -6,7 +6,14 @@ const GITHUB_ACTIONS_PROVIDER_NAME = "GitHub Actions"; const GITLAB_PIPELINES_PROVIDER_NAME = "GitLab CI/CD"; async function exchangeGithubActionsToken(packageName) { - const idToken = await getIDToken("npm:registry.npmjs.org"); + let idToken; + + try { + idToken = await getIDToken("npm:registry.npmjs.org"); + } catch (e) { + return undefined; + } + const response = await fetch( `${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { diff --git a/test/trusted-publishing/token-exchange.test.js b/test/trusted-publishing/token-exchange.test.js index c4b4f5e1..0b66ef33 100644 --- a/test/trusted-publishing/token-exchange.test.js +++ b/test/trusted-publishing/token-exchange.test.js @@ -36,6 +36,12 @@ test.serial("that an access token is returned when token exchange succeeds", asy t.is(await exchangeToken(pkg), token); }); +test.serial("that `undefined` is returned when ID token retrieval fails", async (t) => { + td.when(getIDToken("npm:registry.npmjs.org")).thenThrow(new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable")); + + t.is(await exchangeToken(pkg), undefined); +}); + test.serial("that `undefined` is returned when token exchange fails", async (t) => { td.when(getIDToken("npm:registry.npmjs.org")).thenResolve(idToken); td.when( From 6d1c3cf9b3c9a9c44a855ab7fc16b7b9e76026ca Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 12:30:13 -0500 Subject: [PATCH 35/45] feat(trusted-publishing): pass id-token as bearer header for gitlab pipelines for #958 --- lib/definitions/constants.js | 3 + lib/trusted-publishing/token-exchange.js | 54 +++++++++++---- .../trusted-publishing/token-exchange.test.js | 66 +++++++++++++++++-- 3 files changed, 103 insertions(+), 20 deletions(-) diff --git a/lib/definitions/constants.js b/lib/definitions/constants.js index 9f59ec7e..6c651a62 100644 --- a/lib/definitions/constants.js +++ b/lib/definitions/constants.js @@ -1 +1,4 @@ export const OFFICIAL_REGISTRY = "https://registry.npmjs.org/"; + +export const GITHUB_ACTIONS_PROVIDER_NAME = "GitHub Actions"; +export const GITLAB_PIPELINES_PROVIDER_NAME = "GitLab CI/CD"; diff --git a/lib/trusted-publishing/token-exchange.js b/lib/trusted-publishing/token-exchange.js index 90595dda..0d59c737 100644 --- a/lib/trusted-publishing/token-exchange.js +++ b/lib/trusted-publishing/token-exchange.js @@ -1,19 +1,13 @@ import { getIDToken } from "@actions/core"; +import envCi from "env-ci"; -import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; - -const GITHUB_ACTIONS_PROVIDER_NAME = "GitHub Actions"; -const GITLAB_PIPELINES_PROVIDER_NAME = "GitLab CI/CD"; - -async function exchangeGithubActionsToken(packageName) { - let idToken; - - try { - idToken = await getIDToken("npm:registry.npmjs.org"); - } catch (e) { - return undefined; - } +import { + OFFICIAL_REGISTRY, + GITHUB_ACTIONS_PROVIDER_NAME, + GITLAB_PIPELINES_PROVIDER_NAME, +} from "../definitions/constants.js"; +async function exchangeIdToken(idToken, packageName) { const response = await fetch( `${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { @@ -29,6 +23,38 @@ async function exchangeGithubActionsToken(packageName) { return undefined; } +async function exchangeGithubActionsToken(packageName) { + let idToken; + + try { + idToken = await getIDToken("npm:registry.npmjs.org"); + } catch (e) { + return undefined; + } + + return exchangeIdToken(idToken, packageName); +} + +async function exchangeGitlabPipelinesToken(packageName) { + const idToken = process.env.NPM_ID_TOKEN; + + if (!idToken) { + return undefined; + } + + return await exchangeIdToken(idToken, packageName); +} + export default async function exchangeToken(pkg) { - return await exchangeGithubActionsToken(pkg.name); + const { name: ciProviderName } = envCi(); + + if (GITHUB_ACTIONS_PROVIDER_NAME === ciProviderName) { + return await exchangeGithubActionsToken(pkg.name); + } + + if (GITLAB_PIPELINES_PROVIDER_NAME === ciProviderName) { + return await exchangeGitlabPipelinesToken(pkg.name); + } + + return undefined; } diff --git a/test/trusted-publishing/token-exchange.test.js b/test/trusted-publishing/token-exchange.test.js index 0b66ef33..f4e38940 100644 --- a/test/trusted-publishing/token-exchange.test.js +++ b/test/trusted-publishing/token-exchange.test.js @@ -1,11 +1,15 @@ import test from "ava"; import * as td from "testdouble"; -import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; +import { + OFFICIAL_REGISTRY, + GITHUB_ACTIONS_PROVIDER_NAME, + GITLAB_PIPELINES_PROVIDER_NAME, +} from "../../lib/definitions/constants.js"; // https://api-docs.npmjs.com/#tag/registry.npmjs.org/operation/exchangeOidcToken -let exchangeToken, getIDToken; +let exchangeToken, getIDToken, envCi; const packageName = "@scope/some-package"; const pkg = { name: packageName }; const idToken = "id-token-value"; @@ -14,15 +18,19 @@ const token = "token-value"; test.beforeEach(async (t) => { await td.replace(globalThis, "fetch"); ({ getIDToken } = await td.replaceEsm("@actions/core")); + ({ default: envCi } = await td.replaceEsm("env-ci")); ({ default: exchangeToken } = await import("../../lib/trusted-publishing/token-exchange.js")); }); test.afterEach.always((t) => { td.reset(); + + delete process.env.NPM_ID_TOKEN; }); -test.serial("that an access token is returned when token exchange succeeds", async (t) => { +test.serial("that an access token is returned when token exchange succeeds on GitHub Actions", async (t) => { + td.when(envCi()).thenReturn({ name: GITHUB_ACTIONS_PROVIDER_NAME }); td.when(getIDToken("npm:registry.npmjs.org")).thenResolve(idToken); td.when( fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { @@ -36,13 +44,17 @@ test.serial("that an access token is returned when token exchange succeeds", asy t.is(await exchangeToken(pkg), token); }); -test.serial("that `undefined` is returned when ID token retrieval fails", async (t) => { - td.when(getIDToken("npm:registry.npmjs.org")).thenThrow(new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable")); +test.serial("that `undefined` is returned when ID token retrieval fails on GitHub Actions", async (t) => { + td.when(envCi()).thenReturn({ name: GITHUB_ACTIONS_PROVIDER_NAME }); + td.when(getIDToken("npm:registry.npmjs.org")).thenThrow( + new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable") + ); t.is(await exchangeToken(pkg), undefined); }); -test.serial("that `undefined` is returned when token exchange fails", async (t) => { +test.serial("that `undefined` is returned when token exchange fails on GitHub Actions", async (t) => { + td.when(envCi()).thenReturn({ name: GITHUB_ACTIONS_PROVIDER_NAME }); td.when(getIDToken("npm:registry.npmjs.org")).thenResolve(idToken); td.when( fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { @@ -55,3 +67,45 @@ test.serial("that `undefined` is returned when token exchange fails", async (t) t.is(await exchangeToken(pkg), undefined); }); + +test.serial("that an access token is returned when token exchange succeeds on GitLab Pipelines", async (t) => { + process.env.NPM_ID_TOKEN = idToken; + td.when(envCi()).thenReturn({ name: GITLAB_PIPELINES_PROVIDER_NAME }); + td.when( + fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { + method: "POST", + headers: { Authorization: `Bearer ${idToken}` }, + }) + ).thenResolve( + new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } }) + ); + + t.is(await exchangeToken(pkg), token); +}); + +test.serial("that `undefined` is returned when ID token is not available on GitLab Pipelines", async (t) => { + td.when(envCi()).thenReturn({ name: GITLAB_PIPELINES_PROVIDER_NAME }); + + t.is(await exchangeToken(pkg), undefined); +}); + +test.serial("that `undefined` is returned when token exchange fails on GitLab Pipelines", async (t) => { + process.env.NPM_ID_TOKEN = idToken; + td.when(envCi()).thenReturn({ name: GITLAB_PIPELINES_PROVIDER_NAME }); + td.when( + fetch(`${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { + method: "POST", + headers: { Authorization: `Bearer ${idToken}` }, + }) + ).thenResolve( + new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } }) + ); + + t.is(await exchangeToken(pkg), undefined); +}); + +test.serial("that `undefined` is returned when no supported CI provider is detected", async (t) => { + td.when(envCi()).thenReturn({ name: "Other Service" }); + + t.is(await exchangeToken(pkg), undefined); +}); From cf9011b19ba1d0f7213085d37a00bb40c6129897 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 12:36:08 -0500 Subject: [PATCH 36/45] refactor(trusted-publishing): remove redundant ci-providers check since that logic is now handled with the step to acquire the id-token for #958 --- lib/trusted-publishing/oidc-context.js | 3 +- .../supported-ci-provider.js | 7 ---- test/trusted-publishing/oidc-context.test.js | 12 ------- .../supported-ci-provider.test.js | 34 ------------------- 4 files changed, 1 insertion(+), 55 deletions(-) delete mode 100644 lib/trusted-publishing/supported-ci-provider.js delete mode 100644 test/trusted-publishing/supported-ci-provider.test.js diff --git a/lib/trusted-publishing/oidc-context.js b/lib/trusted-publishing/oidc-context.js index ac73a27a..e6b134fd 100644 --- a/lib/trusted-publishing/oidc-context.js +++ b/lib/trusted-publishing/oidc-context.js @@ -1,7 +1,6 @@ import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; -import trustedCiProvider from "./supported-ci-provider.js"; import exchangeToken from "./token-exchange.js"; export default async function oidcContextEstablished(registry, pkg) { - return OFFICIAL_REGISTRY === registry && trustedCiProvider() && !!(await exchangeToken(pkg)); + return OFFICIAL_REGISTRY === registry && !!(await exchangeToken(pkg)); } diff --git a/lib/trusted-publishing/supported-ci-provider.js b/lib/trusted-publishing/supported-ci-provider.js deleted file mode 100644 index 2008588f..00000000 --- a/lib/trusted-publishing/supported-ci-provider.js +++ /dev/null @@ -1,7 +0,0 @@ -import envCi from "env-ci"; - -export default function trustedCiProvider() { - const { name } = envCi(); - - return "GitHub Actions" === name || "GitLab CI/CD" === name; -} diff --git a/test/trusted-publishing/oidc-context.test.js b/test/trusted-publishing/oidc-context.test.js index 2636c9ee..eb5dd433 100644 --- a/test/trusted-publishing/oidc-context.test.js +++ b/test/trusted-publishing/oidc-context.test.js @@ -8,7 +8,6 @@ const pkg = {}; test.beforeEach(async (t) => { await td.replace(globalThis, "fetch"); - ({ default: trustedCiProvider } = await td.replaceEsm("../../lib/trusted-publishing/supported-ci-provider.js")); ({ default: exchangeToken } = await td.replaceEsm("../../lib/trusted-publishing/token-exchange.js")); td.when(exchangeToken(pkg)).thenResolve(undefined); @@ -22,7 +21,6 @@ test.afterEach.always((t) => { test.serial( "that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => { - td.when(trustedCiProvider()).thenResolve(true); td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); td.when(exchangeToken(pkg)).thenResolve("token-value"); @@ -30,17 +28,7 @@ test.serial( } ); -test.serial( - "that `false` is returned when the official registry is targeted, but outside the context of a supported CI provider", - async (t) => { - td.when(trustedCiProvider()).thenResolve(false); - - t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); - } -); - test.serial("that `false` is returned when OIDC token exchange fails in a supported CI provider", async (t) => { - td.when(trustedCiProvider()).thenResolve(true); td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); td.when(exchangeToken(pkg)).thenResolve(undefined); diff --git a/test/trusted-publishing/supported-ci-provider.test.js b/test/trusted-publishing/supported-ci-provider.test.js deleted file mode 100644 index c00fe43c..00000000 --- a/test/trusted-publishing/supported-ci-provider.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import test from "ava"; -import * as td from "testdouble"; - -//https://docs.npmjs.com/trusted-publishers#supported-cicd-providers - -let envCi, trustedCiProvider; - -test.beforeEach(async (t) => { - ({ default: envCi } = await td.replaceEsm("env-ci")); - - ({ default: trustedCiProvider } = await import("../../lib/trusted-publishing/supported-ci-provider.js")); -}); - -test.afterEach.always((t) => { - td.reset(); -}); - -test("that `true` is returned when GitHub Actions is detected", async (t) => { - td.when(envCi()).thenReturn({ name: "GitHub Actions" }); - - t.true(await trustedCiProvider()); -}); - -test("that `true` is returned when GitLab Pipelines is detected", async (t) => { - td.when(envCi()).thenReturn({ name: "GitLab CI/CD" }); - - t.true(await trustedCiProvider()); -}); - -test("that `false` is returned when no supported CI provider is detected", async (t) => { - td.when(envCi()).thenReturn({ name: "Other Service" }); - - t.false(await trustedCiProvider()); -}); From 67ee603b4be9e21cc6f59e56b7bd006130444ad7 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 13:57:59 -0500 Subject: [PATCH 37/45] fix(verify-auth): stream output of the dry-run for custom registries for #958 --- lib/verify-auth.js | 40 ++++++++++++++++++++++++++-------------- test/verify-auth.test.js | 18 ++++++++++++------ 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index da7f6779..5eef0364 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -11,7 +11,14 @@ function registryIsDefault(registry, DEFAULT_NPM_REGISTRY) { return normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY); } -async function verifyAuthContextAgainstRegistry(npmrc, registry, cwd, env, stdout, stderr) { +async function verifyAuthContextAgainstRegistry(npmrc, registry, context) { + const { + cwd, + env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY, ...env }, + stdout, + stderr, + } = context; + try { const whoamiResult = execa("npm", ["whoami", "--userconfig", npmrc, "--registry", registry], { cwd, @@ -28,15 +35,22 @@ async function verifyAuthContextAgainstRegistry(npmrc, registry, cwd, env, stdou } } -async function attemptPublishDryRun(npmrc, registry, cwd, env) { +async function attemptPublishDryRun(npmrc, registry, context) { + const { + cwd, + env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY, ...env }, + stdout, + stderr, + } = context; + const publishDryRunResult = execa( "npm", ["publish", "--dry-run", "--tag=semantic-release-auth-check", "--userconfig", npmrc, "--registry", registry], { cwd, env, preferLocal: true, lines: true } ); - // publishDryRunResult.stdout.pipe(stdout, { end: false }); - // publishDryRunResult.stderr.pipe(stderr, { end: false }); + publishDryRunResult.stdout.pipe(stdout, { end: false }); + publishDryRunResult.stderr.pipe(stderr, { end: false }); (await publishDryRunResult).stderr.forEach((line) => { if (line.includes("This command requires you to be logged in to ")) { @@ -45,21 +59,19 @@ async function attemptPublishDryRun(npmrc, registry, cwd, env) { }); } -async function verifyTokenAuth(registry, DEFAULT_NPM_REGISTRY, npmrc, cwd, env, stdout, stderr) { +async function verifyTokenAuth(registry, npmrc, context) { + const { + env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY }, + } = context; + if (registryIsDefault(registry, DEFAULT_NPM_REGISTRY)) { - await verifyAuthContextAgainstRegistry(npmrc, registry, cwd, env, stdout, stderr); + await verifyAuthContextAgainstRegistry(npmrc, registry, context); } else { - await attemptPublishDryRun(npmrc, registry, cwd, env); + await attemptPublishDryRun(npmrc, registry, context); } } export default async function (npmrc, pkg, context) { - const { - cwd, - env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY, ...env }, - stdout, - stderr, - } = context; const registry = getRegistry(pkg, context); if (oidcContextEstablished(registry, pkg)) { @@ -68,5 +80,5 @@ export default async function (npmrc, pkg, context) { await setNpmrcAuth(npmrc, registry, context); - await verifyTokenAuth(registry, DEFAULT_NPM_REGISTRY, npmrc, cwd, env, stdout, stderr); + await verifyTokenAuth(registry, npmrc, context); } diff --git a/test/verify-auth.test.js b/test/verify-auth.test.js index a64217fa..78a5a8d3 100644 --- a/test/verify-auth.test.js +++ b/test/verify-auth.test.js @@ -83,6 +83,11 @@ test.serial( "that a publish dry run is performed to validate token presence when publishing to a custom registry", async (t) => { const otherRegistry = "https://other.registry.org"; + const execaResult = Promise.resolve({ + stderr: ["foo", "bar", "baz"], + }); + execaResult.stderr = { pipe: () => undefined }; + execaResult.stdout = { pipe: () => undefined }; td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); td.when(oidcContextEstablished(otherRegistry, pkg)).thenReturn(false); td.when( @@ -104,9 +109,7 @@ test.serial( lines: true, } ) - ).thenResolve({ - stderr: ["foo", "bar", "baz"], - }); + ).thenReturn(execaResult); await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); } @@ -118,6 +121,11 @@ test.serial( "that the token is considered invalid when the publish dry run fails when publishing to a custom registry", async (t) => { const otherRegistry = "https://other.registry.org"; + const execaResult = Promise.resolve({ + stderr: ["foo", "bar", "baz", `This command requires you to be logged in to ${otherRegistry}`, "qux"], + }); + execaResult.stderr = { pipe: () => undefined }; + execaResult.stdout = { pipe: () => undefined }; td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); td.when(oidcContextEstablished(otherRegistry, pkg)).thenReturn(false); td.when( @@ -139,9 +147,7 @@ test.serial( lines: true, } ) - ).thenResolve({ - stderr: ["foo", "bar", "baz", `This command requires you to be logged in to ${otherRegistry}`, "qux"], - }); + ).thenReturn(execaResult); const { errors: [error], From a4a5add415ab4ebdcddc78b0a8bedf4e66ec72f0 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 14:21:39 -0500 Subject: [PATCH 38/45] feat(trusted-publishing): log progression of token-exchange steps for #958 --- lib/trusted-publishing/oidc-context.js | 4 +-- lib/trusted-publishing/token-exchange.js | 30 +++++++++++++------ lib/verify-auth.js | 2 +- test/trusted-publishing/oidc-context.test.js | 13 ++++---- .../trusted-publishing/token-exchange.test.js | 15 +++++----- test/verify-auth.test.js | 11 +++---- 6 files changed, 45 insertions(+), 30 deletions(-) diff --git a/lib/trusted-publishing/oidc-context.js b/lib/trusted-publishing/oidc-context.js index e6b134fd..0427ae9d 100644 --- a/lib/trusted-publishing/oidc-context.js +++ b/lib/trusted-publishing/oidc-context.js @@ -1,6 +1,6 @@ import { OFFICIAL_REGISTRY } from "../definitions/constants.js"; import exchangeToken from "./token-exchange.js"; -export default async function oidcContextEstablished(registry, pkg) { - return OFFICIAL_REGISTRY === registry && !!(await exchangeToken(pkg)); +export default async function oidcContextEstablished(registry, pkg, context) { + return OFFICIAL_REGISTRY === registry && !!(await exchangeToken(pkg, context)); } diff --git a/lib/trusted-publishing/token-exchange.js b/lib/trusted-publishing/token-exchange.js index 0d59c737..238f37eb 100644 --- a/lib/trusted-publishing/token-exchange.js +++ b/lib/trusted-publishing/token-exchange.js @@ -7,7 +7,7 @@ import { GITLAB_PIPELINES_PROVIDER_NAME, } from "../definitions/constants.js"; -async function exchangeIdToken(idToken, packageName) { +async function exchangeIdToken(idToken, packageName, logger) { const response = await fetch( `${OFFICIAL_REGISTRY}-/npm/v1/oidc/token/exchange/package/${encodeURIComponent(packageName)}`, { @@ -15,45 +15,57 @@ async function exchangeIdToken(idToken, packageName) { headers: { Authorization: `Bearer ${idToken}` }, } ); + const responseBody = await response.json(); if (response.ok) { - return (await response.json()).token; + logger.log("OIDC token exchange with the npm registry succeeded"); + + return responseBody.token; } + logger.log(`OIDC token exchange with the npm registry failed: ${response.status} ${responseBody.message}`); + return undefined; } -async function exchangeGithubActionsToken(packageName) { +async function exchangeGithubActionsToken(packageName, logger) { let idToken; + logger.log("Verifying OIDC context for publishing from GitHub Actions"); + try { idToken = await getIDToken("npm:registry.npmjs.org"); } catch (e) { + logger.log(`Retrieval of GitHub Actions OIDC token failed: ${e.message}`); + logger.log("Have you granted the `id-token: write` permission to this workflow?"); + return undefined; } - return exchangeIdToken(idToken, packageName); + return exchangeIdToken(idToken, packageName, logger); } -async function exchangeGitlabPipelinesToken(packageName) { +async function exchangeGitlabPipelinesToken(packageName, logger) { const idToken = process.env.NPM_ID_TOKEN; + logger.log("Verifying OIDC context for publishing from GitLab Pipelines"); + if (!idToken) { return undefined; } - return await exchangeIdToken(idToken, packageName); + return exchangeIdToken(idToken, packageName, logger); } -export default async function exchangeToken(pkg) { +export default function exchangeToken(pkg, {logger}) { const { name: ciProviderName } = envCi(); if (GITHUB_ACTIONS_PROVIDER_NAME === ciProviderName) { - return await exchangeGithubActionsToken(pkg.name); + return exchangeGithubActionsToken(pkg.name, logger); } if (GITLAB_PIPELINES_PROVIDER_NAME === ciProviderName) { - return await exchangeGitlabPipelinesToken(pkg.name); + return exchangeGitlabPipelinesToken(pkg.name, logger); } return undefined; diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 5eef0364..ea3f5f0a 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -74,7 +74,7 @@ async function verifyTokenAuth(registry, npmrc, context) { export default async function (npmrc, pkg, context) { const registry = getRegistry(pkg, context); - if (oidcContextEstablished(registry, pkg)) { + if (oidcContextEstablished(registry, pkg, context)) { return; } diff --git a/test/trusted-publishing/oidc-context.test.js b/test/trusted-publishing/oidc-context.test.js index eb5dd433..f07c2161 100644 --- a/test/trusted-publishing/oidc-context.test.js +++ b/test/trusted-publishing/oidc-context.test.js @@ -5,11 +5,12 @@ import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; let oidcContextEstablished, trustedCiProvider, exchangeToken; const pkg = {}; +const context = {}; test.beforeEach(async (t) => { await td.replace(globalThis, "fetch"); ({ default: exchangeToken } = await td.replaceEsm("../../lib/trusted-publishing/token-exchange.js")); - td.when(exchangeToken(pkg)).thenResolve(undefined); + td.when(exchangeToken(pkg, context)).thenResolve(undefined); ({ default: oidcContextEstablished } = await import("../../lib/trusted-publishing/oidc-context.js")); }); @@ -22,19 +23,19 @@ test.serial( "that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => { td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); - td.when(exchangeToken(pkg)).thenResolve("token-value"); + td.when(exchangeToken(pkg, context)).thenResolve("token-value"); - t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); + t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg, context)); } ); test.serial("that `false` is returned when OIDC token exchange fails in a supported CI provider", async (t) => { td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); - td.when(exchangeToken(pkg)).thenResolve(undefined); + td.when(exchangeToken(pkg, context)).thenResolve(undefined); - t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg)); + t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg, context)); }); test.serial("that `false` is returned when a custom registry is targeted", async (t) => { - t.false(await oidcContextEstablished("https://custom.registry.org/", pkg)); + t.false(await oidcContextEstablished("https://custom.registry.org/", pkg, context)); }); diff --git a/test/trusted-publishing/token-exchange.test.js b/test/trusted-publishing/token-exchange.test.js index f4e38940..db38a109 100644 --- a/test/trusted-publishing/token-exchange.test.js +++ b/test/trusted-publishing/token-exchange.test.js @@ -14,6 +14,7 @@ const packageName = "@scope/some-package"; const pkg = { name: packageName }; const idToken = "id-token-value"; const token = "token-value"; +const logger = { log: () => undefined }; test.beforeEach(async (t) => { await td.replace(globalThis, "fetch"); @@ -41,7 +42,7 @@ test.serial("that an access token is returned when token exchange succeeds on Gi new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } }) ); - t.is(await exchangeToken(pkg), token); + t.is(await exchangeToken(pkg, {logger}), token); }); test.serial("that `undefined` is returned when ID token retrieval fails on GitHub Actions", async (t) => { @@ -50,7 +51,7 @@ test.serial("that `undefined` is returned when ID token retrieval fails on GitHu new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable") ); - t.is(await exchangeToken(pkg), undefined); + t.is(await exchangeToken(pkg, {logger}), undefined); }); test.serial("that `undefined` is returned when token exchange fails on GitHub Actions", async (t) => { @@ -65,7 +66,7 @@ test.serial("that `undefined` is returned when token exchange fails on GitHub Ac new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } }) ); - t.is(await exchangeToken(pkg), undefined); + t.is(await exchangeToken(pkg, {logger}), undefined); }); test.serial("that an access token is returned when token exchange succeeds on GitLab Pipelines", async (t) => { @@ -80,13 +81,13 @@ test.serial("that an access token is returned when token exchange succeeds on Gi new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } }) ); - t.is(await exchangeToken(pkg), token); + t.is(await exchangeToken(pkg, {logger}), token); }); test.serial("that `undefined` is returned when ID token is not available on GitLab Pipelines", async (t) => { td.when(envCi()).thenReturn({ name: GITLAB_PIPELINES_PROVIDER_NAME }); - t.is(await exchangeToken(pkg), undefined); + t.is(await exchangeToken(pkg, {logger}), undefined); }); test.serial("that `undefined` is returned when token exchange fails on GitLab Pipelines", async (t) => { @@ -101,11 +102,11 @@ test.serial("that `undefined` is returned when token exchange fails on GitLab Pi new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } }) ); - t.is(await exchangeToken(pkg), undefined); + t.is(await exchangeToken(pkg, {logger}), undefined); }); test.serial("that `undefined` is returned when no supported CI provider is detected", async (t) => { td.when(envCi()).thenReturn({ name: "Other Service" }); - t.is(await exchangeToken(pkg), undefined); + t.is(await exchangeToken(pkg, {logger}), undefined); }); diff --git a/test/verify-auth.test.js b/test/verify-auth.test.js index 78a5a8d3..a994db72 100644 --- a/test/verify-auth.test.js +++ b/test/verify-auth.test.js @@ -30,7 +30,7 @@ test.serial( "that the auth context for the official registry is considered valid when trusted publishing is established", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg)).thenReturn(true); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenReturn(true); await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); } @@ -40,7 +40,7 @@ test.serial( "that the provided token is verified with `npm whoami` when trusted publishing is not established for the official registry", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg)).thenReturn(false); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenReturn(false); td.when( execa("npm", ["whoami", "--userconfig", npmrc, "--registry", DEFAULT_NPM_REGISTRY], { cwd, @@ -60,7 +60,7 @@ test.serial( "that the auth context for the official registry is considered invalid when no token is provided and trusted publishing is not established", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg)).thenReturn(false); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenReturn(false); td.when( execa("npm", ["whoami", "--userconfig", npmrc, "--registry", DEFAULT_NPM_REGISTRY], { cwd, @@ -89,7 +89,7 @@ test.serial( execaResult.stderr = { pipe: () => undefined }; execaResult.stdout = { pipe: () => undefined }; td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); - td.when(oidcContextEstablished(otherRegistry, pkg)).thenReturn(false); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenReturn(false); td.when( execa( "npm", @@ -127,7 +127,7 @@ test.serial( execaResult.stderr = { pipe: () => undefined }; execaResult.stdout = { pipe: () => undefined }; td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); - td.when(oidcContextEstablished(otherRegistry, pkg)).thenReturn(false); + td.when(oidcContextEstablished(otherRegistry, pkg, context)).thenReturn(false); td.when( execa( "npm", @@ -163,6 +163,7 @@ test.serial("that errors from setting up auth bubble through this function", asy const registry = DEFAULT_NPM_REGISTRY; const thrownError = new Error(); td.when(getRegistry(pkg, context)).thenReturn(registry); + td.when(oidcContextEstablished(registry, pkg, context)).thenReturn(false); td.when(setNpmrcAuth(npmrc, registry, context)).thenThrow(new AggregateError([thrownError])); const { From 23c8610feb429809eae0516b8c622e3472f742df Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 14:30:04 -0500 Subject: [PATCH 39/45] fix(trusted-publishing): properly await the check for trusted publishing context for #958 --- lib/verify-auth.js | 2 +- test/trusted-publishing/oidc-context.test.js | 5 +---- test/verify-auth.test.js | 12 ++++++------ 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/lib/verify-auth.js b/lib/verify-auth.js index ea3f5f0a..48e0f5fc 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -74,7 +74,7 @@ async function verifyTokenAuth(registry, npmrc, context) { export default async function (npmrc, pkg, context) { const registry = getRegistry(pkg, context); - if (oidcContextEstablished(registry, pkg, context)) { + if (await oidcContextEstablished(registry, pkg, context)) { return; } diff --git a/test/trusted-publishing/oidc-context.test.js b/test/trusted-publishing/oidc-context.test.js index f07c2161..97e3b1d1 100644 --- a/test/trusted-publishing/oidc-context.test.js +++ b/test/trusted-publishing/oidc-context.test.js @@ -3,12 +3,11 @@ import * as td from "testdouble"; import { OFFICIAL_REGISTRY } from "../../lib/definitions/constants.js"; -let oidcContextEstablished, trustedCiProvider, exchangeToken; +let oidcContextEstablished, exchangeToken; const pkg = {}; const context = {}; test.beforeEach(async (t) => { - await td.replace(globalThis, "fetch"); ({ default: exchangeToken } = await td.replaceEsm("../../lib/trusted-publishing/token-exchange.js")); td.when(exchangeToken(pkg, context)).thenResolve(undefined); @@ -22,7 +21,6 @@ test.afterEach.always((t) => { test.serial( "that `true` is returned when a trusted-publishing context has been established with the official registry", async (t) => { - td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); td.when(exchangeToken(pkg, context)).thenResolve("token-value"); t.true(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg, context)); @@ -30,7 +28,6 @@ test.serial( ); test.serial("that `false` is returned when OIDC token exchange fails in a supported CI provider", async (t) => { - td.when(fetch("https://matt.travi.org")).thenResolve(new Response(null, { status: 401 })); td.when(exchangeToken(pkg, context)).thenResolve(undefined); t.false(await oidcContextEstablished(OFFICIAL_REGISTRY, pkg, context)); diff --git a/test/verify-auth.test.js b/test/verify-auth.test.js index a994db72..f4b39acc 100644 --- a/test/verify-auth.test.js +++ b/test/verify-auth.test.js @@ -30,7 +30,7 @@ test.serial( "that the auth context for the official registry is considered valid when trusted publishing is established", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenReturn(true); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenResolve(true); await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); } @@ -40,7 +40,7 @@ test.serial( "that the provided token is verified with `npm whoami` when trusted publishing is not established for the official registry", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenReturn(false); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenResolve(false); td.when( execa("npm", ["whoami", "--userconfig", npmrc, "--registry", DEFAULT_NPM_REGISTRY], { cwd, @@ -60,7 +60,7 @@ test.serial( "that the auth context for the official registry is considered invalid when no token is provided and trusted publishing is not established", async (t) => { td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenReturn(false); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenResolve(false); td.when( execa("npm", ["whoami", "--userconfig", npmrc, "--registry", DEFAULT_NPM_REGISTRY], { cwd, @@ -89,7 +89,7 @@ test.serial( execaResult.stderr = { pipe: () => undefined }; execaResult.stdout = { pipe: () => undefined }; td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); - td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenReturn(false); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenResolve(false); td.when( execa( "npm", @@ -127,7 +127,7 @@ test.serial( execaResult.stderr = { pipe: () => undefined }; execaResult.stdout = { pipe: () => undefined }; td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); - td.when(oidcContextEstablished(otherRegistry, pkg, context)).thenReturn(false); + td.when(oidcContextEstablished(otherRegistry, pkg, context)).thenResolve(false); td.when( execa( "npm", @@ -163,7 +163,7 @@ test.serial("that errors from setting up auth bubble through this function", asy const registry = DEFAULT_NPM_REGISTRY; const thrownError = new Error(); td.when(getRegistry(pkg, context)).thenReturn(registry); - td.when(oidcContextEstablished(registry, pkg, context)).thenReturn(false); + td.when(oidcContextEstablished(registry, pkg, context)).thenResolve(false); td.when(setNpmrcAuth(npmrc, registry, context)).thenThrow(new AggregateError([thrownError])); const { From e5c98574c59735d63b2ac3dd6efae4cd9ee0e032 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 15:01:36 -0500 Subject: [PATCH 40/45] docs(trusted-publishing): add details to the readme for configuring within GitLab Pipelines for #958 --- README.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 69e94ede..0a568999 100644 --- a/README.md +++ b/README.md @@ -47,18 +47,18 @@ When publishing to the [official registry](https://registry.npmjs.org/), it is r - [Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended when publishing from a CI provider that is not supported by npm for trusted publishing, and can be set via [environment variables](#environment-variables). Because these access tokens expire, rotation will need to be accounted for in this scenario. +> [!NOTE] +> When using trusted publishing, provenance attestations are automatically generated for your packages without requiring provenance to be explicitly enabled. + #### Trusted publishing from GitHub Actions -To publish with OIDC from GitHub Actions, the `id-token: write` permission is required to be enabled on the job: +To leverage trusted publishing and publish with provenance from GitHub Actions, the `id-token: write` permission is required to be enabled on the job: ```yaml permissions: id-token: write # to enable use of OIDC for trusted publishing and npm provenance ``` -> [!NOTE] -> When using trusted publishing, provenance attestations are automatically generated for your packages without requiring provenance to be explicitly enabled. - It's also worth noting that if you are using semantic-release to its fullest with a GitHub release, GitHub comments, and other features, then [more permissions are required](https://github.com/semantic-release/github#github-authentication) to be enabled on this job: @@ -72,6 +72,18 @@ permissions: Refer to the [GitHub Actions recipe for npm package provenance](https://semantic-release.gitbook.io/semantic-release/recipes/ci-configurations/github-actions#.github-workflows-release.yml-configuration-for-node-projects) for the full CI job's YAML code example. +#### Trusted publishing for GitLab Pipelines + +To leverage trusted publishing and publish with provenance from GitLab Pipelines, `NPM_ID_TOKEN` needs to be added as an entry under `id_tokens` in the job definition with an audience of `npm:registry.npmjs.org`: + +```yaml +id_tokens: + NPM_ID_TOKEN: + aud: "npm:registry.npmjs.org" +``` + +See the [npm documentation for more detail about configuring pipeline details](https://docs.npmjs.com/trusted-publishers#gitlab-cicd-configuration) + ### Alternative Registries The npm token authentication configuration is **required** and can be set via [environment variables](#environment-variables). From 38b0466f89ec7983f01824a55203e77237c77697 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 15:06:02 -0500 Subject: [PATCH 41/45] refactor: resolve prettier issues --- lib/trusted-publishing/token-exchange.js | 2 +- test/trusted-publishing/token-exchange.test.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/trusted-publishing/token-exchange.js b/lib/trusted-publishing/token-exchange.js index 238f37eb..899560b2 100644 --- a/lib/trusted-publishing/token-exchange.js +++ b/lib/trusted-publishing/token-exchange.js @@ -57,7 +57,7 @@ async function exchangeGitlabPipelinesToken(packageName, logger) { return exchangeIdToken(idToken, packageName, logger); } -export default function exchangeToken(pkg, {logger}) { +export default function exchangeToken(pkg, { logger }) { const { name: ciProviderName } = envCi(); if (GITHUB_ACTIONS_PROVIDER_NAME === ciProviderName) { diff --git a/test/trusted-publishing/token-exchange.test.js b/test/trusted-publishing/token-exchange.test.js index db38a109..f9ea19bd 100644 --- a/test/trusted-publishing/token-exchange.test.js +++ b/test/trusted-publishing/token-exchange.test.js @@ -42,7 +42,7 @@ test.serial("that an access token is returned when token exchange succeeds on Gi new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } }) ); - t.is(await exchangeToken(pkg, {logger}), token); + t.is(await exchangeToken(pkg, { logger }), token); }); test.serial("that `undefined` is returned when ID token retrieval fails on GitHub Actions", async (t) => { @@ -51,7 +51,7 @@ test.serial("that `undefined` is returned when ID token retrieval fails on GitHu new Error("Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable") ); - t.is(await exchangeToken(pkg, {logger}), undefined); + t.is(await exchangeToken(pkg, { logger }), undefined); }); test.serial("that `undefined` is returned when token exchange fails on GitHub Actions", async (t) => { @@ -66,7 +66,7 @@ test.serial("that `undefined` is returned when token exchange fails on GitHub Ac new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } }) ); - t.is(await exchangeToken(pkg, {logger}), undefined); + t.is(await exchangeToken(pkg, { logger }), undefined); }); test.serial("that an access token is returned when token exchange succeeds on GitLab Pipelines", async (t) => { @@ -81,13 +81,13 @@ test.serial("that an access token is returned when token exchange succeeds on Gi new Response(JSON.stringify({ token }), { status: 201, headers: { "Content-Type": "application/json" } }) ); - t.is(await exchangeToken(pkg, {logger}), token); + t.is(await exchangeToken(pkg, { logger }), token); }); test.serial("that `undefined` is returned when ID token is not available on GitLab Pipelines", async (t) => { td.when(envCi()).thenReturn({ name: GITLAB_PIPELINES_PROVIDER_NAME }); - t.is(await exchangeToken(pkg, {logger}), undefined); + t.is(await exchangeToken(pkg, { logger }), undefined); }); test.serial("that `undefined` is returned when token exchange fails on GitLab Pipelines", async (t) => { @@ -102,11 +102,11 @@ test.serial("that `undefined` is returned when token exchange fails on GitLab Pi new Response(JSON.stringify({ message: "foo" }), { status: 401, headers: { "Content-Type": "application/json" } }) ); - t.is(await exchangeToken(pkg, {logger}), undefined); + t.is(await exchangeToken(pkg, { logger }), undefined); }); test.serial("that `undefined` is returned when no supported CI provider is detected", async (t) => { td.when(envCi()).thenReturn({ name: "Other Service" }); - t.is(await exchangeToken(pkg, {logger}), undefined); + t.is(await exchangeToken(pkg, { logger }), undefined); }); From d351eca722bf7d4b5eaaa044ff135d3ebc3f6dc4 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Wed, 15 Oct 2025 22:00:51 -0500 Subject: [PATCH 42/45] ci(node-versions): disable node v24 from the verification matrix for now since stubbing `@actions/core` breaks in that version and i don't want figuring that out to delay getting the trusted-publishing feature out for #958 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b3057c01..e5db92bc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,7 @@ jobs: node-version: - 22.14.0 - 22 - - 24 + # - 24 os: - ubuntu-latest runs-on: "${{ matrix.os }}" From e7d684c8d143f3541b056bc22dc509568d70b667 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sat, 18 Oct 2025 09:20:44 -0500 Subject: [PATCH 43/45] fix(verify-auth): enable the publish dry-run to work for projects publishing from a sub-directory --- index.js | 8 +++--- lib/verify-auth.js | 23 ++++++++++++----- test/verify-auth.test.js | 54 ++++++++++++++++++++++++++++++++++------ 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/index.js b/index.js index 590eace3..d43ada7e 100644 --- a/index.js +++ b/index.js @@ -30,7 +30,7 @@ export async function verifyConditions(pluginConfig, context) { // Verify the npm authentication only if `npmPublish` is not false and `pkg.private` is not `true` if (pluginConfig.npmPublish !== false && pkg.private !== true) { - await verifyNpmAuth(npmrc, pkg, context); + await verifyNpmAuth(npmrc, pkg, pluginConfig, context); } } catch (error) { errors.push(...error.errors); @@ -50,7 +50,7 @@ export async function prepare(pluginConfig, context) { // Reload package.json in case a previous external step updated it const pkg = await getPkg(pluginConfig, context); if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) { - await verifyNpmAuth(npmrc, pkg, context); + await verifyNpmAuth(npmrc, pkg, pluginConfig, context); } } catch (error) { errors.push(...error.errors); @@ -72,7 +72,7 @@ export async function publish(pluginConfig, context) { // Reload package.json in case a previous external step updated it pkg = await getPkg(pluginConfig, context); if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) { - await verifyNpmAuth(npmrc, pkg, context); + await verifyNpmAuth(npmrc, pkg, pluginConfig, context); } } catch (error) { errors.push(...error.errors); @@ -97,7 +97,7 @@ export async function addChannel(pluginConfig, context) { // Reload package.json in case a previous external step updated it pkg = await getPkg(pluginConfig, context); if (!verified && pluginConfig.npmPublish !== false && pkg.private !== true) { - await verifyNpmAuth(npmrc, pkg, context); + await verifyNpmAuth(npmrc, pkg, pluginConfig, context); } } catch (error) { errors.push(...error.errors); diff --git a/lib/verify-auth.js b/lib/verify-auth.js index 48e0f5fc..7bb44ecb 100644 --- a/lib/verify-auth.js +++ b/lib/verify-auth.js @@ -6,6 +6,7 @@ import setNpmrcAuth from "./set-npmrc-auth.js"; import getError from "./get-error.js"; import oidcContextEstablished from "./trusted-publishing/oidc-context.js"; import { OFFICIAL_REGISTRY } from "./definitions/constants.js"; +import path from "path"; function registryIsDefault(registry, DEFAULT_NPM_REGISTRY) { return normalizeUrl(registry) === normalizeUrl(DEFAULT_NPM_REGISTRY); @@ -35,17 +36,27 @@ async function verifyAuthContextAgainstRegistry(npmrc, registry, context) { } } -async function attemptPublishDryRun(npmrc, registry, context) { +async function attemptPublishDryRun(npmrc, registry, context, pkgRoot) { const { cwd, env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY, ...env }, stdout, stderr, } = context; + const basePath = pkgRoot ? path.resolve(cwd, pkgRoot) : cwd; const publishDryRunResult = execa( "npm", - ["publish", "--dry-run", "--tag=semantic-release-auth-check", "--userconfig", npmrc, "--registry", registry], + [ + "publish", + basePath, + "--dry-run", + "--tag=semantic-release-auth-check", + "--userconfig", + npmrc, + "--registry", + registry, + ], { cwd, env, preferLocal: true, lines: true } ); @@ -59,7 +70,7 @@ async function attemptPublishDryRun(npmrc, registry, context) { }); } -async function verifyTokenAuth(registry, npmrc, context) { +async function verifyTokenAuth(registry, npmrc, context, pkgRoot) { const { env: { DEFAULT_NPM_REGISTRY = OFFICIAL_REGISTRY }, } = context; @@ -67,11 +78,11 @@ async function verifyTokenAuth(registry, npmrc, context) { if (registryIsDefault(registry, DEFAULT_NPM_REGISTRY)) { await verifyAuthContextAgainstRegistry(npmrc, registry, context); } else { - await attemptPublishDryRun(npmrc, registry, context); + await attemptPublishDryRun(npmrc, registry, context, pkgRoot); } } -export default async function (npmrc, pkg, context) { +export default async function (npmrc, pkg, { pkgRoot }, context) { const registry = getRegistry(pkg, context); if (await oidcContextEstablished(registry, pkg, context)) { @@ -80,5 +91,5 @@ export default async function (npmrc, pkg, context) { await setNpmrcAuth(npmrc, registry, context); - await verifyTokenAuth(registry, npmrc, context); + await verifyTokenAuth(registry, npmrc, context, pkgRoot); } diff --git a/test/verify-auth.test.js b/test/verify-auth.test.js index f4b39acc..3ca0fdbc 100644 --- a/test/verify-auth.test.js +++ b/test/verify-auth.test.js @@ -32,7 +32,7 @@ test.serial( td.when(getRegistry(pkg, context)).thenReturn(DEFAULT_NPM_REGISTRY); td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenResolve(true); - await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); + await t.notThrowsAsync(verifyAuth(npmrc, pkg, {}, context)); } ); @@ -52,7 +52,7 @@ test.serial( stderr: { pipe: () => undefined }, }); - await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); + await t.notThrowsAsync(verifyAuth(npmrc, pkg, {}, context)); } ); @@ -71,7 +71,7 @@ test.serial( const { errors: [error], - } = await t.throwsAsync(verifyAuth(npmrc, pkg, context)); + } = await t.throwsAsync(verifyAuth(npmrc, pkg, {}, context)); t.is(error.name, "SemanticReleaseError"); t.is(error.code, "EINVALIDNPMTOKEN"); @@ -95,6 +95,45 @@ test.serial( "npm", [ "publish", + cwd, + "--dry-run", + "--tag=semantic-release-auth-check", + "--userconfig", + npmrc, + "--registry", + otherRegistry, + ], + { + cwd, + env: otherEnvVars, + preferLocal: true, + lines: true, + } + ) + ).thenReturn(execaResult); + + await t.notThrowsAsync(verifyAuth(npmrc, pkg, {}, context)); + } +); + +test.serial( + "that a publish dry run is performed to validate token presence when publishing to a custom registry from a sub-directory", + async (t) => { + const otherRegistry = "https://other.registry.org"; + const pkgRoot = "/dist"; + const execaResult = Promise.resolve({ + stderr: ["foo", "bar", "baz"], + }); + execaResult.stderr = { pipe: () => undefined }; + execaResult.stdout = { pipe: () => undefined }; + td.when(getRegistry(pkg, context)).thenReturn(otherRegistry); + td.when(oidcContextEstablished(DEFAULT_NPM_REGISTRY, pkg, context)).thenResolve(false); + td.when( + execa( + "npm", + [ + "publish", + pkgRoot, "--dry-run", "--tag=semantic-release-auth-check", "--userconfig", @@ -111,12 +150,12 @@ test.serial( ) ).thenReturn(execaResult); - await t.notThrowsAsync(verifyAuth(npmrc, pkg, context)); + await t.notThrowsAsync(verifyAuth(npmrc, pkg, { pkgRoot }, context)); } ); // since alternative registries are not consistent in implementing `npm whoami`, -// we do not attempt to verify the provided token when publishing to them +// the best we can attempt to verify is to do a dry run publish and check for auth warnings test.serial( "that the token is considered invalid when the publish dry run fails when publishing to a custom registry", async (t) => { @@ -133,6 +172,7 @@ test.serial( "npm", [ "publish", + cwd, "--dry-run", "--tag=semantic-release-auth-check", "--userconfig", @@ -151,7 +191,7 @@ test.serial( const { errors: [error], - } = await t.throwsAsync(verifyAuth(npmrc, pkg, context)); + } = await t.throwsAsync(verifyAuth(npmrc, pkg, {}, context)); t.is(error.name, "SemanticReleaseError"); t.is(error.code, "EINVALIDNPMAUTH"); @@ -168,7 +208,7 @@ test.serial("that errors from setting up auth bubble through this function", asy const { errors: [error], - } = await t.throwsAsync(verifyAuth(npmrc, pkg, context)); + } = await t.throwsAsync(verifyAuth(npmrc, pkg, {}, context)); t.is(error, thrownError); }); From cf14bd9098e5ff51f46615f4b65766c3b1612037 Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sat, 18 Oct 2025 09:38:36 -0500 Subject: [PATCH 44/45] docs(trusted-publishing): expand further around details for publishing in various contexts for #958 --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0a568999..261fca76 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The plugin can be configured in the [**semantic-release** configuration file](ht When publishing to the [official registry](https://registry.npmjs.org/), it is recommended to publish with authentication intended for automation: -- Since access tokens have recently had their [maximum lifetimes restricted](https://github.blog/changelog/2025-09-29-strengthening-npm-security-important-changes-to-authentication-and-token-management/), +- For improved security, and since access tokens have recently had their [maximum lifetimes restricted](https://github.blog/changelog/2025-09-29-strengthening-npm-security-important-changes-to-authentication-and-token-management/), [trusted publishing](https://docs.npmjs.com/trusted-publishers) is recommended when publishing from a [supported CI provider](https://docs.npmjs.com/trusted-publishers#supported-cicd-providers) - [Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended when publishing from a CI provider that is not supported by npm for trusted publishing, and can be set via [environment variables](#environment-variables). Because these access tokens expire, rotation will need to be accounted for in this scenario. @@ -82,11 +82,17 @@ id_tokens: aud: "npm:registry.npmjs.org" ``` -See the [npm documentation for more detail about configuring pipeline details](https://docs.npmjs.com/trusted-publishers#gitlab-cicd-configuration) +See the [npm documentation for more details about configuring pipeline details](https://docs.npmjs.com/trusted-publishers#gitlab-cicd-configuration) + +#### Unsupported CI providers + +Token authentication is **required** and can be set via [environment variables](#environment-variables). +[Granular access tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-granular-access-tokens-on-the-website) are recommended in this scenario, since trusted publishing is not available from all CI providers. +Because these access tokens expire, rotation will need to be accounted for in your process. ### Alternative Registries -The npm token authentication configuration is **required** and can be set via [environment variables](#environment-variables). +Token authentication is **required** and can be set via [environment variables](#environment-variables). See the documentation for your registry for details on how to create a token for automation. ### Environment variables From 316ce2161e1a7894ce9775b4c8788b966c4dc0db Mon Sep 17 00:00:00 2001 From: Matt Travi Date: Sat, 18 Oct 2025 10:28:23 -0500 Subject: [PATCH 45/45] feat(trusted-publishing): refine the messages for related errors now that the ordering of checks and fallbacks have been settled for #958 --- lib/definitions/errors.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/definitions/errors.js b/lib/definitions/errors.js index 6237c79c..6458f6cd 100644 --- a/lib/definitions/errors.js +++ b/lib/definitions/errors.js @@ -37,20 +37,22 @@ Your configuration for the \`pkgRoot\` option is \`${pkgRoot}\`.`, export function ENONPMTOKEN({ registry }) { return { message: "No npm token specified.", - details: `When not publishing through [trusted publishing](https://docs.npmjs.com/trusted-publishers) an [npm token](${linkify( + details: `When not publishing through [trusted publishing](https://docs.npmjs.com/trusted-publishers), an [npm token](${linkify( "README.md#npm-registry-authentication" )}) must be created and set in the \`NPM_TOKEN\` environment variable on your CI environment. -Please make sure to create an [npm token](https://docs.npmjs.com/getting-started/working_with_tokens#how-to-create-new-tokens) and to set it in the \`NPM_TOKEN\` environment variable on your CI environment. The token must allow to publish to the registry \`${registry}\`.`, +Please make sure to create an [npm token](https://docs.npmjs.com/getting-started/working_with_tokens#how-to-create-new-tokens) and set it in the \`NPM_TOKEN\` environment variable on your CI environment. The token must allow publishing to the registry \`${registry}\`.`, }; } -export function EINVALIDNPMAUTH() { +export function EINVALIDNPMAUTH({ registry }) { return { message: "Invalid npm authentication.", details: `The [authentication required to publish](${linkify( "README.md#npm-registry-authentication" - )}) is not configured as needed.`, + )}) configured in the \`NPM_TOKEN\` environment variable must be a valid [token](https://docs.npmjs.com/getting-started/working_with_tokens) allowed to publish to the registry \`${registry}\`. + +Please make sure to set the \`NPM_TOKEN\` environment variable in your CI with the exact value of the npm token.`, }; }