diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index dc332d67..64af26e3 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -10,7 +10,7 @@ jobs: commitlint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 100 - uses: wagoid/commitlint-github-action@v6 diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 750760c6..cf85f643 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -11,9 +11,9 @@ jobs: name: Lint using ESLint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Use latest Node.js LTS - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: lts/* - name: Install dependencies diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index ec4fdf48..3d3ce2e4 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -25,8 +25,8 @@ jobs: if: ${{ needs.release-please.outputs.release_created }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 with: node-version: 24.x registry-url: 'https://registry.npmjs.org' diff --git a/CHANGELOG.md b/CHANGELOG.md index e36be5b5..5c519ccb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## [6.0.0](https://github.com/nodejs/node-core-utils/compare/v5.16.2...v6.0.0) (2025-12-21) + + +### ⚠ BREAKING CHANGES + +* drop Node.js 18.x from supported engines ([#988](https://github.com/nodejs/node-core-utils/issues/988)) + +### Features + +* **git-node:** enhance remote configuration error message ([#965](https://github.com/nodejs/node-core-utils/issues/965)) ([ea17768](https://github.com/nodejs/node-core-utils/commit/ea17768bc3d3aec853e698beaecc8d97da298d65)) +* **git-node:** verify tag with official keyring ([#989](https://github.com/nodejs/node-core-utils/issues/989)) ([48a418b](https://github.com/nodejs/node-core-utils/commit/48a418b1eee227096be438c38759c96b843b8bb8)) +* **ncu-config:** add support for partially encrypted config files ([#1004](https://github.com/nodejs/node-core-utils/issues/1004)) ([7342aff](https://github.com/nodejs/node-core-utils/commit/7342affa6402a5f10130c0f486b5e1b08a4b3919)) +* **v8:** add command to sync V8 deps ([#1016](https://github.com/nodejs/node-core-utils/issues/1016)) ([aadc4fc](https://github.com/nodejs/node-core-utils/commit/aadc4fcb9c8d889424ddf51bd38a0dca1a8ec375)) + + +### Bug Fixes + +* **git-node:** clarify remote configuration error message ([#1001](https://github.com/nodejs/node-core-utils/issues/1001)) ([8746db3](https://github.com/nodejs/node-core-utils/commit/8746db3aca61587a41de221f6bd6ae554c71f1fe)) +* **git-node:** harmonize time left error messages ([#1007](https://github.com/nodejs/node-core-utils/issues/1007)) ([d5a488f](https://github.com/nodejs/node-core-utils/commit/d5a488f78bc5e143813251ae132e2c71aa79384f)) +* **git-node:** include full URL in suggested `gh` command ([#1002](https://github.com/nodejs/node-core-utils/issues/1002)) ([caee3c5](https://github.com/nodejs/node-core-utils/commit/caee3c5f9dda188d62f24d0533882ce52c60dd16)) +* **ncu-config:** do not override encrypted values silently ([#1006](https://github.com/nodejs/node-core-utils/issues/1006)) ([5d72b3e](https://github.com/nodejs/node-core-utils/commit/5d72b3e51b281e0300039e0824efe8251efce6f6)) +* remove stack traces from non-debug output ([#990](https://github.com/nodejs/node-core-utils/issues/990)) ([687217f](https://github.com/nodejs/node-core-utils/commit/687217f7867add49f8be8461cf0a85fc9364dda0)) +* spawning of `gpg` to read config ([#991](https://github.com/nodejs/node-core-utils/issues/991)) ([100c694](https://github.com/nodejs/node-core-utils/commit/100c6946b167ab7b035db929f779274b79e07694)) + + +### Miscellaneous Chores + +* drop Node.js 18.x from supported engines ([#988](https://github.com/nodejs/node-core-utils/issues/988)) ([f8371dd](https://github.com/nodejs/node-core-utils/commit/f8371dde2d8c78b50c272c8f41dedbca7b54f57c)) + ## [5.16.2](https://github.com/nodejs/node-core-utils/compare/v5.16.1...v5.16.2) (2025-10-12) diff --git a/README.md b/README.md index 9bcf06de..007f400e 100644 --- a/README.md +++ b/README.md @@ -76,18 +76,29 @@ Optionally, if you want to grant write access so `git-node` can write comments: You can also edit the permission of existing tokens later. -After the token is generated, create an rc file with the following content: -(`~/.ncurc` or `$XDG_CONFIG_HOME/ncurc`): - -```json -{ - "username": "your_github_username", - "token": "token_that_you_created" -} +After the token is generated, you can give it to NCU using: + +
With encryption (Recommended) + +```sh +ncu-config set username your_github_username +# Do not provide the token in the CLI, `ncu-config` will prompt you for it. +ncu-config set -x token +``` + +Note: Encryption is available only if you have `gpg` setup on your machine. + +
+ +
Without encryption + +```sh +ncu-config set username your_github_username +# Do not provide the token in the CLI, `ncu-config` will prompt you for it. +ncu-config set token ``` -Note: you could use `ncu-config` to configure these variables, but it's not -recommended to leave your tokens in your command line history. +
### Setting up Jenkins credentials @@ -108,27 +119,24 @@ To obtain the Jenkins API token `~/.ncurc.gpg` or `$XDG_CONFIG_HOME/ncurc.gpg`) with `jenkins_token` as key, like this: - ```json - { - "username": "your_github_username", - "token": "your_github_token", - "jenkins_token": "your_jenkins_token" - } +
With encryption (recommended) + + ```sh + ncu-config set -x jenkins_token ``` -### Protecting your credentials + Note: Encryption is available only if you have `gpg` setup on your machine. -If you have `gpg` installed and setup on your local machine, it is strongly recommended -to store an encrypted version of this file: +
+
Without encryption + + ```sh + ncu-config set jenkins_token + ``` -```console -$ gpg --default-recipient-self --encrypt ~/.ncurc -$ rm ~/.ncurc -``` +
-The credentials are now encrypted in `~/.ncurc.gpg` and everytime it's needed, -node-core-utils will invoke `gpg` that may ask you to decrypt it using -your default key via pinentry. +### Protecting your credentials Put the following entries into your [global `gitignore` file](https://git-scm.com/docs/git-config#Documentation/git-config.txt-coreexcludesFile) diff --git a/bin/ncu-config.js b/bin/ncu-config.js index ffe50209..67e5545b 100755 --- a/bin/ncu-config.js +++ b/bin/ncu-config.js @@ -1,10 +1,14 @@ #!/usr/bin/env node +import * as readline from 'node:readline/promises'; +import { stdin as input, stdout as output } from 'node:process'; + import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { - getConfig, updateConfig, GLOBAL_CONFIG, PROJECT_CONFIG, LOCAL_CONFIG + getConfig, updateConfig, GLOBAL_CONFIG, PROJECT_CONFIG, LOCAL_CONFIG, + encryptValue } from '../lib/config.js'; import { setVerbosityFromEnv } from '../lib/verbosity.js'; @@ -13,10 +17,15 @@ setVerbosityFromEnv(); const args = yargs(hideBin(process.argv)) .completion('completion') .command({ - command: 'set ', + command: 'set []', desc: 'Set a config variable', builder: (yargs) => { yargs + .option('encrypt', { + describe: 'Store the value encrypted using gpg', + alias: 'x', + type: 'boolean' + }) .positional('key', { describe: 'key of the configuration', type: 'string' @@ -61,8 +70,6 @@ const args = yargs(hideBin(process.argv)) .conflicts('global', 'project') .help(); -const argv = args.parse(); - function getConfigType(argv) { if (argv.global) { return { configName: 'global', configType: GLOBAL_CONFIG }; @@ -73,9 +80,19 @@ function getConfigType(argv) { return { configName: 'local', configType: LOCAL_CONFIG }; } -function setHandler(argv) { +async function setHandler(argv) { const { configName, configType } = getConfigType(argv); const config = getConfig(configType); + if (!argv.value) { + const rl = readline.createInterface({ input, output }); + argv.value = await rl.question('What value do you want to set? '); + rl.close(); + } else if (argv.encrypt) { + console.warn('Passing sensitive config value via the shell is discouraged'); + } + if (argv.encrypt) { + argv.value = await encryptValue(argv.value); + } console.log( `Updating ${configName} configuration ` + `[${argv.key}]: ${config[argv.key]} -> ${argv.value}`); @@ -96,6 +113,8 @@ function listHandler(argv) { } } +const argv = await args.parse(); + if (!['get', 'set', 'list'].includes(argv._[0])) { args.showHelp(); } diff --git a/components/git/v8.js b/components/git/v8.js index 716718ab..094fc08a 100644 --- a/components/git/v8.js +++ b/components/git/v8.js @@ -2,12 +2,12 @@ import path from 'node:path'; import logSymbols from 'log-symbols'; -import { minor, major, backport } from '../../lib/update-v8/index.js'; +import { minor, major, backport, deps } from '../../lib/update-v8/index.js'; import { defaultBaseDir } from '../../lib/update-v8/constants.js'; import { checkCwd } from '../../lib/update-v8/common.js'; import { forceRunAsync } from '../../lib/run.js'; -export const command = 'v8 [major|minor|backport]'; +export const command = 'v8 [major|minor|backport|deps]'; export const describe = 'Update or patch the V8 engine'; export function builder(yargs) { @@ -26,6 +26,11 @@ export function builder(yargs) { describe: 'Bump the NODE_MODULE_VERSION constant', default: true }); + yargs.option('concurrent', { + type: 'boolean', + describe: 'Update dependencies concurrently', + default: true, + }); } }) .command({ @@ -64,6 +69,19 @@ export function builder(yargs) { }); } }) + .command({ + command: 'deps', + desc: 'Update V8 dependencies from the DEPS file', + handler, + builder: (yargs) => { + yargs + .option('concurrent', { + type: 'boolean', + describe: 'Update dependencies concurrently', + default: true, + }); + } + }) .demandCommand(1, 'Please provide a valid command') .option('node-dir', { describe: 'Directory of a Node.js clone', @@ -126,6 +144,8 @@ export function handler(argv) { return major(options); case 'backport': return backport(options); + case 'deps': + return deps(options); } }) .catch((err) => { diff --git a/eslint.config.js b/eslint.config.js index 10e67b91..890d83b4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -18,11 +18,12 @@ export default [ 'coverage/', 'node_modules/', 'lib/wpt/templates/', + 'test/fixtures/release/*.js', // Copied from the nodejs/node repo ], }, { languageOptions: { - globals: globals.node, + globals: globals.nodeBuiltin, sourceType: 'module', ecmaVersion: 'latest', }, @@ -42,5 +43,22 @@ export default [ 'n/no-process-exit': 'off', 'n/no-unsupported-features/node-builtins': 'off', }, + settings: { + 'import/resolver': { + node: { + pathFilter(pkg, path, relativePath) { + const pkgExport = relativePath + ? pkg.exports?.[`./${relativePath}`] + : pkg.exports?.['.']; + return pkgExport?.import?.default ?? + pkgExport?.import ?? + pkgExport?.[0]?.import ?? + pkgExport?.default ?? + pkgExport ?? + (relativePath || pkg.main); + }, + }, + }, + }, }, ]; diff --git a/lib/auth.js b/lib/auth.js index a0cf5b4a..a55f667a 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -3,7 +3,7 @@ import { ClientRequest } from 'node:http'; import ghauth from 'ghauth'; -import { getMergedConfig, getNcurcPath } from './config.js'; +import { clearCachedConfig, encryptValue, getMergedConfig, getNcurcPath } from './config.js'; export default lazy(auth); @@ -60,67 +60,90 @@ function encode(name, token) { return Buffer.from(`${name}:${token}`).toString('base64'); } +function setOwnProperty(target, key, value) { + return Object.defineProperty(target, key, { + __proto__: null, + configurable: true, + enumerable: true, + value + }); +} + // TODO: support jenkins only...or not necessary? // TODO: make this a class with dependency (CLI) injectable for testing async function auth( options = { github: true }, githubAuth = ghauth) { - const result = {}; + const result = { + get github() { + let username; + let token; + try { + ({ username, token } = getMergedConfig()); + } catch (e) { + // Ignore error and prompt + } + + check(username, token); + const github = encode(username, token); + setOwnProperty(result, 'github', github); + return github; + }, + + get jenkins() { + const { username, jenkins_token } = getMergedConfig(); + if (!username || !jenkins_token) { + errorExit( + 'Get your Jenkins API token in https://ci.nodejs.org/me/security ' + + 'and run the following command to add it to your ncu config: ' + + 'ncu-config --global set -x jenkins_token' + ); + }; + check(username, jenkins_token); + const jenkins = encode(username, jenkins_token); + setOwnProperty(result, 'jenkins', jenkins); + return jenkins; + }, + + get h1() { + const { h1_username, h1_token } = getMergedConfig(); + check(h1_username, h1_token); + const h1 = encode(h1_username, h1_token); + setOwnProperty(result, 'h1', h1); + return h1; + } + }; if (options.github) { - let username; - let token; + let config; try { - ({ username, token } = getMergedConfig()); - } catch (e) { - // Ignore error and prompt + config = getMergedConfig(); + } catch { + config = {}; } - - if (!username || !token) { + if (!Object.hasOwn(config, 'token') || !Object.hasOwn(config, 'username')) { process.stdout.write( 'If this is your first time running this command, ' + 'follow the instructions to create an access token' + '. If you prefer to create it yourself on Github, ' + 'see https://github.com/nodejs/node-core-utils/blob/main/README.md.\n'); const credentials = await tryCreateGitHubToken(githubAuth); - username = credentials.user; - token = credentials.token; + const username = credentials.user; + let token; + try { + token = await encryptValue(credentials.token); + } catch (err) { + console.warn('Failed encrypt token, storing unencrypted instead'); + token = credentials.token; + } const json = JSON.stringify({ username, token }, null, 2); fs.writeFileSync(getNcurcPath(), json, { mode: 0o600 /* owner read/write */ }); // Try again reading the file - ({ username, token } = getMergedConfig()); + clearCachedConfig(); } - check(username, token); - result.github = encode(username, token); } - if (options.jenkins) { - const { username, jenkins_token } = getMergedConfig(); - if (!username || !jenkins_token) { - errorExit( - 'Get your Jenkins API token in https://ci.nodejs.org/me/configure ' + - 'and run the following command to add it to your ncu config: ' + - 'ncu-config --global set jenkins_token TOKEN' - ); - }; - check(username, jenkins_token); - result.jenkins = encode(username, jenkins_token); - } - - if (options.h1) { - const { h1_username, h1_token } = getMergedConfig(); - if (!h1_username || !h1_token) { - errorExit( - 'Get your HackerOne API token in ' + - 'https://docs.hackerone.com/organizations/api-tokens.html ' + - 'and run the following command to add it to your ncu config: ' + - 'ncu-config --global set h1_token TOKEN or ' + - 'ncu-config --global set h1_username USERNAME' - ); - }; - result.h1 = encode(h1_username, h1_token); - } return result; } diff --git a/lib/config.js b/lib/config.js index 6703ec87..3c91217d 100644 --- a/lib/config.js +++ b/lib/config.js @@ -4,6 +4,7 @@ import os from 'node:os'; import { readJson, writeJson } from './file.js'; import { existsSync, mkdtempSync, rmSync } from 'node:fs'; import { spawnSync } from 'node:child_process'; +import { forceRunAsync, runSync } from './run.js'; export const GLOBAL_CONFIG = Symbol('globalConfig'); export const PROJECT_CONFIG = Symbol('projectConfig'); @@ -18,32 +19,95 @@ export function getNcurcPath() { } } -export function getMergedConfig(dir, home) { - const globalConfig = getConfig(GLOBAL_CONFIG, home); - const projectConfig = getConfig(PROJECT_CONFIG, dir); - const localConfig = getConfig(LOCAL_CONFIG, dir); - return Object.assign(globalConfig, projectConfig, localConfig); +let mergedConfig; +export function getMergedConfig(dir, home, additional) { + if (mergedConfig == null) { + const globalConfig = getConfig(GLOBAL_CONFIG, home); + const projectConfig = getConfig(PROJECT_CONFIG, dir); + const localConfig = getConfig(LOCAL_CONFIG, dir); + mergedConfig = Object.assign(globalConfig, projectConfig, localConfig, additional); + } + return mergedConfig; }; +export function clearCachedConfig() { + mergedConfig = null; +} + +export async function encryptValue(input) { + console.warn('Spawning gpg to encrypt the config value'); + return forceRunAsync( + process.env.GPG_BIN || 'gpg', + ['--default-recipient-self', '--encrypt', '--armor'], + { + captureStdout: true, + ignoreFailure: false, + input + } + ); +} + +function setOwnProperty(target, key, value) { + return Object.defineProperty(target, key, { + __proto__: null, + configurable: true, + enumerable: true, + value + }); +} +function addEncryptedPropertyGetter(target, key, input) { + if (input?.startsWith?.('-----BEGIN PGP MESSAGE-----\n')) { + return Object.defineProperty(target, key, { + __proto__: null, + configurable: true, + get() { + // Using an error object to get a stack trace in debug mode. + const warn = new Error( + `The config value for ${key} is encrypted, spawning gpg to decrypt it...` + ); + console.warn(setOwnProperty(warn, 'name', 'Warning')); + const value = runSync(process.env.GPG_BIN || 'gpg', ['--decrypt'], { input }); + setOwnProperty(target, key, value); + return value; + }, + set(newValue) { + if (!addEncryptedPropertyGetter(target, key, newValue)) { + throw new Error( + 'Refusing to override an encrypted value with a non-encrypted one. ' + + 'Please use an encrypted one, or delete the config key first.' + ); + } + } + }); + } +} -export function getConfig(configType, dir) { +export function getConfig(configType, dir, raw = false) { const configPath = getConfigPath(configType, dir); const encryptedConfigPath = configPath + '.gpg'; if (existsSync(encryptedConfigPath)) { console.warn('Encrypted config detected, spawning gpg to decrypt it...'); const { status, stdout } = - spawnSync('gpg', ['--decrypt', encryptedConfigPath]); + spawnSync(process.env.GPG_BIN || 'gpg', ['--decrypt', encryptedConfigPath]); if (status === 0) { return JSON.parse(stdout.toString('utf-8')); } } try { - return readJson(configPath); + const json = readJson(configPath); + if (!raw) { + // Raw config means encrypted values are returned as is. + // Otherwise we install getters to decrypt them when accessed. + for (const [key, val] of Object.entries(json)) { + addEncryptedPropertyGetter(json, key, val); + } + } + return json; } catch (cause) { throw new Error('Unable to parse config file ' + configPath, { cause }); } }; -export function getConfigPath(configType, dir) { +function getConfigPath(configType, dir) { switch (configType) { case GLOBAL_CONFIG: return getNcurcPath(); @@ -61,7 +125,7 @@ export function getConfigPath(configType, dir) { } }; -export function writeConfig(configType, obj, dir) { +function writeConfig(configType, obj, dir) { const configPath = getConfigPath(configType, dir); const encryptedConfigPath = configPath + '.gpg'; if (existsSync(encryptedConfigPath)) { @@ -69,7 +133,7 @@ export function writeConfig(configType, obj, dir) { const tmpFile = path.join(tmpDir, 'config.json'); try { writeJson(tmpFile, obj); - const { status } = spawnSync('gpg', + const { status } = spawnSync(process.env.GPG_BIN || 'gpg', ['--default-recipient-self', '--yes', '--encrypt', '--output', encryptedConfigPath, tmpFile] ); if (status !== 0) { @@ -85,7 +149,7 @@ export function writeConfig(configType, obj, dir) { }; export function updateConfig(configType, obj, dir) { - const config = getConfig(configType, dir); + const config = getConfig(configType, dir, true); writeConfig(configType, Object.assign(config, obj), dir); }; diff --git a/lib/landing_session.js b/lib/landing_session.js index 139eabc3..19944ba4 100644 --- a/lib/landing_session.js +++ b/lib/landing_session.js @@ -464,8 +464,8 @@ export default class LandingSession extends Session { const url = `https://github.com/${owner}/${repo}/pull/${prid}`; cli.log(`2. Post "Landed in ${willBeLanded}" in ${url}`); if (isGhAvailable()) { - cli.log(` gh pr comment ${prid} --body "Landed in ${willBeLanded}"`); - cli.log(` gh pr close ${prid}`); + cli.log(` gh pr comment ${url} --body "Landed in ${willBeLanded}"`); + cli.log(` gh pr close ${url}`); } } diff --git a/lib/pr_checker.js b/lib/pr_checker.js index 273d0fe7..a4dfeaac 100644 --- a/lib/pr_checker.js +++ b/lib/pr_checker.js @@ -19,7 +19,6 @@ const { FROM_COMMENT, FROM_REVIEW_COMMENT } = REVIEW_SOURCES; const SECOND = 1000; const MINUTE = SECOND * 60; -const HOUR = MINUTE * 60; const WAIT_TIME_MULTI_APPROVAL = 24 * 2; const WAIT_TIME_SINGLE_APPROVAL = 24 * 7; @@ -196,10 +195,18 @@ export default class PRChecker { const createTime = new Date(this.pr.createdAt); const msFromCreateTime = now.getTime() - createTime.getTime(); - const minutesFromCreateTime = Math.ceil(msFromCreateTime / MINUTE); - const hoursFromCreateTime = Math.ceil(msFromCreateTime / HOUR); - let timeLeftMulti = this.waitTimeMultiApproval - hoursFromCreateTime; - const timeLeftSingle = this.waitTimeSingleApproval - hoursFromCreateTime; + const minutesFromCreateTime = msFromCreateTime / MINUTE; + const timeLeftMulti = this.waitTimeMultiApproval * 60 - minutesFromCreateTime; + const timeLeftSingle = this.waitTimeSingleApproval * 60 - minutesFromCreateTime; + const timeToText = (time, liaison_word = undefined) => { + let unity = 'minute'; + if (time > 59) { + unity = 'hour'; + time /= 60; + } + time = Math.ceil(time); + return `${time} ${liaison_word ? liaison_word + ' ' : ''}${unity}${time === 1 ? '' : 's'}`; + }; if (approved.length >= 2) { if (isFastTracked || isCodeAndLearn) { @@ -208,15 +215,9 @@ export default class PRChecker { if (timeLeftMulti < 0) { return true; } - if (timeLeftMulti === 0) { - const timeLeftMins = - this.waitTimeMultiApproval * 60 - minutesFromCreateTime; - cli.error(`This PR needs to wait ${timeLeftMins} ` + - `more minutes to land${fastTrackAppendix}`); - return false; - } - cli.error(`This PR needs to wait ${timeLeftMulti} more ` + - `hours to land${fastTrackAppendix}`); + cli.error( + `This PR needs to wait ${timeToText(timeLeftMulti, 'more')} to land${fastTrackAppendix}` + ); return false; } @@ -224,10 +225,9 @@ export default class PRChecker { if (timeLeftSingle < 0) { return true; } - timeLeftMulti = timeLeftMulti < 0 || isFastTracked ? 0 : timeLeftMulti; - cli.error(`This PR needs to wait ${timeLeftSingle} more hours to land ` + - `(or ${timeLeftMulti} hours if there is one more approval)` + - fastTrackAppendix); + cli.error(`This PR needs to wait ${timeToText(timeLeftSingle, 'more')} to land (or ${ + timeToText(timeLeftMulti < 0 || isFastTracked ? 0 : timeLeftMulti) + } if there is one more approval)${fastTrackAppendix}`); return false; } } diff --git a/lib/promote_release.js b/lib/promote_release.js index 89abc497..bdddacbb 100644 --- a/lib/promote_release.js +++ b/lib/promote_release.js @@ -1,5 +1,7 @@ import path from 'node:path'; import fs from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { pipeline } from 'node:stream/promises'; import semver from 'semver'; import * as gst from 'git-secure-tag'; @@ -196,31 +198,44 @@ export default class ReleasePromotion extends Session { async verifyTagSignature(version) { const { cli } = this; - const verifyTagPattern = /gpg:[^\n]+\ngpg:\s+using \w+ key ([^\n]+)\ngpg:\s+issuer "([^"]+)"\ngpg:\s+Good signature from (?:"[^"]+"(?: \[ultimate\])?\ngpg:\s+aka )*"([^<]+) <\2>"/; - const [verifyTagOutput, haystack] = await Promise.all([forceRunAsync( - 'git', ['--no-pager', - 'verify-tag', - `v${version}` - ], { ignoreFailure: false, captureStderr: true }), fs.readFile('README.md')]); - const match = verifyTagPattern.exec(verifyTagOutput); - if (match == null) { - cli.warn('git was not able to verify the tag:'); - cli.info(verifyTagOutput); - } else { - const [, keyID, email, name] = match; - const needle = `* **${name}** <<${email}>>\n ${'`'}${keyID}${'`'}`; - if (haystack.includes(needle)) { - return; + + cli.startSpinner('Downloading active releasers keyring from nodejs/release-keys...'); + const [keyRingStream, [GNUPGHOME, keyRingFd]] = await Promise.all([ + fetch('https://github.com/nodejs/release-keys/raw/HEAD/gpg-only-active-keys/pubring.kbx'), + fs.mkdtemp(path.join(tmpdir(), 'ncu-')) + .then(async d => [d, await fs.open(path.join(d, 'pubring.kbx'), 'w')]), + ]); + if (!keyRingStream.ok) throw new Error('Failed to download keyring', { cause: keyRingStream }); + await pipeline(keyRingStream.body, keyRingFd.createWriteStream()); + cli.stopSpinner('Active releasers keyring stored in temp directory'); + + try { + await forceRunAsync( + 'git', ['--no-pager', + 'verify-tag', + `v${version}` + ], { + ignoreFailure: false, + spawnArgs: { env: { ...process.env, GNUPGHOME } }, + }); + cli.ok('git tag signature verified'); + } catch (cause) { + cli.error('git was not able to verify the tag'); + cli.warn('This means that either the tag was signed with the wrong key,'); + cli.warn('or that nodejs/release-keys contains outdated information.'); + cli.warn('The release should not proceed.'); + if (!await cli.prompt('Do you want to proceed anyway?', { defaultAnswer: false })) { + if (await cli.prompt('Do you want to delete the local tag?')) { + await forceRunAsync('git', ['tag', '-d', `v${version}`]); + } else { + cli.info(`Run 'git tag -d v${version}' to remove the local tag.`); + } + throw new Error('Aborted', { cause }); } - cli.warn('Tag was signed with an undocumented identity/key pair!'); - cli.info('Expected to find the following entry in the README:'); - cli.info(needle); - cli.info('If you are using a subkey, it might be OK.'); - } - cli.info(`If that doesn't sound right, consider removing the tag (git tag -d v${version - }), check your local config, and start the process over.`); - if (!await cli.prompt('Do you want to proceed anyway?', { defaultAnswer: false })) { - throw new Error('Aborted'); + } finally { + cli.startSpinner('Cleaning up temp files'); + await fs.rm(GNUPGHOME, { force: true, recursive: true }); + cli.stopSpinner('Temp files removed'); } } diff --git a/lib/session.js b/lib/session.js index 4ce08a93..4c2dacf3 100644 --- a/lib/session.js +++ b/lib/session.js @@ -19,7 +19,7 @@ export default class Session { this.cli = cli; this.dir = dir; this.prid = prid; - this.config = { ...getMergedConfig(this.dir), ...argv }; + this.config = getMergedConfig(this.dir, undefined, argv); this.gpgSign = argv?.['gpg-sign'] ? (argv['gpg-sign'] === true ? ['-S'] : ['-S', argv['gpg-sign']]) : []; @@ -126,7 +126,12 @@ export default class Session { writeJson(this.sessionPath, { state: STARTED, prid: this.prid, - config: this.config + // Filter out getters, those are likely encrypted data we don't need on the session. + config: Object.entries(Object.getOwnPropertyDescriptors(this.config)) + .reduce((pv, [key, desc]) => { + if (Object.hasOwn(desc, 'value')) pv[key] = desc.value; + return pv; + }, { __proto__: null }), }); } @@ -382,12 +387,19 @@ export default class Session { cli.setExitCode(1); } if (!upstream) { - cli.warn('You have not told git-node the remote you want to sync with.'); + cli.warn('You have not told git-node what remote name you are trying to land commits on.'); cli.separator(); - cli.info( - 'For example, if your remote pointing to nodejs/node is' + - ' `remote-upstream`, you can run:\n\n' + - ' $ ncu-config set upstream remote-upstream'); + const remoteName = runSync('git', ['remote', '-v']) + .match(/^(\S+)\s+(?:https:\/\/github\.com\/|git@github\.com:)nodejs\/node\.git \(\S+\)\r?$/m)?.[1]; + cli.info(remoteName + ? `You likely want to run the following:\n\n $ ncu-config set upstream ${remoteName}` + : 'The expected repository does not seem to appear in your local config.\n' + + '1. First, add the Node.js core repository as a remote:\n' + + ' $ git remote add upstream https://github.com/nodejs/node.git\n\n' + + '2. Then, tell git-node to use this remote for syncing:\n' + + ' $ ncu-config set upstream upstream\n\n' + + 'Note: Using "upstream" is recommended, but you can use any remote name.\n' + + 'For security reasons, you need to add the remote manually.'); cli.separator(); cli.setExitCode(1); } diff --git a/lib/update-v8/deps.js b/lib/update-v8/deps.js new file mode 100644 index 00000000..f4043384 --- /dev/null +++ b/lib/update-v8/deps.js @@ -0,0 +1,88 @@ +import path from 'node:path'; +import { promises as fs } from 'node:fs'; + +import { chromiumGit, v8Deps } from './constants.js'; +import { forceRunAsync } from '../run.js'; +import { + addToGitignore, + filterForVersion, + getNodeV8Version, + removeDirectory, + replaceGitignore, +} from './util.js'; + +async function fetchFromGit(cwd, repo, commit) { + await removeDirectory(cwd); + await fs.mkdir(cwd, { recursive: true }); + await exec('init'); + await exec('remote', 'add', 'origin', repo); + await exec('fetch', 'origin', commit); + await exec('reset', '--hard', 'FETCH_HEAD'); + await removeDirectory(path.join(cwd, '.git')); + + function exec(...options) { + return forceRunAsync('git', options, { + ignoreFailure: false, + spawnArgs: { cwd, stdio: 'ignore' } + }); + } +} + +async function readDeps(nodeDir) { + const depsStr = await fs.readFile(path.join(nodeDir, 'deps/v8/DEPS'), 'utf8'); + const start = depsStr.indexOf('deps = {'); + const end = depsStr.indexOf('\n}', start) + 2; + const depsDeclaration = depsStr.substring(start, end).replace(/^ *#.*/gm, ''); + const Var = () => chromiumGit; // eslint-disable-line no-unused-vars + let deps; + eval(depsDeclaration); // eslint-disable-line no-eval + return deps; +} + +async function lookupDep(depsTable, depName) { + const dep = depsTable[depName]; + if (!dep) { + throw new Error(`V8 dep "${depName}" not found in DEPS file`); + } + if (typeof dep === 'object') { + return dep.url.split('@'); + } + return dep.split('@'); +} + +export default function updateV8Deps() { + return { + title: 'Update V8 DEPS', + task: async(ctx, task) => { + const newV8Version = await getNodeV8Version(ctx.nodeDir); + const repoPrefix = newV8Version.majorMinor >= 86 ? '' : 'v8/'; + const deps = filterForVersion(v8Deps.map((v8Dep) => ({ + ...v8Dep, + repo: `${repoPrefix}${v8Dep.repo}`, + path: v8Dep.repo + })), newV8Version); + if (deps.length === 0) return; + const depsTable = await readDeps(ctx.nodeDir); + const subtasks = []; + for (const dep of deps) { + // Update .gitignore sequentially to avoid races + if (dep.gitignore) { + if (typeof dep.gitignore === 'string') { + await addToGitignore(ctx.nodeDir, dep.gitignore); + } else { + await replaceGitignore(ctx.nodeDir, dep.gitignore); + } + } + subtasks.push({ + title: `Update ${dep.path}`, + task: async(ctx) => { + const [repo, commit] = await lookupDep(depsTable, dep.repo); + const thePath = path.join(ctx.nodeDir, 'deps/v8', dep.path); + await fetchFromGit(thePath, repo, commit); + } + }); + } + return task.newListr(subtasks, { concurrent: ctx.concurrent }); + } + }; +}; diff --git a/lib/update-v8/index.js b/lib/update-v8/index.js index 1c9afca1..896a2228 100644 --- a/lib/update-v8/index.js +++ b/lib/update-v8/index.js @@ -5,6 +5,7 @@ import updateVersionNumbers from './updateVersionNumbers.js'; import commitUpdate from './commitUpdate.js'; import majorUpdate from './majorUpdate.js'; import minorUpdate from './minorUpdate.js'; +import updateDeps from './deps.js'; import updateV8Clone from './updateV8Clone.js'; export function major(options) { @@ -34,6 +35,14 @@ export async function backport(options) { return tasks.run(options); }; +export async function deps(options) { + const tasks = new Listr( + [updateDeps()], + getOptions(options) + ); + return tasks.run(options); +}; + /** * Get the listr2 options. * @param {{ verbose?: boolean }} options The original options. diff --git a/lib/update-v8/majorUpdate.js b/lib/update-v8/majorUpdate.js index 15fd4cd5..4005794d 100644 --- a/lib/update-v8/majorUpdate.js +++ b/lib/update-v8/majorUpdate.js @@ -1,17 +1,12 @@ import path from 'node:path'; -import { promises as fs } from 'node:fs'; import { getCurrentV8Version } from './common.js'; +import updateV8Deps from './deps.js'; import { - getNodeV8Version, - filterForVersion, - addToGitignore, - replaceGitignore, removeDirectory, isVersionString } from './util.js'; import applyNodeChanges from './applyNodeChanges.js'; -import { chromiumGit, v8Deps } from './constants.js'; import { forceRunAsync } from '../run.js'; export default function majorUpdate() { @@ -106,65 +101,3 @@ function addDepsV8() { }) }; } - -function updateV8Deps() { - return { - title: 'Update V8 DEPS', - task: async(ctx) => { - const newV8Version = await getNodeV8Version(ctx.nodeDir); - const repoPrefix = newV8Version.majorMinor >= 86 ? '' : 'v8/'; - const deps = filterForVersion(v8Deps.map((v8Dep) => ({ - ...v8Dep, - repo: `${repoPrefix}${v8Dep.repo}`, - path: v8Dep.repo - })), newV8Version); - if (deps.length === 0) return; - for (const dep of deps) { - if (dep.gitignore) { - if (typeof dep.gitignore === 'string') { - await addToGitignore(ctx.nodeDir, dep.gitignore); - } else { - await replaceGitignore(ctx.nodeDir, dep.gitignore); - } - } - const [repo, commit] = await readDeps(ctx.nodeDir, dep.repo); - const thePath = path.join(ctx.nodeDir, 'deps/v8', dep.path); - await fetchFromGit(thePath, repo, commit); - } - } - }; -} - -async function readDeps(nodeDir, depName) { - const depsStr = await fs.readFile(path.join(nodeDir, 'deps/v8/DEPS'), 'utf8'); - const start = depsStr.indexOf('deps = {'); - const end = depsStr.indexOf('\n}', start) + 2; - const depsDeclaration = depsStr.substring(start, end).replace(/^ *#.*/gm, ''); - const Var = () => chromiumGit; // eslint-disable-line no-unused-vars - let deps; - eval(depsDeclaration); // eslint-disable-line no-eval - const dep = deps[depName]; - if (!dep) { - throw new Error(`V8 dep "${depName}" not found in DEPS file`); - } - if (typeof dep === 'object') { - return dep.url.split('@'); - } - return dep.split('@'); -} - -async function fetchFromGit(cwd, repo, commit) { - await fs.mkdir(cwd, { recursive: true }); - await exec('init'); - await exec('remote', 'add', 'origin', repo); - await exec('fetch', 'origin', commit); - await exec('reset', '--hard', 'FETCH_HEAD'); - await removeDirectory(path.join(cwd, '.git')); - - function exec(...options) { - return forceRunAsync('git', options, { - ignoreFailure: false, - spawnArgs: { cwd, stdio: 'ignore' } - }); - } -} diff --git a/lib/update-v8/util.js b/lib/update-v8/util.js index b94a4356..a372a9fc 100644 --- a/lib/update-v8/util.js +++ b/lib/update-v8/util.js @@ -37,13 +37,18 @@ export function filterForVersion(list, version) { export async function addToGitignore(nodeDir, value) { const gitignorePath = path.join(nodeDir, 'deps/v8/.gitignore'); - await fs.appendFile(gitignorePath, `${value}\n`); + const gitignore = await fs.readFile(gitignorePath, 'utf8'); + if (!gitignore.includes(value)) { + await fs.appendFile(gitignorePath, `${value}\n`); + } } export async function replaceGitignore(nodeDir, options) { const gitignorePath = path.join(nodeDir, 'deps/v8/.gitignore'); let gitignore = await fs.readFile(gitignorePath, 'utf8'); - gitignore = gitignore.replace(options.match, options.replace); + if (!gitignore.includes(options.replace)) { + gitignore = gitignore.replace(options.match, options.replace); + } await fs.writeFile(gitignorePath, gitignore); } diff --git a/lib/verbosity.js b/lib/verbosity.js index d52b77c8..d14a7f3a 100644 --- a/lib/verbosity.js +++ b/lib/verbosity.js @@ -18,6 +18,9 @@ export function setVerbosityFromEnv() { if (Object.keys(VERBOSITY).includes(env)) { verbosity = VERBOSITY[env]; } + if (!isDebugVerbosity()) { + Error.stackTraceLimit = 0; + } }; export function debuglog(...args) { diff --git a/lib/voting_session.js b/lib/voting_session.js index 02b035a2..e3d349fe 100644 --- a/lib/voting_session.js +++ b/lib/voting_session.js @@ -10,7 +10,6 @@ import { getEditor, isGhAvailable } from './utils.js'; -// eslint-disable-next-line import/no-unresolved import voteUsingGit from '@node-core/caritat/voteUsingGit'; import * as yaml from 'js-yaml'; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e63edefa..f91d647a 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,18 +1,18 @@ { "name": "@node-core/utils", - "version": "5.16.2", + "version": "6.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@node-core/utils", - "version": "5.16.2", + "version": "6.0.0", "license": "MIT", "dependencies": { "@inquirer/prompts": "^7.4.1", "@listr2/prompt-adapter-enquirer": "^2.0.12", "@node-core/caritat": "^1.6.0", - "@pkgjs/nv": "^0.2.2", + "@pkgjs/nv": "^0.3.0", "branch-diff": "^3.1.1", "chalk": "^5.4.1", "changelog-maker": "^4.4.1", @@ -23,15 +23,15 @@ "ghauth": "^6.0.12", "git-secure-tag": "^2.3.1", "js-yaml": "^4.1.0", - "listr2": "^8.2.5", + "listr2": "^9.0.5", "lodash": "^4.17.21", "log-symbols": "^7.0.0", - "ora": "^8.2.0", + "ora": "^9.0.0", "replace-in-file": "^8.3.0", "semver": "^7.7.1", "undici": "^7.7.0", - "which": "^5.0.0", - "yargs": "^17.7.2" + "which": "^6.0.0", + "yargs": "^18.0.0" }, "bin": { "get-metadata": "bin/get-metadata.js", @@ -53,7 +53,7 @@ "sinon": "^21.0.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^20.19.0 || ^22.20.0 || >=24.10.0" } }, "node_modules/@actions/core": { @@ -176,9 +176,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", + "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, "license": "MIT", "dependencies": { @@ -218,13 +218,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -233,19 +233,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", - "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", - "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -293,9 +296,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", - "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "license": "MIT", "engines": { @@ -306,9 +309,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -316,13 +319,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", - "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.1", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -1045,12 +1048,12 @@ } }, "node_modules/@pkgjs/nv": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@pkgjs/nv/-/nv-0.2.2.tgz", - "integrity": "sha512-LbQbUKwt2d4VsTM5MKG25WLiZtmXu89zeoqVdJz3POlnyICkZ/o63WLswLleIysYOGHY9rqeOF/SBwC4hbq90Q==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@pkgjs/nv/-/nv-0.3.0.tgz", + "integrity": "sha512-axZda7mqX9UIos07lh8hyl+sx+/oSNAY4esWiaCHWWavV3IPiqMyX1g6OufByCKwtAEsWhDvgdWgoiycMQHd7w==", "license": "MIT", "dependencies": { - "got": "^11.8.3", + "got": "^11.8.6", "semver": "^7.1.1", "yargs": "^16.2.0" }, @@ -1189,9 +1192,9 @@ } }, "node_modules/@reporters/github": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@reporters/github/-/github-1.7.2.tgz", - "integrity": "sha512-8mvTyKUxxDXkNIWfzv3FsHVwjr8JCwVtwidQws2neV6YgrsJW6OwTOBBhd01RKrDMXPxgpMQuFEfN9hRuUZGuA==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@reporters/github/-/github-1.11.0.tgz", + "integrity": "sha512-sP/fSOgIoMYXZFWVy2Hw6vWUG3akUBiykqnFjx2jWI/kdqj55VZNXAQ27MYuiNffWlITW6mMBcv8+i47O7C77w==", "dev": true, "license": "MIT", "dependencies": { @@ -1417,6 +1420,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz", "integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.8.0" } @@ -1482,6 +1486,7 @@ "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.38.0", "@typescript-eslint/types": "8.38.0", @@ -1978,6 +1983,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2017,7 +2023,6 @@ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -2422,6 +2427,129 @@ } } }, + "node_modules/c8/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" + } + }, + "node_modules/c8/node_modules/ansi-styles": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/c8/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/c8/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/c8/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" + } + }, + "node_modules/c8/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", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/c8/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/c8/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/c8/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/cacheable-lookup": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", @@ -2544,9 +2672,9 @@ } }, "node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -2629,25 +2757,25 @@ "license": "MIT" }, "node_modules/cheerio": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.0.tgz", - "integrity": "sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz", + "integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==", "license": "MIT", "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", - "encoding-sniffer": "^0.2.0", + "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.0.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", - "undici": "^7.10.0", + "undici": "^7.12.0", "whatwg-mimetype": "^4.0.0" }, "engines": { - "node": ">=18.17" + "node": ">=20.18.1" }, "funding": { "url": "https://github.com/cheeriojs/cheerio?sponsor=1" @@ -2698,16 +2826,32 @@ } }, "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.1.1.tgz", + "integrity": "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==", "license": "MIT", "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" + "slice-ansi": "^7.1.0", + "string-width": "^8.0.0" }, "engines": { - "node": ">=18" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2740,96 +2884,31 @@ } }, "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "license": "ISC", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/cliui/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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/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==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=8" + "node": ">=20" } }, "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" @@ -3416,7 +3495,6 @@ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "license": "MIT", - "peer": true, "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -3430,7 +3508,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -3440,7 +3517,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3681,25 +3757,24 @@ } }, "node_modules/eslint": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz", - "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==", + "version": "9.39.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.0.tgz", + "integrity": "sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.0", - "@eslint/core": "^0.15.0", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.31.0", - "@eslint/plugin-kit": "^0.3.1", + "@eslint/js": "9.39.0", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -3929,6 +4004,7 @@ "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/types": "^8.35.0", "comment-parser": "^1.4.1", @@ -3997,9 +4073,9 @@ } }, "node_modules/eslint-plugin-n": { - "version": "17.21.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.21.0.tgz", - "integrity": "sha512-1+iZ8We4ZlwVMtb/DcHG3y5/bZOdazIpa/4TySo22MLKdwrLcfrX0hbadnCvykSQCCmkAnWmIP8jZVb2AAq29A==", + "version": "17.23.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-17.23.1.tgz", + "integrity": "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==", "dev": true, "license": "MIT", "dependencies": { @@ -4008,8 +4084,8 @@ "eslint-plugin-es-x": "^7.8.0", "get-tsconfig": "^4.8.1", "globals": "^15.11.0", + "globrex": "^0.1.2", "ignore": "^5.3.2", - "minimatch": "^9.0.5", "semver": "^7.6.3", "ts-declaration-location": "^1.0.6" }, @@ -4023,16 +4099,6 @@ "eslint": ">=8.23.0" } }, - "node_modules/eslint-plugin-n/node_modules/brace-expansion": { - "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" - } - }, "node_modules/eslint-plugin-n/node_modules/globals": { "version": "15.15.0", "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", @@ -4046,22 +4112,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-n/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/eslint-plugin-promise": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-7.2.1.tgz", @@ -4173,9 +4223,9 @@ } }, "node_modules/eslint/node_modules/@eslint/js": { - "version": "9.31.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz", - "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==", + "version": "9.39.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.0.tgz", + "integrity": "sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==", "dev": true, "license": "MIT", "engines": { @@ -4539,9 +4589,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", + "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", "license": "MIT", "engines": { "node": ">=18" @@ -5133,6 +5183,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true, + "license": "MIT" + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -5740,12 +5797,15 @@ } }, "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -6194,9 +6254,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -6330,12 +6390,12 @@ "license": "MIT" }, "node_modules/listr2": { - "version": "8.3.3", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", - "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.5.tgz", + "integrity": "sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==", "license": "MIT", "dependencies": { - "cli-truncate": "^4.0.0", + "cli-truncate": "^5.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", @@ -6343,7 +6403,7 @@ "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/listr2/node_modules/wrap-ansi": { @@ -6480,37 +6540,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, "node_modules/log-update/node_modules/wrap-ansi": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", @@ -7927,51 +7956,51 @@ } }, "node_modules/ora": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-9.0.0.tgz", + "integrity": "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==", "license": "MIT", "dependencies": { - "chalk": "^5.3.0", + "chalk": "^5.6.2", "cli-cursor": "^5.0.0", - "cli-spinners": "^2.9.2", + "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", - "is-unicode-supported": "^2.0.0", - "log-symbols": "^6.0.0", + "is-unicode-supported": "^2.1.0", + "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0" + "string-width": "^8.1.0", + "strip-ansi": "^7.1.2" }, "engines": { - "node": ">=18" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/log-symbols": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "node_modules/ora/node_modules/cli-spinners": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.3.0.tgz", + "integrity": "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==", "license": "MIT", - "dependencies": { - "chalk": "^5.3.0", - "is-unicode-supported": "^1.3.0" - }, "engines": { - "node": ">=18" + "node": ">=18.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "node_modules/ora/node_modules/string-width": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.1.0.tgz", + "integrity": "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg==", "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=12" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -9528,6 +9557,120 @@ "node": ">=18" } }, + "node_modules/replace-in-file/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/replace-in-file/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/replace-in-file/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/replace-in-file/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/replace-in-file/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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/replace-in-file/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==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/replace-in-file/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/replace-in-file/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/replace-in-file/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9948,16 +10091,16 @@ } }, "node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "license": "MIT", "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/slice-ansi?sponsor=1" @@ -10294,9 +10437,9 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -10498,6 +10641,7 @@ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -10734,7 +10878,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -10787,9 +10930,9 @@ } }, "node_modules/undici": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz", - "integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", + "integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -11079,9 +11222,9 @@ } }, "node_modules/which": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -11090,7 +11233,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/which-boxed-primitive": { @@ -11468,21 +11611,20 @@ } }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", "license": "MIT", "dependencies": { - "cliui": "^8.0.1", + "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", + "string-width": "^7.2.0", "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "yargs-parser": "^22.0.0" }, "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yargs-parser": { @@ -11494,54 +11636,13 @@ "node": ">=12" } }, - "node_modules/yargs/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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/yargs/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==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/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==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", "engines": { - "node": ">=8" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/yocto-queue": { diff --git a/package.json b/package.json index e93344cb..bee6b404 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "@node-core/utils", - "version": "5.16.2", + "version": "6.0.0", "description": "Utilities for Node.js core collaborators", "type": "module", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^20.19.0 || ^22.20.0 || >=24.10.0" }, "bin": { "get-metadata": "bin/get-metadata.js", @@ -38,7 +38,7 @@ "@inquirer/prompts": "^7.4.1", "@listr2/prompt-adapter-enquirer": "^2.0.12", "@node-core/caritat": "^1.6.0", - "@pkgjs/nv": "^0.2.2", + "@pkgjs/nv": "^0.3.0", "branch-diff": "^3.1.1", "chalk": "^5.4.1", "changelog-maker": "^4.4.1", @@ -49,15 +49,15 @@ "ghauth": "^6.0.12", "git-secure-tag": "^2.3.1", "js-yaml": "^4.1.0", - "listr2": "^8.2.5", + "listr2": "^9.0.5", "lodash": "^4.17.21", "log-symbols": "^7.0.0", - "ora": "^8.2.0", + "ora": "^9.0.0", "replace-in-file": "^8.3.0", "semver": "^7.7.1", "undici": "^7.7.0", - "which": "^5.0.0", - "yargs": "^17.7.2" + "which": "^6.0.0", + "yargs": "^18.0.0" }, "devDependencies": { "@eslint/js": "^9.24.0", diff --git a/test/fixtures/release/expected-test-process-release.js b/test/fixtures/release/expected-test-process-release.js index c031f1f2..ebb259f7 100644 --- a/test/fixtures/release/expected-test-process-release.js +++ b/test/fixtures/release/expected-test-process-release.js @@ -1,6 +1,5 @@ 'use strict'; -// eslint-disable-next-line n/no-missing-require require('../common'); const assert = require('assert'); diff --git a/test/fixtures/release/original-test-process-release.js b/test/fixtures/release/original-test-process-release.js index 72275a0b..13263894 100644 --- a/test/fixtures/release/original-test-process-release.js +++ b/test/fixtures/release/original-test-process-release.js @@ -1,6 +1,5 @@ 'use strict'; -// eslint-disable-next-line n/no-missing-require require('../common'); const assert = require('assert'); diff --git a/test/fixtures/run-auth-github.js b/test/fixtures/run-auth-github.js index d7151ea6..30f4c2c3 100644 --- a/test/fixtures/run-auth-github.js +++ b/test/fixtures/run-auth-github.js @@ -16,6 +16,11 @@ async function mockCredentials(options) { (async function() { const { default: auth } = await import('../../lib/auth.js'); const authParams = await auth({ github: true }, mockCredentials); + if (typeof authParams === 'object' && authParams != null) { + for (const key of Object.getOwnPropertyNames(authParams)) { + if (key !== 'github') delete authParams[key]; + } + } process.stdout.write(`${JSON.stringify(authParams)}\n`); })().catch(err => { console.error(err); diff --git a/test/unit/auth.test.js b/test/unit/auth.test.js index 3bc7d948..e753cb87 100644 --- a/test/unit/auth.test.js +++ b/test/unit/auth.test.js @@ -21,14 +21,16 @@ describe('auth', async function() { it('asks for auth data if no ncurc is found', async function() { await runAuthScript( undefined, - [FIRST_TIME_MSG, MOCKED_TOKEN] + [FIRST_TIME_MSG, MOCKED_TOKEN], + /^Spawning gpg to encrypt the config value\r?\nError: spawn do-not-exist ENOENT(?:.*\n)+Failed encrypt token, storing unencrypted instead\r?\n$/ ); }); it('asks for auth data if ncurc is invalid json', async function() { await runAuthScript( { HOME: 'this is not json' }, - [FIRST_TIME_MSG, MOCKED_TOKEN] + [FIRST_TIME_MSG, MOCKED_TOKEN], + /^Spawning gpg to encrypt the config value\r?\nError: spawn do-not-exist ENOENT(?:.*\n)+Failed encrypt token, storing unencrypted instead\r?\n$/ ); }); @@ -117,7 +119,7 @@ describe('auth', async function() { function runAuthScript( ncurc = {}, expect = [], error = '', fixture = 'run-auth-github') { return new Promise((resolve, reject) => { - const newEnv = { HOME: undefined, XDG_CONFIG_HOME: undefined }; + const newEnv = { HOME: undefined, XDG_CONFIG_HOME: undefined, GPG_BIN: 'do-not-exist' }; if (ncurc.HOME === undefined) ncurc.HOME = ''; // HOME must always be set. for (const envVar in ncurc) { if (ncurc[envVar] === undefined) continue; @@ -154,8 +156,9 @@ function runAuthScript( }); proc.on('close', () => { try { - assert.strictEqual(stderr, error); - assert.strictEqual(expect.length, 0); + if (typeof error === 'string') assert.strictEqual(stderr, error); + else assert.match(stderr, error); + assert.deepStrictEqual(expect, []); if (newEnv.HOME) { fs.rmSync(newEnv.HOME, { recursive: true, force: true }); } diff --git a/test/unit/ci_start.test.js b/test/unit/ci_start.test.js index 154356e4..cd8df818 100644 --- a/test/unit/ci_start.test.js +++ b/test/unit/ci_start.test.js @@ -1,8 +1,7 @@ -/* eslint-disable import/no-named-as-default-member */ import { describe, it, before, afterEach } from 'node:test'; import assert from 'assert'; -import sinon from 'sinon'; +import * as sinon from 'sinon'; import { FormData } from 'undici'; import { diff --git a/test/unit/pr_checker.test.js b/test/unit/pr_checker.test.js index bd98c6e4..4542e194 100644 --- a/test/unit/pr_checker.test.js +++ b/test/unit/pr_checker.test.js @@ -1,8 +1,7 @@ -/* eslint-disable import/no-named-as-default-member */ import { describe, it, before, after, afterEach } from 'node:test'; import assert from 'node:assert'; -import sinon from 'sinon'; +import * as sinon from 'sinon'; import PRData from '../../lib/pr_data.js'; import PRChecker from '../../lib/pr_checker.js'; @@ -51,6 +50,7 @@ const GT_7D = '2018-11-23T17:50:44.477Z'; const LT_7D_GT_48H = '2018-11-27T17:50:44.477Z'; const LT_48H = '2018-11-30T17:50:44.477Z'; const LT_48H_GT_47H = '2018-11-29T17:55:44.477Z'; +const LT_48H_GT_47_59 = '2018-11-29T17:51:43.477Z'; const NOW = '2018-11-31T17:50:44.477Z'; const argv = { maxCommits: 3 }; @@ -305,6 +305,43 @@ describe('PRChecker', () => { cli.assertCalledWith(expectedLogs); }); + it('should error when PR is younger than 48h and older than 47h58min', () => { + const cli = new TestCLI(); + + const expectedLogs = { + ok: + [['Approvals: 4'], + ['- Foo User (@foo): https://github.com/nodejs/node/pull/16438#pullrequestreview-71480624'], + ['- Quux User (@Quux): LGTM'], + ['- Baz User (@Baz): https://github.com/nodejs/node/pull/16438#pullrequestreview-71488236'], + ['- Bar User (@bar) (TSC): lgtm']], + info: [['This PR was created on Thu, 29 Nov 2018 17:51:43 GMT']], + error: [['This PR needs to wait 1 more minute to land']] + }; + + const youngPR = Object.assign({}, firstTimerPR, { + createdAt: LT_48H_GT_47_59 + }); + + const data = { + pr: youngPR, + reviewers: allGreenReviewers, + comments: commentsWithLGTM, + reviews: approvingReviews, + commits: simpleCommits, + collaborators, + authorIsNew: () => true, + getThread() { + return PRData.prototype.getThread.call(this); + } + }; + const checker = new PRChecker(cli, data, {}, argv); + + const status = checker.checkReviewsAndWait(new Date(NOW)); + assert(!status); + cli.assertCalledWith(expectedLogs); + }); + it('should error when PR has only 1 approval < 48h', () => { const cli = new TestCLI(); @@ -351,7 +388,7 @@ describe('PRChecker', () => { info: [['This PR was created on Tue, 27 Nov 2018 17:50:44 GMT']], error: [['This PR needs to wait 72 more hours to land ' + - '(or 0 hours if there is one more approval)']] + '(or 0 minutes if there is one more approval)']] }; const youngPR = Object.assign({}, firstTimerPR, { diff --git a/test/unit/pr_data.test.js b/test/unit/pr_data.test.js index 1b4870ff..86f7b31d 100644 --- a/test/unit/pr_data.test.js +++ b/test/unit/pr_data.test.js @@ -1,8 +1,7 @@ -/* eslint-disable import/no-named-as-default-member */ import { describe, it } from 'node:test'; import assert from 'node:assert'; -import sinon from 'sinon'; +import * as sinon from 'sinon'; import PRData from '../../lib/pr_data.js'; diff --git a/test/unit/team_info.test.js b/test/unit/team_info.test.js index a696204a..89e7e1f6 100644 --- a/test/unit/team_info.test.js +++ b/test/unit/team_info.test.js @@ -1,8 +1,7 @@ -/* eslint-disable import/no-named-as-default-member */ import { describe, it, before, after } from 'node:test'; import assert from 'node:assert'; -import sinon from 'sinon'; +import * as sinon from 'sinon'; import TestCLI from '../fixtures/test_cli.js'; import TeamInfo from '../../lib/team_info.js';